001/*
002 * $Id: FlatFileChooser.java,v 1.11 2011/03/24 16:06:31 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2011
007 * Space Science and Engineering Center (SSEC)
008 * University of Wisconsin - Madison
009 * 1225 W. Dayton Street, Madison, WI 53706, USA
010 * https://www.ssec.wisc.edu/mcidas
011 * 
012 * All Rights Reserved
013 * 
014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015 * some McIDAS-V source code is based on IDV and VisAD source code.  
016 * 
017 * McIDAS-V is free software; you can redistribute it and/or modify
018 * it under the terms of the GNU Lesser Public License as published by
019 * the Free Software Foundation; either version 3 of the License, or
020 * (at your option) any later version.
021 * 
022 * McIDAS-V is distributed in the hope that it will be useful,
023 * but WITHOUT ANY WARRANTY; without even the implied warranty of
024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
025 * GNU Lesser Public License for more details.
026 * 
027 * You should have received a copy of the GNU Lesser Public License
028 * along with this program.  If not, see http://www.gnu.org/licenses.
029 */
030
031package edu.wisc.ssec.mcidasv.chooser;
032
033
034import static javax.swing.GroupLayout.DEFAULT_SIZE;
035import static javax.swing.GroupLayout.Alignment.BASELINE;
036import static javax.swing.GroupLayout.Alignment.LEADING;
037import static javax.swing.GroupLayout.Alignment.TRAILING;
038import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
039import static javax.swing.LayoutStyle.ComponentPlacement.UNRELATED;
040
041import java.awt.Image;
042import java.awt.MediaTracker;
043import java.awt.Toolkit;
044import java.awt.event.ActionEvent;
045import java.awt.event.ActionListener;
046import java.awt.event.FocusEvent;
047import java.awt.event.FocusListener;
048import java.io.File;
049import java.io.FileNotFoundException;
050import java.io.FileReader;
051import java.io.InputStream;
052import java.util.ArrayList;
053import java.util.Hashtable;
054import java.util.List;
055
056import javax.swing.GroupLayout;
057import javax.swing.JButton;
058import javax.swing.JCheckBox;
059import javax.swing.JComboBox;
060import javax.swing.JComponent;
061import javax.swing.JFileChooser;
062import javax.swing.JLabel;
063import javax.swing.JPanel;
064import javax.swing.JRadioButton;
065import javax.swing.JTextField;
066
067import org.w3c.dom.Element;
068
069import ucar.unidata.idv.IntegratedDataViewer;
070import ucar.unidata.idv.chooser.IdvChooser;
071import ucar.unidata.idv.chooser.IdvChooserManager;
072import ucar.unidata.util.GuiUtils;
073import ucar.unidata.util.IOUtil;
074import ucar.unidata.util.Misc;
075import ucar.unidata.util.TwoFacedObject;
076import ucar.unidata.xml.XmlUtil;
077import visad.util.ImageHelper;
078import edu.wisc.ssec.mcidasv.Constants;
079import edu.wisc.ssec.mcidasv.data.AxformInfo;
080import edu.wisc.ssec.mcidasv.data.EnviInfo;
081import edu.wisc.ssec.mcidasv.data.HeaderInfo;
082import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
083import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Position;
084import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Prefer;
085import edu.wisc.ssec.mcidasv.util.McVGuiUtils.TextColor;
086import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width;
087import edu.wisc.ssec.mcidasv.util.McVGuiUtils.IconPanel;
088
089/**
090 * @author SSEC Development Team
091 */
092
093public class FlatFileChooser extends IdvChooser implements Constants {
094
095    /** Set default stride to keep dimensions within this */
096    private int maxDefDim = 1000;
097    
098    // Properties associated with the button selector
099    private File dataFile;
100    private JTextField dataFileText = new JTextField();
101    private JButton dataFileButton = new JButton();
102    private JLabel dataFileDescription = new JLabel();
103    private JLabel textDescription = new JLabel();
104    
105    // Dimensions
106    // elements, lines, bands
107    private JTextField textElements = new JTextField();
108    private JTextField textLines = new JTextField();
109    private JTextField textBands = new JTextField();
110    private JTextField textUnit = new JTextField();
111    private JTextField textStride = new JTextField();
112    private JCheckBox checkTranspose = new JCheckBox("Transpose elements/lines");
113    private List bandNames = new ArrayList();
114    private List bandFiles = new ArrayList();
115
116    // Navigation
117    // lat/lon files or bounds
118    private JRadioButton radioLatLonFiles = new JRadioButton("Files", true);
119    private JRadioButton radioLatLonBounds = new JRadioButton("Bounds", false);
120    private File latFile, lonFile;
121    private JLabel textLatFile = new JLabel();
122    private JButton buttonLatFile = new JButton();
123    private JLabel textLonFile = new JLabel();
124    private JButton buttonLonFile = new JButton();
125    private JPanel panelLatLonFiles = new JPanel();
126    private JTextField textLatUL = new JTextField();
127    private JTextField textLonUL = new JTextField();
128    private JTextField textLatLR = new JTextField();
129    private JTextField textLonLR = new JTextField();
130    private JPanel panelLatLonBounds = new JPanel();
131    private JTextField textLatLonScale = new JTextField();
132    private JCheckBox checkEastPositive = new JCheckBox("East positive");
133
134
135    // Properties associated with the data file
136    // bytes/pixel, ASCII delimiter, endianness, interleave, offset, missing
137    private JRadioButton radioBinary = new JRadioButton("Binary", true);
138    private JRadioButton radioASCII = new JRadioButton("ASCII", false);
139    private JRadioButton radioImage = new JRadioButton("Image", false);
140    private JRadioButton radioEndianLittle = new JRadioButton("Little", true);
141    private JRadioButton radioEndianBig = new JRadioButton("Big", false);
142    private JComboBox comboByteFormat = new JComboBox();
143    private JComboBox comboInterleave = new JComboBox();
144    private JTextField textOffset = new JTextField();
145    private JPanel panelBinary = new JPanel();
146    private JTextField textDelimiter = new JTextField();
147    private JPanel panelASCII = new JPanel();
148    private JPanel panelImage = new JPanel();
149    private JTextField textMissing = new JTextField();
150    
151    private List<TwoFacedObject> listByteFormat = Misc.newList(new TwoFacedObject[] {
152            new TwoFacedObject("1-byte unsigned integer", HeaderInfo.kFormat1ByteUInt),
153            new TwoFacedObject("2-byte signed integer", HeaderInfo.kFormat2ByteSInt),
154            new TwoFacedObject("4-byte signed integer", HeaderInfo.kFormat4ByteSInt),
155            new TwoFacedObject("4-byte float", HeaderInfo.kFormat4ByteFloat),
156            new TwoFacedObject("8-byte double", HeaderInfo.kFormat8ByteDouble),
157            new TwoFacedObject("2x8-byte complex number", HeaderInfo.kFormat2x8Byte),
158            new TwoFacedObject("2-byte unsigned integer", HeaderInfo.kFormat2ByteUInt)
159    });
160
161    private List<TwoFacedObject> listInterleave = Misc.newList(
162            new TwoFacedObject("Sequential", HeaderInfo.kInterleaveSequential),
163            new TwoFacedObject("By line", HeaderInfo.kInterleaveByLine),
164            new TwoFacedObject("By pixel", HeaderInfo.kInterleaveByPixel));
165
166    private JLabel statusLabel = new JLabel("Status");
167    
168    /**
169     * Super setStatus() takes a second string to enable "simple" mode
170     * which highlights the required component.  We don't really care
171     * about that feature, and we don't want getStatusLabel() to
172     * change the label background color.
173     */
174    @Override
175    public void setStatus(String statusString, String foo) {
176        if (statusString == null)
177            statusString = "";
178        statusLabel.setText(statusString);
179    }
180
181    /**
182     * Get a handle on the IDV
183     */
184    protected IntegratedDataViewer idv = getIdv();
185
186    /**
187     * Create the FileChooser, passing in the manager and the xml element
188     * from choosers.xml
189     *
190     * @param mgr The manager
191     * @param root The xml root
192     *
193     */
194    public FlatFileChooser(IdvChooserManager mgr, Element root) {
195        super(mgr, root);
196        
197        loadButton = McVGuiUtils.makeImageTextButton(ICON_ACCEPT_SMALL, getLoadCommandName());
198        loadButton.setActionCommand(getLoadCommandName());
199        loadButton.addActionListener(this);
200
201        dataFileButton = McVGuiUtils.makeImageButton(ICON_OPEN, "Open file");
202        dataFileButton.addActionListener(new ActionListener(){
203            public void actionPerformed(ActionEvent e) {
204                dataFile = getDataFile(dataFile);
205                if (dataFile!=null) {
206                    dataFileText.setText(dataFile.getAbsolutePath());
207                    inspectDataFile(dataFile);
208                }
209            }
210        });
211        dataFileText.addActionListener(new ActionListener(){
212            public void actionPerformed(ActionEvent e) {
213                dataFile = new File(dataFileText.getText());
214                inspectDataFile(dataFile);
215            }
216        });
217        dataFileText.addFocusListener(new FocusListener() {
218            public void focusGained(FocusEvent e) {}
219            public void focusLost(FocusEvent e) {
220                dataFile = new File(dataFileText.getText());
221                inspectDataFile(dataFile);
222            }
223        });
224        
225        radioLatLonFiles.addActionListener(new ActionListener(){
226            public void actionPerformed(ActionEvent e) {
227                checkSetLatLon();
228            }
229        });
230        radioLatLonBounds.addActionListener(new ActionListener(){
231            public void actionPerformed(ActionEvent e) {
232                checkSetLatLon();
233            }
234        });
235
236        buttonLatFile = McVGuiUtils.makeImageButton(ICON_OPEN, "Select latitude file");
237        buttonLatFile.addActionListener(new ActionListener(){
238            public void actionPerformed(ActionEvent e) {
239                latFile = getDataFile(latFile);
240                if (latFile!=null)
241                    textLatFile.setText(latFile.getName());
242            }
243        });
244        buttonLonFile = McVGuiUtils.makeImageButton(ICON_OPEN, "Select longitude file");
245        buttonLonFile.addActionListener(new ActionListener(){
246            public void actionPerformed(ActionEvent e) {
247                lonFile = getDataFile(lonFile);
248                if (lonFile!=null)
249                    textLonFile.setText(lonFile.getName());
250            }
251        });
252        GuiUtils.buttonGroup(radioLatLonFiles, radioLatLonBounds);
253        
254        radioBinary.addActionListener(new ActionListener(){
255            public void actionPerformed(ActionEvent e) {
256                checkSetBinaryASCIIImage();
257            }
258        });
259        radioASCII.addActionListener(new ActionListener(){
260            public void actionPerformed(ActionEvent e) {
261                checkSetBinaryASCIIImage();
262            }
263        });
264        radioImage.addActionListener(new ActionListener(){
265            public void actionPerformed(ActionEvent e) {
266                checkSetBinaryASCIIImage();
267            }
268        });
269        GuiUtils.buttonGroup(radioBinary, radioASCII, radioImage);
270        GuiUtils.buttonGroup(radioEndianLittle, radioEndianBig);
271
272        GuiUtils.setListData(comboByteFormat, listByteFormat);
273        GuiUtils.setListData(comboInterleave, listInterleave);
274
275        setHaveData(false);
276    }
277    
278    /**
279     * enable/disable widgets for navigation
280     */
281    private void checkSetLatLon() {
282        boolean isFile = radioLatLonFiles.isSelected();
283        GuiUtils.enableTree(panelLatLonFiles, isFile);
284        GuiUtils.enableTree(panelLatLonBounds, !isFile);
285    }
286
287    /**
288     * enable/disable widgets for binary/ASCII
289     */
290    private void checkSetBinaryASCIIImage() {
291        GuiUtils.enableTree(panelBinary, radioBinary.isSelected());
292        GuiUtils.enableTree(panelASCII, radioASCII.isSelected());
293        GuiUtils.enableTree(panelImage, radioImage.isSelected());
294    }
295
296    /**
297     * Set whether the user has made a selection that contains data.
298     *
299     * @param have   true to set the haveData property.  Enables the
300     *               loading button
301     */
302    public void setHaveData(boolean have) {
303        super.setHaveData(have);
304        updateStatus();
305    }
306    
307    /**
308     * Set the status message appropriately
309     */
310    protected void updateStatus() {
311        super.updateStatus();
312        checkSetLatLon();
313        checkSetBinaryASCIIImage();
314        if(!getHaveData()) {
315            setStatus("Select a file"); 
316        }
317    }
318
319    /**
320     * Inspect the selected data file
321     * Determine if it is a known header file type
322     * 
323     * @param thisFile
324     * @throws FileNotFoundException 
325     */
326    private void inspectDataFile(File thisFile) {
327        if (thisFile == null || thisFile.getName()=="") {
328            dataFileDescription.setText("");
329            setHaveData(false);
330            return;
331        }
332        if (!thisFile.exists()) {
333            dataFileDescription.setText("File does not exist");
334            setHaveData(false);
335            return;
336        }
337        try {
338            FileReader fr = new FileReader(thisFile);
339            char first80c[] = new char[80];
340            fr.read(first80c, 0, 80);
341            fr.close();
342            String first80 = new String(first80c);
343            clearValues();
344            boolean doStride = false;
345            if (IOUtil.hasSuffix(thisFile.getName(), ".gif") ||
346                    IOUtil.hasSuffix(thisFile.getName(), ".jpg") ||
347                    IOUtil.hasSuffix(thisFile.getName(), ".png")) {
348                dataFileDescription.setText("Image file");
349                processImageFile(thisFile);
350            }
351            else if (IOUtil.hasSuffix(thisFile.getName(), ".xml") ||
352                    IOUtil.hasSuffix(thisFile.getName(), ".ximg")) {
353                dataFileDescription.setText("XML image header file");
354                processXmlHeaderFile(thisFile);
355            }
356            else if (first80.indexOf("                     Space Science & Engineering Center") >= 0) {
357                dataFileDescription.setText("McIDAS-X AXFORM header file");
358                processAxformHeaderFile(thisFile);
359                doStride = true;
360            }
361            else if (first80.indexOf("ENVI") >= 0) {
362                dataFileDescription.setText("ENVI header file");
363                processEnviHeaderFile(thisFile);
364                doStride = true;
365            }
366            else {
367                dataFileDescription.setText("Binary, ASCII or Image data");
368                processGenericFile(thisFile);
369                doStride = true;
370            }
371            
372            // Default the stride
373            int newStride = 1;
374            if (doStride) {
375                String textLinesText = textLines.getText();
376                String textElementsText = textElements.getText();
377                if (!(textLinesText.equalsIgnoreCase("") || textElementsText.equalsIgnoreCase(""))) {
378                    int myLines = Integer.parseInt(textLinesText);
379                    int myElements = Integer.parseInt(textElementsText);
380                    if (myLines > maxDefDim || myElements > maxDefDim) {
381                        newStride = Math.max((int)Math.ceil((float)myLines/(float)maxDefDim), (int)Math.ceil((float)myElements/(float)maxDefDim));
382                    }
383                }
384            }
385            textStride.setText(Integer.toString(newStride));
386            
387            setHaveData(true);
388        }
389        catch (Exception e) {
390            e.printStackTrace();
391        }
392    }
393        
394    /**
395     * Special processing for a known data type
396     * This deals specifically with AXFORM header files
397     */
398    private void processAxformHeaderFile(File thisFile) {
399        try {
400            AxformInfo axformInfo = new AxformInfo(thisFile);
401            
402            // Set the properties in the GUI
403            textDescription.setText(axformInfo.getParameter(HeaderInfo.DESCRIPTION, ""));
404            textElements.setText((axformInfo.getParameter(HeaderInfo.ELEMENTS, 0)).toString());
405            textLines.setText((axformInfo.getParameter(HeaderInfo.LINES, 0)).toString());
406            textUnit.setText((axformInfo.getParameter(HeaderInfo.UNIT, "")).toString());
407            bandNames = (List)axformInfo.getParameter(HeaderInfo.BANDNAMES, new ArrayList());
408            bandFiles = (List)axformInfo.getParameter(HeaderInfo.BANDFILES, new ArrayList());
409            textBands.setText(Integer.toString(bandNames.size()));
410            textOffset.setText((axformInfo.getParameter(HeaderInfo.OFFSET, 0)).toString());
411            textMissing.setText((axformInfo.getParameter(HeaderInfo.MISSINGVALUE, (float)0)).toString());
412            
413            Integer dataType = (Integer)axformInfo.getParameter(HeaderInfo.DATATYPE, HeaderInfo.kFormatUnknown);
414            Boolean bigEndian = (Boolean)axformInfo.getParameter(HeaderInfo.BIGENDIAN, false);
415            if (dataType==HeaderInfo.kFormatASCII) {
416                radioASCII.setSelected(true);
417            }
418            else if (dataType==HeaderInfo.kFormatImage) {
419                radioImage.setSelected(true);
420            }
421            else {
422                radioBinary.setSelected(true);
423                TwoFacedObject tfo = TwoFacedObject.findId(dataType.intValue(), listByteFormat);
424                if (tfo!=null)
425                    comboByteFormat.setSelectedItem(tfo);
426                tfo = TwoFacedObject.findId(HeaderInfo.kInterleaveSequential, listInterleave);
427                if (tfo!=null)
428                    comboInterleave.setSelectedItem(tfo);
429            }
430            
431            radioEndianLittle.setSelected(!bigEndian.booleanValue());
432            radioEndianBig.setSelected(bigEndian.booleanValue());
433
434            List latlonFiles = axformInfo.getParameter(HeaderInfo.NAVFILES, new ArrayList());
435            if (latlonFiles.size() == 2) {
436                latFile = new File((String)latlonFiles.get(0));
437                lonFile = new File((String)latlonFiles.get(1));
438            }
439
440            if (latFile==null || lonFile==null) {
441                radioLatLonBounds.setSelected(true);
442            }
443            else {
444                textLatFile.setText(latFile.getName());
445                textLonFile.setText(lonFile.getName());
446                radioLatLonFiles.setSelected(true);
447            }
448            
449            textLatLonScale.setText("100");
450            checkEastPositive.setSelected(true);
451            
452        }
453        catch (Exception e) {
454            e.printStackTrace();
455        }
456    }
457
458    /**
459     * Special processing for a known data type
460     * This deals specifically with ENVI header files
461     */
462    private void processEnviHeaderFile(File thisFile) {
463        try {
464            EnviInfo enviInfo = new EnviInfo(thisFile);
465            
466            // Set the properties in the GUI
467            textDescription.setText(enviInfo.getParameter(HeaderInfo.DESCRIPTION, ""));
468            textElements.setText((enviInfo.getParameter(HeaderInfo.ELEMENTS, 0)).toString());
469            textLines.setText((enviInfo.getParameter(HeaderInfo.LINES, 0)).toString());
470            textUnit.setText((enviInfo.getParameter(HeaderInfo.UNIT, "")).toString());
471            bandNames = (List)enviInfo.getParameter(HeaderInfo.BANDNAMES, new ArrayList());
472            bandFiles = (List)enviInfo.getParameter(HeaderInfo.BANDFILES, new ArrayList());
473            textBands.setText(Integer.toString(bandNames.size()));
474            textOffset.setText((enviInfo.getParameter(HeaderInfo.OFFSET, 0)).toString());
475            textMissing.setText((enviInfo.getParameter(HeaderInfo.MISSINGVALUE, (float)0)).toString());
476
477            Integer dataType = (Integer)enviInfo.getParameter(HeaderInfo.DATATYPE, HeaderInfo.kFormatUnknown);
478            String interleaveType = (enviInfo.getParameter(HeaderInfo.INTERLEAVE, HeaderInfo.kInterleaveSequential)).toString();
479            Boolean bigEndian = (Boolean)enviInfo.getParameter(HeaderInfo.BIGENDIAN, false);
480            radioBinary.setSelected(true);
481            TwoFacedObject tfo = TwoFacedObject.findId(dataType.intValue(), listByteFormat);
482            if (tfo!=null)
483                comboByteFormat.setSelectedItem(tfo);
484            tfo = TwoFacedObject.findId(interleaveType, listInterleave);
485            if (tfo!=null)
486                comboInterleave.setSelectedItem(tfo);
487            
488            radioEndianLittle.setSelected(!bigEndian.booleanValue());
489            radioEndianBig.setSelected(bigEndian.booleanValue());
490
491            // Look for a geo.hdr file that contains Latitude and Longitude bands
492            String parent = thisFile.getParent();
493            if (parent==null) parent=".";
494            String navFile = thisFile.getName().replace(".hdr", "");
495            int lastDot = navFile.lastIndexOf(".");
496            if (lastDot >= 0) {
497                navFile = navFile.substring(0, lastDot) + ".geo.hdr";
498            }
499            navFile = parent + "/" + navFile;
500            EnviInfo navInfo = new EnviInfo(navFile);
501            if (navInfo.isNavHeader()) {
502                latFile = new File(navFile);
503                lonFile = new File(navFile);
504            }
505            
506            if (latFile==null || lonFile==null) {
507                radioLatLonBounds.setSelected(true);
508            }
509            else {
510                textLatFile.setText(latFile.getName());
511                textLonFile.setText(lonFile.getName());
512                radioLatLonFiles.setSelected(true);
513            }
514            
515            textLatLonScale.setText("1");
516            checkEastPositive.setSelected(false);
517
518        }
519        catch (Exception e) {
520            e.printStackTrace();
521        }
522    }
523
524    /**
525     * Special processing for a known data type
526     * This deals specifically with XML header files
527     */
528    private void processXmlHeaderFile(File thisFile) {
529        try {
530            
531            String description = "";
532            int lines = 0;
533            int elements = 0;
534            float missingVal = -1;
535            
536            bandFiles = new ArrayList();
537            bandNames = new ArrayList();
538
539            Element root = XmlUtil.getRoot(thisFile.getAbsolutePath(), getClass());
540            if (!root.getTagName().equals("image")) {
541                processGenericFile(thisFile);
542                return;
543            }
544            
545            description = XmlUtil.getAttribute(root, "name", (String) null);
546            String url = "";
547            if (XmlUtil.hasAttribute(root, "url")) {
548                url = XmlUtil.getAttribute(root, "url");
549                if (description == "") {
550                    description = url;
551                }
552                String parent = thisFile.getParent();
553                if (parent==null) parent=".";
554                url = parent + "/" + url;
555            }
556            else {
557                processGenericFile(thisFile);
558                return;
559            }
560            if (XmlUtil.hasAttribute(root, "ullat")) {
561                radioLatLonBounds.setSelected(true);
562                textLatUL.setText(XmlUtil.getAttribute(root, "ullat"));
563            }
564            if (XmlUtil.hasAttribute(root, "ullon")) {
565                radioLatLonBounds.setSelected(true);
566                textLonUL.setText(XmlUtil.getAttribute(root, "ullon"));
567            }
568            if (XmlUtil.hasAttribute(root, "lrlat")) {
569                radioLatLonBounds.setSelected(true);
570                textLatLR.setText(XmlUtil.getAttribute(root, "lrlat"));
571            }
572            if (XmlUtil.hasAttribute(root, "lrlon")) {
573                radioLatLonBounds.setSelected(true);
574                textLonLR.setText(XmlUtil.getAttribute(root, "lrlon"));
575            }
576
577            // Try to read the referenced image to get lines and elements
578            setStatus("Loading image");
579            InputStream is   = null;
580            is = IOUtil.getInputStream(url, getClass());
581            byte[] imageContent = IOUtil.readBytes(is);
582            Image image = Toolkit.getDefaultToolkit().createImage(imageContent);
583            MediaTracker tracker = new MediaTracker(this); 
584            tracker.addImage(image, 0);
585            try { 
586                tracker.waitForAll(); 
587            } 
588            catch(InterruptedException e) {}
589            ImageHelper ih = new ImageHelper();
590            image.getWidth(ih);
591            if (ih.badImage) {
592                throw new IllegalStateException("Bad image: " + url);
593            }
594            elements = image.getWidth(ih);
595            lines = image.getHeight(ih);
596            
597            // Bands
598            bandFiles.add(url);
599            bandNames.add("XML image file");
600            
601            // Set the properties in the GUI
602            textDescription.setText(description);
603            textElements.setText(Integer.toString(elements));
604            textLines.setText(Integer.toString(lines));
605            textBands.setText(Integer.toString(bandNames.size()));
606
607            radioImage.setSelected(true);
608            textMissing.setText(Float.toString(missingVal));
609            
610            textLatLonScale.setText("1");
611            checkEastPositive.setSelected(false);
612
613        }
614        catch (Exception e) {
615            e.printStackTrace();
616        }
617    }
618
619    /**
620     * Special processing for a known data type
621     * This deals specifically with XML header files
622     */
623    private void processImageFile(File thisFile) {
624        try {
625            
626            String description = "";
627            int lines = 0;
628            int elements = 0;
629            float missingVal = -1;
630            
631            bandFiles = new ArrayList();
632            bandNames = new ArrayList();
633
634            description = thisFile.getName();
635            String url = thisFile.getAbsolutePath();
636
637            // Try to read the referenced image to get lines and elements
638            setStatus("Loading image");
639            InputStream is   = null;
640            is = IOUtil.getInputStream(url, getClass());
641            byte[] imageContent = IOUtil.readBytes(is);
642            Image image = Toolkit.getDefaultToolkit().createImage(imageContent);
643            MediaTracker tracker = new MediaTracker(this); 
644            tracker.addImage(image, 0);
645            try { 
646                tracker.waitForAll(); 
647            } 
648            catch(InterruptedException e) {}
649            ImageHelper ih = new ImageHelper();
650            image.getWidth(ih);
651            if (ih.badImage) {
652                throw new IllegalStateException("Bad image: " + url);
653            }
654            elements = image.getWidth(ih);
655            lines = image.getHeight(ih);
656
657            // Bands
658            bandFiles.add(url);
659            bandNames.add("Image file");
660
661            // Set the properties in the GUI
662            textDescription.setText(description);
663            textElements.setText(Integer.toString(elements));
664            textLines.setText(Integer.toString(lines));
665            textBands.setText(Integer.toString(bandNames.size()));
666            
667            radioImage.setSelected(true);
668            textMissing.setText(Float.toString(missingVal));
669            
670            textLatLonScale.setText("1");
671            checkEastPositive.setSelected(false);
672
673        }
674        catch (Exception e) {
675            e.printStackTrace();
676        }
677    }
678
679    /**
680     * Special processing for an unknown data type
681     * Can we glean anything about the file by inspecting it more?
682     */
683    private void processGenericFile(File thisFile) {
684
685        clearValues();
686
687        // Set appropriate defaults
688        // Bands
689        bandFiles.add(thisFile.getAbsolutePath());
690        bandNames.add("Flat data");
691
692        // Set the properties in the GUI
693        textDescription.setText(thisFile.getName());
694//      textElements.setText(Integer.toString(elements));
695//      textLines.setText(Integer.toString(lines));
696        textBands.setText("1");
697        
698        radioBinary.setSelected(true);
699        
700        textLatLonScale.setText("1");
701        checkEastPositive.setSelected(false);
702
703    }
704    
705    /**
706     * Clear out any data values presented to the user
707     */
708    private void clearValues() {
709        textDescription.setText("");
710        textElements.setText("");
711        textLines.setText("");
712        textBands.setText("");
713        textUnit.setText("");
714        textStride.setText("");
715        checkTranspose.setSelected(false);
716
717        textLatFile.setText("");
718        textLonFile.setText("");
719        textLatUL.setText("");
720        textLonUL.setText("");
721        textLatLR.setText("");
722        textLonLR.setText("");
723
724        textLatLonScale.setText("");
725        checkEastPositive.setSelected(false);
726
727        textOffset.setText("");
728        textMissing.setText("");
729    }
730    
731    /**
732     * Ask the user for a data file
733     */
734    private File getDataFile(File thisFile) {
735        JFileChooser fileChooser = new JFileChooser(thisFile);
736        fileChooser.setMultiSelectionEnabled(false);
737        int status = fileChooser.showOpenDialog(null);
738        if (status == JFileChooser.APPROVE_OPTION) {
739            thisFile = fileChooser.getSelectedFile();
740        }
741        return(thisFile);
742    }
743    
744    /**
745     * Get the name of the dataset.
746     *
747     * @return descriptive name of the dataset.
748     */
749    public String getDatasetName() {
750        return "Data Set Name";
751    }
752    
753    /**
754     * Get the properties from the datasource
755     *
756     * @param ht  a Hashtable of properties
757     */
758    protected void getDataSourceProperties(Hashtable ht) {
759        super.getDataSourceProperties(ht);
760        ht.put("FLAT.NAME", textDescription.getText());
761        ht.put("FLAT.ELEMENTS", textElements.getText());
762        ht.put("FLAT.LINES", textLines.getText());
763        ht.put("FLAT.BANDNAMES", bandNames);
764        ht.put("FLAT.BANDFILES", bandFiles);
765        ht.put("FLAT.UNIT", textUnit.getText());
766        ht.put("FLAT.STRIDE", textStride.getText());
767        ht.put("FLAT.TRANSPOSE", checkTranspose.isSelected());
768        ht.put("FLAT.MISSING", textMissing.getText());
769        
770        // Navigation
771        if (radioLatLonFiles.isSelected()) {
772            ht.put("NAV.TYPE", "FILES");
773            ht.put("FILE.LAT", latFile.getAbsolutePath());
774            ht.put("FILE.LON", lonFile.getAbsolutePath());
775        }
776        else if (radioLatLonBounds.isSelected()) {
777            ht.put("NAV.TYPE", "BOUNDS");
778            ht.put("BOUNDS.ULLAT", textLatUL.getText());
779            ht.put("BOUNDS.ULLON", textLonUL.getText());
780            ht.put("BOUNDS.LRLAT", textLatLR.getText());
781            ht.put("BOUNDS.LRLON", textLonLR.getText());
782        }
783        else {
784            ht.put("NAV.TYPE", "UNKNOWN");
785        }
786        ht.put("NAV.SCALE", textLatLonScale.getText());
787        ht.put("NAV.EASTPOS", checkEastPositive.isSelected());
788
789        // Data type
790        if (radioBinary.isSelected()) {
791            TwoFacedObject format = (TwoFacedObject) comboByteFormat.getSelectedItem();
792            TwoFacedObject interleave = (TwoFacedObject) comboInterleave.getSelectedItem();
793            ht.put("FORMAT.TYPE", "BINARY");
794            ht.put("BINARY.FORMAT", format.getId());
795            ht.put("BINARY.INTERLEAVE", interleave.getId());
796            ht.put("BINARY.BIGENDIAN", radioEndianBig.isSelected());
797            ht.put("BINARY.OFFSET", textOffset.getText());
798        }
799        else if (radioASCII.isSelected()) {
800            ht.put("FORMAT.TYPE", "ASCII");
801            ht.put("ASCII.DELIMITER", textDelimiter.getText());
802        }
803        else if (radioImage.isSelected()) {
804            ht.put("FORMAT.TYPE", "IMAGE");         
805        }
806        else {
807            ht.put("FORMAT.TYPE", "UNKNOWN");
808        }
809
810    }
811        
812    /**
813     * User said go, we go. Simply get the list of images
814     * from the imageChooser and create the FILE.FLAT
815     * DataSource
816     *
817     */
818    public void doLoadInThread() {
819        String definingObject = dataFileText.getText();
820        String dataType = "FILE.FLAT";
821        
822        Hashtable properties = new Hashtable();
823        getDataSourceProperties(properties);
824        
825        makeDataSource(definingObject, dataType, properties);
826    }
827    
828    /**
829     * The dimensions inner panel
830     */
831    protected JPanel makeDimensionsPanel() {
832        JPanel myPanel = new JPanel();
833        myPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Dimensions"));
834        
835        JLabel elementsLabel = McVGuiUtils.makeLabelRight("Elements:");
836        McVGuiUtils.setComponentWidth(textElements);
837        
838        JLabel linesLabel = McVGuiUtils.makeLabelRight("Lines:");
839        McVGuiUtils.setComponentWidth(textLines);
840        
841        JLabel bandsLabel = McVGuiUtils.makeLabelRight("Bands:");
842        McVGuiUtils.setComponentWidth(textBands);
843
844        JLabel unitLabel = McVGuiUtils.makeLabelRight("Units:");
845        McVGuiUtils.setComponentWidth(textUnit);
846
847        JLabel strideLabel = McVGuiUtils.makeLabelRight("Sampling:");
848        McVGuiUtils.setComponentWidth(textStride);
849
850//      JLabel transposeLabel = McVGuiUtils.makeLabelRight("");
851        
852        GroupLayout layout = new GroupLayout(myPanel);
853        myPanel.setLayout(layout);
854        layout.setHorizontalGroup(
855            layout.createParallelGroup(LEADING)
856            .addGroup(layout.createSequentialGroup()
857                .addContainerGap()
858                .addGroup(layout.createParallelGroup(LEADING)
859                    .addGroup(layout.createSequentialGroup()
860                        .addComponent(elementsLabel)
861                        .addGap(GAP_RELATED)
862                        .addComponent(textElements))
863                    .addGroup(layout.createSequentialGroup()
864                        .addComponent(linesLabel)
865                        .addGap(GAP_RELATED)
866                        .addComponent(textLines))
867                    .addGroup(layout.createSequentialGroup()
868                        .addComponent(bandsLabel)
869                        .addGap(GAP_RELATED)
870                        .addComponent(textBands))
871                    .addGroup(layout.createSequentialGroup()
872                        .addComponent(unitLabel)
873                        .addGap(GAP_RELATED)
874                        .addComponent(textUnit))
875                    .addGroup(layout.createSequentialGroup()
876                        .addComponent(strideLabel)
877                        .addGap(GAP_RELATED)
878                        .addComponent(textStride)))
879                .addContainerGap())
880        );
881        layout.setVerticalGroup(
882            layout.createParallelGroup(LEADING)
883            .addGroup(TRAILING, layout.createSequentialGroup()
884                .addContainerGap()
885                .addGroup(layout.createParallelGroup(BASELINE)
886                    .addComponent(textElements)
887                    .addComponent(elementsLabel))
888                .addPreferredGap(RELATED)
889                .addGroup(layout.createParallelGroup(BASELINE)
890                    .addComponent(textLines)
891                    .addComponent(linesLabel))
892                .addPreferredGap(RELATED)
893                .addGroup(layout.createParallelGroup(BASELINE)
894                    .addComponent(textBands)
895                    .addComponent(bandsLabel))
896                .addPreferredGap(RELATED)
897                .addGroup(layout.createParallelGroup(BASELINE)
898                    .addComponent(textUnit)
899                    .addComponent(unitLabel))
900                .addPreferredGap(RELATED)
901                .addGroup(layout.createParallelGroup(BASELINE)
902                    .addComponent(textStride)
903                    .addComponent(strideLabel))
904                .addContainerGap())
905        );
906        
907        return myPanel;
908    }
909
910    /**
911     * The navigation inner panel
912     */
913    protected JPanel makeNavigationPanel() {
914        JPanel myPanel = new JPanel();
915        myPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Navigation"));
916        
917        McVGuiUtils.setComponentWidth(textLatFile, Width.DOUBLE);
918        McVGuiUtils.setComponentWidth(textLonFile, Width.DOUBLE);
919        panelLatLonFiles = McVGuiUtils.topBottom(
920                GuiUtils.leftRight(McVGuiUtils.makeLabeledComponent("Latitude:",textLatFile), buttonLatFile),
921                GuiUtils.leftRight(McVGuiUtils.makeLabeledComponent("Longitude:",textLonFile), buttonLonFile),
922                Prefer.NEITHER);
923        
924        // Images to make the bounds more clear
925        IconPanel urPanel = new IconPanel("/edu/wisc/ssec/mcidasv/images/upper_right.gif");
926        IconPanel llPanel = new IconPanel("/edu/wisc/ssec/mcidasv/images/lower_left.gif");
927        
928        McVGuiUtils.setComponentWidth(textLatUL);
929        McVGuiUtils.setComponentWidth(textLonUL);
930        McVGuiUtils.setComponentWidth(textLatLR);
931        McVGuiUtils.setComponentWidth(textLonLR);
932        panelLatLonBounds = McVGuiUtils.topBottom(
933                McVGuiUtils.makeLabeledComponent("UL Lat/Lon:", GuiUtils.leftRight(GuiUtils.hbox(textLatUL, textLonUL), urPanel)),
934                McVGuiUtils.makeLabeledComponent("LR Lat/Lon:", GuiUtils.leftRight(llPanel, GuiUtils.hbox(textLatLR, textLonLR))),
935                Prefer.NEITHER);
936        
937        McVGuiUtils.setComponentWidth(radioLatLonFiles);
938        McVGuiUtils.setComponentWidth(radioLatLonBounds);
939        
940        JLabel labelScale = McVGuiUtils.makeLabelRight("Scale:");
941        McVGuiUtils.setComponentWidth(textLatLonScale);
942        
943        JPanel panelScaleEastPositive = GuiUtils.hbox(textLatLonScale, checkEastPositive);
944
945        JLabel labelEastPositive = McVGuiUtils.makeLabelRight("");
946        
947        GroupLayout layout = new GroupLayout(myPanel);
948        myPanel.setLayout(layout);
949        layout.setHorizontalGroup(
950            layout.createParallelGroup(LEADING)
951            .addGroup(layout.createSequentialGroup()
952                .addContainerGap()
953                .addGroup(layout.createParallelGroup(LEADING)
954                    .addGroup(layout.createSequentialGroup()
955                        .addComponent(radioLatLonFiles)
956                        .addGap(GAP_RELATED)
957                        .addComponent(panelLatLonFiles))
958                    .addGroup(layout.createSequentialGroup()
959                        .addComponent(radioLatLonBounds)
960                        .addGap(GAP_RELATED)
961                        .addComponent(panelLatLonBounds))
962                    .addGroup(layout.createSequentialGroup()
963                        .addComponent(labelScale)
964                        .addGap(GAP_RELATED)
965                        .addComponent(panelScaleEastPositive)))
966                .addContainerGap())
967        );
968        layout.setVerticalGroup(
969            layout.createParallelGroup(LEADING)
970            .addGroup(TRAILING, layout.createSequentialGroup()
971                .addContainerGap()
972                .addGroup(layout.createParallelGroup(BASELINE)
973                    .addComponent(radioLatLonFiles)
974                    .addComponent(panelLatLonFiles))
975                .addPreferredGap(RELATED)
976                .addGroup(layout.createParallelGroup(BASELINE)
977                    .addComponent(radioLatLonBounds)
978                    .addComponent(panelLatLonBounds))
979                .addPreferredGap(RELATED)
980                .addGroup(layout.createParallelGroup(BASELINE)
981                    .addComponent(labelScale)
982                    .addComponent(panelScaleEastPositive))
983                .addContainerGap())
984        );
985        
986        return myPanel;
987    }
988
989    /**
990     * The format inner panel
991     */
992    protected JPanel makeFormatPanel() {
993        JPanel myPanel = new JPanel();
994        myPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Format"));
995
996        McVGuiUtils.setComponentWidth(radioBinary);
997        McVGuiUtils.setComponentWidth(radioASCII);
998        McVGuiUtils.setComponentWidth(radioImage);
999        McVGuiUtils.setComponentWidth(radioEndianLittle);
1000        McVGuiUtils.setComponentWidth(radioEndianBig);
1001
1002        McVGuiUtils.setComponentWidth(comboByteFormat, Width.TRIPLE);
1003        McVGuiUtils.setComponentWidth(comboInterleave, Width.DOUBLE);
1004        McVGuiUtils.setComponentWidth(textOffset, Width.HALF);
1005
1006        panelBinary = McVGuiUtils.topBottom(
1007                McVGuiUtils.topBottom(
1008                        McVGuiUtils.makeLabeledComponent("Byte format:", comboByteFormat),
1009                        McVGuiUtils.makeLabeledComponent("Interleave:", comboInterleave),
1010                        Prefer.NEITHER),
1011                McVGuiUtils.topBottom(
1012                        McVGuiUtils.makeLabeledComponent("Endian:", GuiUtils.hbox(radioEndianLittle, radioEndianBig, GAP_RELATED)),
1013                        McVGuiUtils.makeLabeledComponent("Offset:", McVGuiUtils.makeComponentLabeled(textOffset, "bytes")),
1014                        Prefer.NEITHER),
1015                Prefer.NEITHER);
1016        
1017        McVGuiUtils.setComponentWidth(textDelimiter, Width.HALF);
1018        panelASCII = McVGuiUtils.makeLabeledComponent("Delimiter:", textDelimiter);
1019        panelImage = new JPanel();
1020        
1021        JLabel missingLabel = McVGuiUtils.makeLabelRight("Missing value:");
1022        McVGuiUtils.setComponentWidth(textMissing);
1023        JPanel missingPanel = McVGuiUtils.makeComponentLabeled(textMissing, "");
1024
1025        GroupLayout layout = new GroupLayout(myPanel);
1026        myPanel.setLayout(layout);
1027        layout.setHorizontalGroup(
1028            layout.createParallelGroup(LEADING)
1029            .addGroup(layout.createSequentialGroup()
1030                .addContainerGap()
1031                .addGroup(layout.createParallelGroup(LEADING)
1032                    .addGroup(layout.createSequentialGroup()
1033                        .addComponent(radioBinary)
1034                        .addGap(GAP_RELATED)
1035                        .addComponent(panelBinary))
1036                    .addGroup(layout.createSequentialGroup()
1037                        .addComponent(radioASCII)
1038                        .addGap(GAP_RELATED)
1039                        .addComponent(panelASCII))
1040                    .addGroup(layout.createSequentialGroup()
1041                        .addComponent(radioImage)
1042                        .addGap(GAP_RELATED)
1043                        .addComponent(panelImage))
1044                    .addGroup(layout.createSequentialGroup()
1045                        .addComponent(missingLabel)
1046                        .addGap(GAP_RELATED)
1047                        .addComponent(missingPanel)))
1048                .addContainerGap())
1049        );
1050        layout.setVerticalGroup(
1051            layout.createParallelGroup(LEADING)
1052            .addGroup(TRAILING, layout.createSequentialGroup()
1053                .addContainerGap()
1054                .addGroup(layout.createParallelGroup(BASELINE)
1055                    .addComponent(radioBinary)
1056                    .addComponent(panelBinary))
1057                .addPreferredGap(RELATED)
1058                .addGroup(layout.createParallelGroup(BASELINE)
1059                    .addComponent(radioASCII)
1060                    .addComponent(panelASCII))
1061                .addPreferredGap(RELATED)
1062                .addGroup(layout.createParallelGroup(BASELINE)
1063                    .addComponent(radioImage)
1064                    .addComponent(panelImage))
1065                .addPreferredGap(RELATED)
1066                .addGroup(layout.createParallelGroup(BASELINE)
1067                    .addComponent(missingLabel)
1068                    .addComponent(missingPanel))
1069                .addContainerGap())
1070        );
1071
1072        return myPanel;
1073    }
1074
1075    /**
1076     * The main panel properties panel
1077     */
1078    protected JPanel makePropertiesPanel() {
1079        JPanel thisPanel = new JPanel();
1080        JPanel topPanel = McVGuiUtils.sideBySide(makeDimensionsPanel(), makeNavigationPanel());
1081        JPanel bottomPanel = makeFormatPanel();
1082        return McVGuiUtils.topBottom(topPanel, bottomPanel, Prefer.NEITHER);
1083    }
1084    
1085    /**
1086     * @return The gui of this chooser
1087     */
1088    protected JComponent doMakeContents() {
1089
1090        Element chooserNode = getXmlNode();
1091        String path = (String) idv.getPreference(PREF_DEFAULTDIR + getId());
1092        if (path == null) {
1093            path = XmlUtil.getAttribute(chooserNode, "path", (String) null);
1094        }
1095
1096        JPanel myPanel = new JPanel();
1097        
1098        // File
1099        JLabel fileLabel = McVGuiUtils.makeLabelRight("File:");
1100        McVGuiUtils.setComponentWidth(dataFileText, Width.DOUBLEDOUBLE);
1101        
1102        JLabel typeLabel = McVGuiUtils.makeLabelRight("Type:");
1103        McVGuiUtils.setLabelBold(dataFileDescription, true);
1104
1105        JLabel descriptionLabel = McVGuiUtils.makeLabelRight("Description:");
1106        McVGuiUtils.setLabelBold(textDescription, true);
1107
1108        JLabel propertiesLabel = McVGuiUtils.makeLabelRight("Properties:");
1109        JPanel propertiesPanel = makePropertiesPanel();
1110        
1111        JLabel statusLabelLabel = McVGuiUtils.makeLabelRight("");
1112        McVGuiUtils.setLabelPosition(statusLabel, Position.RIGHT);
1113        McVGuiUtils.setComponentColor(statusLabel, TextColor.STATUS);
1114                
1115        JButton helpButton = McVGuiUtils.makeImageButton(ICON_HELP, "Show help");
1116        helpButton.setActionCommand(GuiUtils.CMD_HELP);
1117        helpButton.addActionListener(this);
1118        
1119        McVGuiUtils.setComponentWidth(loadButton, Width.DOUBLE);
1120        
1121        GroupLayout layout = new GroupLayout(myPanel);
1122        myPanel.setLayout(layout);
1123        layout.setHorizontalGroup(
1124            layout.createParallelGroup(LEADING)
1125            .addGroup(layout.createSequentialGroup()
1126                .addContainerGap()
1127                .addGroup(layout.createParallelGroup(LEADING)
1128                    .addGroup(layout.createSequentialGroup()
1129                        .addComponent(fileLabel)
1130                        .addGap(GAP_RELATED)
1131                        .addComponent(dataFileText)
1132                        .addGap(GAP_RELATED)
1133                        .addComponent(dataFileButton))
1134                    .addGroup(layout.createSequentialGroup()
1135                        .addComponent(typeLabel)
1136                        .addGap(GAP_RELATED)
1137                        .addComponent(dataFileDescription))
1138                    .addGroup(layout.createSequentialGroup()
1139                        .addComponent(descriptionLabel)
1140                        .addGap(GAP_RELATED)
1141                        .addComponent(textDescription))
1142                    .addGroup(layout.createSequentialGroup()
1143                        .addComponent(propertiesLabel)
1144                        .addGap(GAP_RELATED)
1145                        .addComponent(propertiesPanel))
1146                    .addGroup(TRAILING, layout.createSequentialGroup()
1147                        .addComponent(helpButton)
1148                        .addPreferredGap(RELATED)
1149                        .addComponent(loadButton))
1150                    .addGroup(layout.createSequentialGroup()
1151                        .addComponent(statusLabelLabel)
1152                        .addGap(GAP_RELATED)
1153                        .addComponent(statusLabel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)))
1154                .addContainerGap())
1155        );
1156        layout.setVerticalGroup(
1157            layout.createParallelGroup(LEADING)
1158            .addGroup(TRAILING, layout.createSequentialGroup()
1159                .addContainerGap()
1160                .addGroup(layout.createParallelGroup(BASELINE)
1161                    .addComponent(dataFileButton)
1162                    .addComponent(dataFileText)
1163                    .addComponent(fileLabel))
1164                .addPreferredGap(RELATED)
1165                .addGroup(layout.createParallelGroup(BASELINE)
1166                    .addComponent(dataFileDescription)
1167                    .addComponent(typeLabel))
1168                .addPreferredGap(RELATED)
1169                .addGroup(layout.createParallelGroup(BASELINE)
1170                    .addComponent(textDescription)
1171                    .addComponent(descriptionLabel))
1172                .addPreferredGap(RELATED)
1173                .addGroup(layout.createParallelGroup(BASELINE)
1174                    .addComponent(propertiesPanel)
1175                    .addComponent(propertiesLabel))
1176                .addPreferredGap(RELATED, DEFAULT_SIZE, Short.MAX_VALUE)
1177                .addGroup(layout.createParallelGroup(BASELINE)
1178                    .addComponent(statusLabelLabel)
1179                    .addComponent(statusLabel))
1180                .addPreferredGap(UNRELATED)
1181                .addGroup(layout.createParallelGroup(BASELINE)
1182                    .addComponent(loadButton)
1183                    .addComponent(helpButton))
1184                .addContainerGap())
1185        );
1186        
1187        return myPanel;
1188        
1189    }
1190
1191}
1192