001    /*
002     * This file is part of McIDAS-V
003     *
004     * Copyright 2007-2013
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    package edu.wisc.ssec.mcidasv.data;
029    
030    import static javax.swing.GroupLayout.Alignment.BASELINE;
031    import static javax.swing.GroupLayout.Alignment.LEADING;
032    import static javax.swing.GroupLayout.Alignment.TRAILING;
033    import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
034    
035    import java.awt.Container;
036    import java.awt.Dimension;
037    import java.awt.EventQueue;
038    import java.awt.Font;
039    import java.awt.event.ActionEvent;
040    import java.awt.event.ActionListener;
041    import java.awt.event.WindowEvent;
042    import java.awt.event.WindowAdapter;
043    import java.awt.event.WindowListener;
044    import java.io.ByteArrayInputStream;
045    import java.io.ByteArrayOutputStream;
046    import java.io.File;
047    import java.io.IOException;
048    import java.util.Iterator;
049    import java.util.LinkedList;
050    import java.util.List;
051    
052    import javax.swing.BoxLayout;
053    import javax.swing.GroupLayout;
054    import javax.swing.JButton;
055    import javax.swing.JCheckBox;
056    import javax.swing.JComboBox;
057    import javax.swing.JDialog;
058    import javax.swing.JEditorPane;
059    import javax.swing.JFrame;
060    import javax.swing.JLabel;
061    import javax.swing.JOptionPane;
062    import javax.swing.JPanel;
063    import javax.swing.JRadioButton;
064    import javax.swing.JScrollPane;
065    import javax.swing.JSeparator;
066    import javax.swing.JTextField;
067    import javax.swing.JTextPane;
068    import javax.swing.JToolBar;
069    import javax.swing.ScrollPaneConstants;
070    import javax.swing.UIManager;
071    import javax.swing.WindowConstants;
072    import javax.swing.border.EmptyBorder;
073    import javax.swing.event.HyperlinkEvent;
074    import javax.swing.event.HyperlinkListener;
075    import javax.swing.text.PlainDocument;
076    
077    import org.slf4j.Logger;
078    import org.slf4j.LoggerFactory;
079    
080    import net.miginfocom.swing.MigLayout;
081    
082    import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
083    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.IconPanel;
084    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Prefer;
085    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width;
086    import edu.wisc.ssec.mcidasv.util.WebBrowser;
087    import edu.wisc.ssec.mcidasv.Constants;
088    
089    import ucar.ma2.Array;
090    import ucar.nc2.Variable;
091    import ucar.nc2.dataset.NetcdfDataset;
092    import ucar.nc2.ncml.NcMLReader;
093    import ucar.nc2.dt.grid.GridDataset;
094    
095    import ucar.unidata.util.FileManager;
096    import ucar.unidata.util.GuiUtils;
097    import ucar.unidata.util.IOUtil;
098    import ucar.unidata.util.Misc;
099    
100    import ucar.unidata.data.DataSourceDescriptor;
101    import ucar.unidata.data.grid.GeoGridDataSource;
102    import ucar.unidata.idv.IntegratedDataViewer;
103    
104    import visad.ConstantMap;
105    import visad.Display;
106    import visad.FlatField;
107    import visad.python.JPythonMethods;
108    import visad.ss.BasicSSCell;
109    import visad.ss.FancySSCell;
110    
111    
112    public class BadNetCDFWidget implements Constants {
113        
114        private static final Logger logger = LoggerFactory.getLogger(BadNetCDFWidget.class);
115        
116        private IntegratedDataViewer idv;
117        
118        private NetcdfDataset ncFile;
119        private List<Variable> varList;
120        private List<String> varNames;
121        
122        
123        // For NcML Editor
124        private JEditorPane NcMLeditor;
125        
126        
127        // For variable display
128        BasicSSCell display;
129        ConstantMap[] cmaps;
130        
131        
132        // For nav specification
133        private JRadioButton radioLatLonVars = new JRadioButton("Variables", true);
134        private JRadioButton radioLatLonBounds = new JRadioButton("Bounds", false);
135        
136        private JComboBox refComboBox = new JComboBox();
137        private JComboBox latComboBox = new JComboBox();
138        private JComboBox lonComboBox = new JComboBox();
139    
140        private JPanel panelLatLonVars = new JPanel();
141        private JPanel panelLatLonBounds = new JPanel();
142    
143        private JTextField textLatUL = new JTextField();
144        private JTextField textLonUL = new JTextField();
145        private JTextField textLatLR = new JTextField();
146        private JTextField textLonLR = new JTextField();
147    
148        
149        // TODO: DO WE NEED THESE?
150        private JTextField textLatLonScale = new JTextField();
151        private JCheckBox checkEastPositive = new JCheckBox("East positive");
152        
153        
154        
155        
156        public BadNetCDFWidget(NetcdfDataset ncFile, IntegratedDataViewer idv)
157        {
158            this.idv = idv;
159            this.ncFile = ncFile;
160            varList = ncFile.getVariables();  
161            
162            varNames = new LinkedList<String>();
163            
164            //System.out.println("Our file has " + varList.size() + " variables named:");
165            Iterator <Variable> varIt = varList.iterator();
166            while(varIt.hasNext())
167            {
168                Variable ourVar = varIt.next();
169                varNames.add(ourVar.getFullName());
170                //System.out.println("Name: " + ourVar.getName());
171            }
172        }
173        
174        
175        // Passes through any exception from openDataset - this function
176        // doesn't provide an IDV and should only be used for testing. (Some functionality
177        // using the rest of the IDV won't work.)
178        public BadNetCDFWidget(String filepath) throws IOException
179        {
180            this(NetcdfDataset.openDataset(filepath), null);
181        }
182        
183        
184        
185        // Tester function to pick a file and send it through the paces.
186        public static void main(String[] args)
187        {
188            String testfile = FileManager.getReadFile();        
189            System.out.println(testfile);
190            
191            //String testfile = "/Users/nickb/testdata/tester.nc";
192            
193            
194            BadNetCDFWidget bfReader;
195            try { bfReader = new BadNetCDFWidget(testfile); }
196            catch (Exception exe)
197            {
198                //System.out.println("This file cannot be read by the BadFileReader!");
199                exe.printStackTrace();
200                return;
201            }
202            
203            bfReader.showChoices();
204            //bfReader.showNavChooser();
205        }
206        
207        
208    
209        /////////////////////////////////////////////////////////
210        // Displays our "main menu" of choices to fix the given file!
211        // Everything else needed can get called from here.
212        /////////////////////////////////////////////////////////
213        public void showChoices()
214        {
215          EventQueue.invokeLater(new Runnable() {
216              public void run() {
217                  try {
218                      BadNetCDFDialog dialog = new BadNetCDFDialog();
219                      dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
220                      dialog.setVisible(true);
221                      dialog.toFront();
222                  } catch (Exception e) {
223                      e.printStackTrace();
224                  }
225              }
226          });
227        }
228    
229        /////////////////////////////////////////////////////////
230        // Creates an editor for NcML and displays it in a window - includes buttons for saving just
231        // the NcML and the full NetCDF file with the changes made.
232        /////////////////////////////////////////////////////////
233        private void showNcMLEditor()
234        {
235            NcMLeditor = new JEditorPane();
236            
237            // We use this to store the actual ncml - 10000 is just the number toolsUI used
238            ByteArrayOutputStream bos = new ByteArrayOutputStream(10000);
239            try
240            {
241                ncFile.writeNcML(bos, null);
242                NcMLeditor.setText(bos.toString());
243                NcMLeditor.setCaretPosition(0);
244                
245            } catch (IOException ioe)
246            {
247                ioe.printStackTrace();
248                //setInError(true, false, "");  DataSourceImpl - doesn't work if we're not a data source
249                return;
250            }
251            
252            NcMLeditor.setEditable(true);
253    
254            // Set the font style.
255            NcMLeditor.setFont(new Font("Courier", Font.PLAIN, 12));
256            
257            // Set the tab size
258            NcMLeditor.getDocument().putProperty(PlainDocument.tabSizeAttribute, 2);
259            
260    
261            // Button to save NcML as text, 
262            // popup allows them to specify where.
263            JButton saveNcMLBtn = new JButton("Save NcML as text");
264            ActionListener saveAction = new ActionListener()
265            {
266                public void actionPerformed(ActionEvent ae)
267                {
268                    // Begin with getting the filename we want to write to.
269                    String ncLocation = ncFile.getLocation();
270                    
271                    if (ncLocation == null) ncLocation = "test";
272                    int pos = ncLocation.lastIndexOf(".");
273                    if (pos > 0)
274                        ncLocation = ncLocation.substring(0, pos);
275    
276                    String filename = FileManager.getWriteFile(ncLocation + ".ncml");  
277                    if (filename == null) return;
278                   // System.out.println("Write NcML to filename:" + filename);
279                    
280                    
281                    // Once we have that, we can actually write to the file!
282                    try{
283                        IOUtil.writeFile(new File(filename), NcMLeditor.getText());
284                    } catch(Exception exc)
285                    {
286                        // TODO: Should probably add some kind of exception handling.
287                        exc.printStackTrace();
288                        return;
289                    }
290                }
291            };
292            saveNcMLBtn.addActionListener(saveAction);
293            
294    
295            // Button to merge the NcML with NetCDF 
296            // a'la ToolsUI and write it back out as NetCDF3.
297            JButton saveNetCDFBtn = new JButton("Merge and save NetCDF");
298            ActionListener saveNetCDFAction = new ActionListener()
299            {
300                public void actionPerformed(ActionEvent ae)
301                {
302                    // Begin with getting the filename we want to write to.
303                    String ncLocation = ncFile.getLocation();
304                    
305                    if (ncLocation == null) ncLocation = "test";
306                    int pos = ncLocation.lastIndexOf(".");
307                    if (pos > 0)
308                        ncLocation = ncLocation.substring(0, pos);
309    
310                    String filename = FileManager.getWriteFile(ncLocation + ".nc");  
311                    if (filename == null) return;
312                  //  System.out.println("Write NetCDF to filename:" + filename);
313                    
314                    
315                    // Once we have that, we can actually write to the file!
316                    try {
317                        ByteArrayInputStream bis = new ByteArrayInputStream(NcMLeditor.getText().getBytes());
318                        NcMLReader.writeNcMLToFile(bis, filename);
319                    } catch(Exception exc)
320                    {
321                        // TODO: Should probably add some kind of exception handling.
322                        exc.printStackTrace();
323                        return;
324                    }
325                }
326            };
327            saveNetCDFBtn.addActionListener(saveNetCDFAction);
328            
329            
330            // Button to load this data into McV from NcML
331            JButton sendToMcVBtn = new JButton("Attempt to load with this NcML");
332            ActionListener sendToMcVAction = new ActionListener()
333            {
334                public void actionPerformed(ActionEvent ae)
335                {
336                    // TODO: save the current NcML into the NetcdfDataSource
337                    createIDVdisplay();
338                    return;
339                }
340            };
341            sendToMcVBtn.addActionListener(sendToMcVAction);
342            
343            
344            JToolBar toolbar = new JToolBar("NcML Editor Controls");
345    
346            toolbar.add(saveNcMLBtn);
347            toolbar.add(saveNetCDFBtn);
348            toolbar.add(sendToMcVBtn);
349            
350            
351            JScrollPane scrollPane = new JScrollPane(NcMLeditor,
352                    ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
353                    ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
354    
355            scrollPane.setPreferredSize(new Dimension(600, 600));  // TODO: PREFERRED SIZE?
356            
357            JPanel panel = GuiUtils.topCenter(toolbar, scrollPane);
358            JFrame editorWindow = GuiUtils.makeWindow("NcML Editor", GuiUtils.inset(panel, 10), 0, 0);
359            editorWindow.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
360            editorWindow.setVisible(true);
361            editorWindow.toFront();
362    
363          //  System.out.println("NcML Editor Created!");
364        }
365        
366        
367        
368        
369        
370        /////////////////////////////////////////////////////////
371        // Takes our ncFile and puts it back into IDV (McV)
372        /////////////////////////////////////////////////////////
373        private void createIDVdisplay()
374        {
375            
376            // Make a NetcdfDataset from our NcML
377    
378            ByteArrayInputStream bis = new ByteArrayInputStream(NcMLeditor.getText().getBytes());
379            
380            try {
381                ncFile = NcMLReader.readNcML(bis, null);
382            } catch (IOException e1) {
383                // TODO Auto-generated catch block
384                e1.printStackTrace();
385                return;
386            }
387    
388            
389            // Now try to turn that NetcdfDataset into a legitimate DataSource!
390            GridDataset gd;
391            
392           // System.out.println("Creating the grid dataset...");
393            
394            try {
395                gd = new GridDataset(ncFile);
396            } catch (IOException e) {
397                // TODO Auto-generated catch block
398                e.printStackTrace();
399                return;
400            }
401            
402            ncFile.getLocation();
403          //  System.out.println("Grid dataset created!  Adding to data manager...");
404    //        GeoGridDataSource ggds = new GeoGridDataSource(gd);
405            DataSourceDescriptor dsd = new DataSourceDescriptor();
406            dsd.setLabel("NcML DS Label");
407            GeoGridDataSource ggds = new GeoGridDataSource(dsd, gd, "NcML Data Source", ncFile.getLocation());
408            ggds.initAfterCreation();
409            this.idv.getDataManager().addDataSource(ggds);
410        }
411        
412        
413        
414        
415        
416        /////////////////////////////////////////////////////////
417        // Shows a window that gives a choice of variables!
418        /////////////////////////////////////////////////////////
419        private void showVarPicker()
420        {
421            // DataImpl
422            //Array arr = var.read();
423                    
424            JComboBox varDD = new JComboBox();
425            GuiUtils.setListData(varDD, varNames);
426            
427            ActionListener getVarAction = new ActionListener()
428            {
429                public void actionPerformed(ActionEvent ae)
430                {
431                    JComboBox cb = (JComboBox)ae.getSource();
432                    Variable plotVar = varList.get(cb.getSelectedIndex());
433                    String varName = (String) cb.getSelectedItem();
434                    
435                    
436                    float [] varVals;
437                    try{
438                        // TODO: Is there a better way to convert this?  Is there another function like reshape?
439                        Array varArray = plotVar.read();
440                        varVals  = (float[]) varArray.get1DJavaArray(float.class);
441                        // TODO: Better exception handling
442                    }
443                    catch (IOException IOexe)
444                    {
445                        IOexe.printStackTrace();
446                        return;
447                    }
448                    
449                        int size = plotVar.getDimensions().size();
450                        if( size != 2)
451                        {
452                            //System.err.println("We should fail here because size != 2, size ==" + size);
453                            JOptionPane.showMessageDialog(null, 
454                                                        ("<html>Variables must have 2 dimensions to be displayed here.<br><br>\"" + varName + "\" has " + size + ".</html>"),
455                                                        "Invalid Dimensions",
456                                                        JOptionPane.ERROR_MESSAGE);
457                            return;
458                        }
459                        
460                        int xdim = plotVar.getDimensions().get(0).getLength();
461                        int ydim = plotVar.getDimensions().get(1).getLength();
462                        
463                      //  System.out.println("xdim: " + xdim + " ydim: " + ydim);
464                        
465                        float[][] var2D = reshape(varVals, ydim, xdim);
466                        
467                        //JPythonMethods.plot(varVals);
468                        //JPythonMethods.plot(var2D);
469                        
470                   try {
471                        FlatField varField = JPythonMethods.field(var2D);
472                        
473    
474                        ConstantMap[] cmaps = {
475                                  new ConstantMap(1.0, Display.Red),
476                                  new ConstantMap(1.0, Display.Green),
477                                  new ConstantMap(1.0, Display.Blue)
478                                }; 
479                        
480                        // Clear out the display or we get some weird stuff going on.
481                        display.clearCell();
482                        display.clearMaps();
483                        display.clearDisplay();  
484                        
485                        display.addData(varField, cmaps);                 
486                        
487                      /*  // Make sure data isn't lingering around:
488                        String[] dataSources = display.getDataSources();
489                        System.out.println("Data sources: " + dataSources.length);  */ 
490                   }
491                    // TODO: Better exception handling - throughout this whole file, really.
492                    catch (Exception exe)
493                    {
494                        exe.printStackTrace();
495                        return;
496    
497                    }
498                }
499            };
500            varDD.addActionListener(getVarAction);
501            
502            //BasicSSCell display = new FancySSCell("Variable!");
503            //display.setDimension(BasicSSCell.JAVA3D_3D);
504            
505            // TODO: exception handling
506            try 
507            { 
508                // Heavily borrowed from VISAD's JPythonMethods
509                display = new FancySSCell("Variable Viewer");
510                display.setDimension(BasicSSCell.JAVA3D_3D);
511                display.setPreferredSize(new Dimension(256, 256));
512                JFrame frame = new JFrame("Variable Viewer");
513                JPanel pane = new JPanel();
514                pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
515                frame.setContentPane(pane);
516                pane.add(varDD);
517                pane.add(display);
518                JButton controls = new JButton("Controls");
519                JPanel buttons = new JPanel();
520                buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS));
521                buttons.add(controls);
522                pane.add(buttons);
523                final FancySSCell fdisp = (FancySSCell) display;
524                fdisp.setAutoShowControls(false);
525                
526                controls.addActionListener(new ActionListener() {
527                    public void actionPerformed(ActionEvent e) {
528                      fdisp.showWidgetFrame();
529                    }
530                });
531                
532                frame.pack();
533               // frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
534                frame.setVisible(true);
535                frame.toFront();
536                        
537            }
538            // TODO: Exception handling
539            catch (Exception exe) {
540                exe.printStackTrace();
541                return;
542            }
543            
544        }
545        
546        
547        
548        // Quick and dirty function to reshape a 1D float array into a 2D
549        private static float[][] reshape(float[] arr, int m, int n)
550        {
551            float[][] newArr = new float[m][n];
552            
553            int index=0;
554            for (int i = 0; i < n; i++)
555            {
556                for(int j = 0; j< m; j++)
557                {
558                    newArr[j][i] = arr[index++];
559                }
560            }
561            return newArr;
562        }
563        
564        
565        
566        /////////////////////////////////////////////////////////
567        // Shows a window that gives the opportunity to either define 
568        // coordinate variables or specify corner points.
569        //
570        // Borrowed heavily from FlatFileChooser's makeNavigationPanel for style
571        /////////////////////////////////////////////////////////
572        private void showNavChooser()
573        {
574            JPanel midPanel = new JPanel();
575            midPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Navigation"));
576    
577            GuiUtils.setListData(refComboBox, varNames);
578            GuiUtils.setListData(latComboBox, varNames);
579            GuiUtils.setListData(lonComboBox, varNames);
580            
581            McVGuiUtils.setComponentWidth(latComboBox, Width.QUADRUPLE);
582            McVGuiUtils.setComponentWidth(lonComboBox, Width.QUADRUPLE);
583            McVGuiUtils.setComponentWidth(refComboBox, Width.QUADRUPLE);
584            
585            //panelLatLonVars = McVGuiUtils.topBottom(latComboBox, lonComboBox, Prefer.NEITHER);
586            panelLatLonVars = McVGuiUtils.topBottom(McVGuiUtils.makeLabeledComponent("Latitude:",latComboBox),
587                                                    McVGuiUtils.makeLabeledComponent("Longitude:",lonComboBox),
588                                                    Prefer.NEITHER);
589            
590    
591            GuiUtils.buttonGroup(radioLatLonVars, radioLatLonBounds);
592    
593            // Images to make the bounds more clear
594            IconPanel urPanel = new IconPanel("/edu/wisc/ssec/mcidasv/images/upper_right.gif");
595            IconPanel llPanel = new IconPanel("/edu/wisc/ssec/mcidasv/images/lower_left.gif");
596            
597            McVGuiUtils.setComponentWidth(textLatUL);
598            McVGuiUtils.setComponentWidth(textLonUL);
599            McVGuiUtils.setComponentWidth(textLatLR);
600            McVGuiUtils.setComponentWidth(textLonLR);
601            panelLatLonBounds = McVGuiUtils.topBottom(
602                    McVGuiUtils.makeLabeledComponent("UL Lat/Lon:", GuiUtils.leftRight(GuiUtils.hbox(textLatUL, textLonUL), urPanel)),
603                    McVGuiUtils.makeLabeledComponent("LR Lat/Lon:", GuiUtils.leftRight(llPanel, GuiUtils.hbox(textLatLR, textLonLR))),
604                    Prefer.NEITHER);
605            
606            panelLatLonBounds = McVGuiUtils.topBottom(panelLatLonBounds, McVGuiUtils.makeLabeledComponent("Reference:", refComboBox), Prefer.NEITHER);
607    
608            
609            McVGuiUtils.setComponentWidth(radioLatLonVars);
610            McVGuiUtils.setComponentWidth(radioLatLonBounds);
611    
612            
613            // Add a bit of a buffer to both
614            panelLatLonVars = GuiUtils.inset(panelLatLonVars, 5);
615            panelLatLonBounds = GuiUtils.inset(panelLatLonBounds, 5);
616            
617            GroupLayout layout = new GroupLayout(midPanel);
618            midPanel.setLayout(layout);
619            layout.setHorizontalGroup(
620                layout.createParallelGroup(LEADING)
621                .addGroup(layout.createSequentialGroup()
622                    .addContainerGap()
623                    .addGroup(layout.createParallelGroup(LEADING)
624                        .addGroup(layout.createSequentialGroup()
625                            .addComponent(radioLatLonVars)
626                            .addGap(GAP_RELATED)
627                            .addComponent(panelLatLonVars))
628                        .addGroup(layout.createSequentialGroup()
629                            .addComponent(radioLatLonBounds)
630                            .addGap(GAP_RELATED)
631                            .addComponent(panelLatLonBounds)))
632                    .addContainerGap())
633            );
634            layout.setVerticalGroup(
635                layout.createParallelGroup(LEADING)
636                .addGroup(TRAILING, layout.createSequentialGroup()
637                    .addContainerGap()
638                    .addGroup(layout.createParallelGroup(BASELINE)
639                        .addComponent(radioLatLonVars)
640                        .addComponent(panelLatLonVars))
641                    .addPreferredGap(RELATED)
642                    .addGroup(layout.createParallelGroup(BASELINE)
643                        .addComponent(radioLatLonBounds)
644                        .addComponent(panelLatLonBounds))
645                    .addPreferredGap(RELATED)
646                    .addContainerGap())
647            );
648     
649    
650            
651            radioLatLonVars.addActionListener(new ActionListener(){
652                public void actionPerformed(ActionEvent e) {
653                    checkSetLatLon();
654                }
655            });
656            
657            radioLatLonBounds.addActionListener(new ActionListener(){
658                public void actionPerformed(ActionEvent e) {
659                    checkSetLatLon();
660                }
661            });
662            
663            
664            JButton goBtn = new JButton("Go!");
665            ActionListener goBtnAction = new ActionListener()
666            {
667                public void actionPerformed(ActionEvent ae)
668                {
669                    boolean isVar = radioLatLonVars.isSelected();
670                    if (isVar)
671                        navVarAction();
672                    else 
673                        navCornersAction();
674                }
675            };
676            goBtn.addActionListener(goBtnAction);
677            
678            
679            JPanel wholePanel = McVGuiUtils.topBottom(midPanel, goBtn, Prefer.NEITHER);
680                
681            JFrame myWindow = GuiUtils.makeWindow("Pick Your Navigation!", GuiUtils.inset(wholePanel, 10), 0, 0);
682            checkSetLatLon();
683            myWindow.setVisible(true);
684            myWindow.toFront();
685        }
686        
687        
688        /**
689         * enable/disable widgets for navigation
690         */
691        private void checkSetLatLon() {
692            boolean isVar = radioLatLonVars.isSelected();
693            GuiUtils.enableTree(panelLatLonVars, isVar);
694            GuiUtils.enableTree(panelLatLonBounds, !isVar);
695        }
696        
697        
698        /**
699         * 
700         * One of the two workhorses of our nav chooser, it alters the chosen 
701         * (existing) variables so they can be used as lat/lon pairs.
702         * 
703         */
704        private void navVarAction()
705        {
706            
707        }
708        
709        
710    
711        /**
712         * 
713         * One of the two workhorses of our nav chooser, it creates new 
714         * variables for lat/lon based on the specified cornerpoints and
715         * reference variable (for dimensions.)
716         * 
717         */
718        
719        private void navCornersAction()
720        {
721            
722        }
723        
724        public class BadNetCDFDialog extends JDialog {
725    
726            /**
727             * Create the dialog.
728             */
729            public BadNetCDFDialog() {
730                setTitle("Non-Compliant NetCDF Tool");
731                setMinimumSize(new Dimension(705, 320));
732                setBounds(100, 100, 705, 320);
733                Container contentPane = getContentPane();
734                
735                JLabel headerLabel = new JLabel("McIDAS-V is unable to read your file.");
736                headerLabel.setFont(UIManager.getFont("OptionPane.font"));
737                headerLabel.setBorder(new EmptyBorder(0, 0, 4, 0));
738                
739                JTextPane messageTextPane = new JTextPane();
740                Font textPaneFont = UIManager.getFont("TextPane.font");
741                String fontCss = String.format("style=\"font-family: '%s'; font-size: %d;\"", textPaneFont.getFamily(), textPaneFont.getSize());
742                messageTextPane.setBackground(UIManager.getColor("Label.background"));
743                messageTextPane.setContentType("text/html");
744                messageTextPane.setDragEnabled(false);
745                messageTextPane.setText("<html>\n<body "+fontCss +">To verify if your file is CF-compliant, you can run your file through an online compliance checker (<a href=\"http://titania.badc.rl.ac.uk/cgi-bin/cf-checker.pl\">example CF-compliance utility</a>).<br/><br/> \n\nIf the checker indicates that your file is not compliant you can attempt to fix it using the NcML Editor provided in this window.<br/><br/>\n\nIn a future release of McIDAS-V, this interface will present you with choices for the variables necessary for McIDAS-V to display your data.<br/></font></body></html>");
746                messageTextPane.setEditable(false);
747                messageTextPane.addHyperlinkListener(new HyperlinkListener() {
748                    public void hyperlinkUpdate(HyperlinkEvent e) {
749                        if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) {
750                            return;
751                        }
752                        String url = null;
753                        if (e.getURL() == null) {
754                            url = e.getDescription();
755                        } else {
756                            url = e.getURL().toString();
757                        }
758                        WebBrowser.browse(url);
759                    }
760                });
761                
762    
763                JSeparator separator = new JSeparator();
764                // seems pretty dumb to have to do this sizing business in order to get the separator to appear, right?
765                // check out: http://docs.oracle.com/javase/tutorial/uiswing/components/separator.html
766                // "Separators have almost no API and are extremely easy to use as 
767                // long as you keep one thing in mind: In most implementations, 
768                // a vertical separator has a preferred height of 0, and a 
769                // horizontal separator has a preferred width of 0. This means a 
770                // separator is not visible unless you either set its preferred 
771                // size or put it in under the control of a layout manager such as 
772                // BorderLayout or BoxLayout that stretches it to fill its 
773                // available display area."
774                // WHO ON EARTH DECIDED THAT WAS SENSIBLE DEFAULT BEHAVIOR FOR A SEPARATOR WIDGET!?
775                separator.setMinimumSize(new Dimension(1, 12));
776                separator.setPreferredSize(new Dimension(1, 12));
777    
778                
779                JLabel editorLabel = new JLabel("Open the file in the NcML editor:");
780                
781                JButton editorButton = new JButton("NcML Editor");
782                editorButton.addActionListener(new ActionListener() {
783                    @Override public void actionPerformed(ActionEvent e) {
784                        showNcMLEditor();
785                    }
786                });
787                
788                JLabel viewLabel = new JLabel("I just want to view one of the variables:");
789                
790                JButton viewButton = new JButton("View Variable");
791                viewButton.addActionListener(new ActionListener() {
792                    @Override public void actionPerformed(ActionEvent e) {
793                        showVarPicker();
794                    }
795                });
796                
797                JLabel noncompliantLabel = new JLabel("I have navigation variables, they just aren't CF-compliant: (FEATURE INCOMPLETE)");
798                
799                JButton noncompliantButton = new JButton("Choose Nav");
800                noncompliantButton.addActionListener(new ActionListener() {
801                    public void actionPerformed(ActionEvent e) {
802                        showNavChooser();
803                    }
804                });
805                this.addWindowListener(new WindowAdapter() {
806                    public void windowClosing(WindowEvent e) {
807                        logger.trace("disposing of dialog");
808                        BadNetCDFDialog.this.dispose();
809                    }
810                });
811    
812                contentPane.setLayout(new MigLayout(
813                    "", 
814                    "[grow][]", 
815                    "[][grow][][][][][][]"));
816                contentPane.add(headerLabel,        "spanx 2, alignx left, aligny top, wrap");
817                contentPane.add(messageTextPane,    "spanx 2, grow, wrap");
818                contentPane.add(separator,          "spanx 2, growx, aligny top, wrap");
819                contentPane.add(editorLabel,        "alignx left, aligny baseline");
820                contentPane.add(editorButton,       "growx, aligny baseline, wrap");
821                contentPane.add(viewLabel,          "alignx left, aligny baseline");
822                contentPane.add(viewButton,         "growx, aligny baseline, wrap");
823                contentPane.add(noncompliantLabel,  "alignx left, aligny baseline");
824                contentPane.add(noncompliantButton, "growx, aligny baseline, wrap");
825                
826            }
827        }
828    }