Projects >> IDV >>9b5f3ae6a354c6835e1fc616dd27a0f50743361d

Chunk
"); theSb.append(""); } } StringBuffer sb = null; if ((sb2d != null) || (sb3d != null)) { sb = new StringBuffer(desc); String resolverUrl = (String) getProperty(PROP_RESOLVERURL); if (resolverUrl != null) { sb.append("

"); sb.append("Resolver URL:" + resolverUrl); } sb.append( "\n

Conflicting content
import java.awt.event.ActionEvent;
<<<<<<< HEAD
/*
 * Copyright 1997-2013 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 * 
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package ucar.unidata.data.grid;


import org.w3c.dom.Document;
import org.w3c.dom.Element;

import ucar.ma2.Array;
import ucar.ma2.InvalidRangeException;
import ucar.ma2.Range;

import ucar.nc2.Attribute;
import ucar.nc2.Group;
import ucar.nc2.Variable;
import ucar.nc2.dataset.CoordinateAxis;
import ucar.nc2.dataset.CoordinateAxis1D;
import ucar.nc2.dataset.CoordinateAxis1DTime;
import ucar.nc2.dataset.NetcdfDataset;
import ucar.nc2.dataset.VariableEnhanced;
import ucar.nc2.dods.DODSNetcdfFile;
import ucar.nc2.dt.GridCoordSystem;
import ucar.nc2.dt.grid.GeoGrid;
import ucar.nc2.dt.grid.GridDataset;
import ucar.nc2.dt.grid.NetcdfCFWriter;
import ucar.nc2.grib.GribVariableRenamer;
import ucar.nc2.time.CalendarDate;
import ucar.nc2.time.CalendarDateRange;
import ucar.nc2.util.NamedAnything;

import ucar.unidata.data.BadDataException;
import ucar.unidata.data.DataCategory;
import ucar.unidata.data.DataChoice;
import ucar.unidata.data.DataManager;
import ucar.unidata.data.DataOperand;
import ucar.unidata.data.DataSelection;
import ucar.unidata.data.DataSourceDescriptor;
import ucar.unidata.data.DataUtil;
import ucar.unidata.data.DerivedDataChoice;
import ucar.unidata.data.DirectDataChoice;
import ucar.unidata.data.GeoLocationInfo;
import ucar.unidata.data.GeoSelection;
import ucar.unidata.geoloc.LatLonPoint;
import ucar.unidata.geoloc.LatLonPointImpl;
import ucar.unidata.geoloc.LatLonRect;
import ucar.unidata.geoloc.ProjectionImpl;
import ucar.unidata.idv.DisplayControl;
import ucar.unidata.idv.IdvConstants;
import ucar.unidata.idv.ui.DataTreeDialog;
import ucar.unidata.ui.TextSearcher;
import ucar.unidata.util.CatalogUtil;
import ucar.unidata.util.FileManager;
import ucar.unidata.util.GuiUtils;
import ucar.unidata.util.IOUtil;
import ucar.unidata.util.JobManager;
import ucar.unidata.util.LogUtil;
import ucar.unidata.util.Misc;
import ucar.unidata.util.StringUtil;
import ucar.unidata.util.ThreeDSize;
import ucar.unidata.util.Trace;
import ucar.unidata.util.TwoFacedObject;
import ucar.unidata.util.WrapperException;
import ucar.unidata.xml.XmlUtil;

import ucar.visad.Util;
import ucar.visad.data.CalendarDateTime;

import visad.Data;
import visad.DateTime;
import visad.FieldImpl;
import visad.Real;
import visad.VisADException;

import visad.georef.EarthLocation;
import visad.georef.EarthLocationTuple;


import java.awt.*;

import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;

import java.rmi.RemoteException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.UUID;

import javax.swing.AbstractAction;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;


/**
 * Handles gridded files
 *
 * @author IDV Development Team
 */

public class GeoGridDataSource extends GridDataSource {


    /** Used to synchronize the geogridadapter */
    protected final Object DOMAIN_SET_MUTEX = new Object();

    /** Throw an error when loading a grid bigger than this in megabytes */
    //private static final int SIZE_THRESHOLD = 500000000;
    //Note that 60 MB is twice the limit of image data from ADDE
    private static final int SIZE_THRESHOLD = 120;

    /** The prefix we hack onto the u and v  variables */
    private static final String PREFIX_GRIDRELATIVE = "GridRelative_";

    /** Preference */
    public static final String PREF_VERTICALCS = IdvConstants.PREF_VERTICALCS;

    /** Preference - warn users for large remote data requests */
    public static final String PREF_LARGE_REMOTE_DATA_WARN =
        IdvConstants.PREF_LARGE_REMOTE_DATA_WARN;

    /** grid size */
    public static final String PROP_GRIDSIZE = "prop.gridsize";

    /** property timesize */
    public static final String PROP_TIMESIZE = "prop.timesize";

    /** property time variable */
    public static final String PROP_TIMEVAR = "timeVariable";

    /** This is used to synchronize geogrid read access */
    protected final Object readLock = new Object();

    /** logging category */
    static ucar.unidata.util.LogUtil.LogCategory log_ =
        ucar.unidata.util.LogUtil.getLogInstance(
            GeoGridDataSource.class.getName());

    /** the dataset */
    private GridDataset dataset;

    /** list of times for this dataset */
    private List myTimes = new ArrayList();

    /** list of levels for this dataset */
    private List myLevels = new ArrayList();

    /** hashtable of coordinate systems and times */
    private Hashtable gcsVsTime = new Hashtable();

    /** Keep track of each data choices times */
    private Hashtable timeMap = new Hashtable();

    /** The first projection we find */
    private ProjectionImpl sampleProjection;

    /** Keep track of the spatial dimensions */
    private String dimensionsLabel;

    /** Keep track of the spatial dimensions */
    private String threeDDimensionsLabel = null;

    /** Keep track of the spatial dimensions */
    private String twoDDimensionsLabel = null;

    /** Keep track of the max grid size */
    private int max3DX;

    /** Keep track of the max grid size */
    private int max3DY;
    /** Keep track of the max grid size */
    private int max3DZ;

    /** Keep track of the max grid size */
    private int max3D;

    /** category attributes */
    private static String[] categoryAttributes = { "GRIB_param_category",
            "Grib2_Parameter_Category" };


    /** Do we really reverse the time indices */
    private boolean reverseTimes = false;

    /** for properties */
    private JCheckBox reverseTimesCheckbox;

    /** for zidv */
    private CalendarDateRange dateRange;

    /** handles the grib variable renaming */
    private static GribVariableRenamer gribRenamer =
        new GribVariableRenamer();


    /**
     * Default constructor
     */
    public GeoGridDataSource() {}


    /**
     * Construct a GeoGridDataSource
     *
     * @param descriptor   the data source descriptor
     * @param gds          The GridDataset
     * @param name         A name
     * @param filename     the filename
     */
    public GeoGridDataSource(DataSourceDescriptor descriptor,
                             GridDataset gds, String name, String filename) {
        super(descriptor, filename, name, (Hashtable) null);
        dataset = gds;
    }


    /**
     * Create a GeoGridDataSource from the GridDataset
     *
     * @param gds  the GridDataset
     */
    public GeoGridDataSource(GridDataset gds) {
        dataset = gds;
    }


    /**
     * Create a GeoGridDataSource from a File.
     *
     * @param descriptor   Describes this data source, has a label etc.
     * @param file         This is the file that points to the actual
     *                     data source.
     * @param properties   General properties used in the base class
     *
     * @throws IOException  problem opening file
     */
    public GeoGridDataSource(DataSourceDescriptor descriptor, File file,
                             Hashtable properties)
            throws IOException {
        this(descriptor, file.getPath(), properties);
    }



    /**
     * Create a GeoGridDataSource from the filename.
     * @param descriptor   Describes this data source, has a label etc.
     * @param filename     This is the filename (or url) that points
     *                     to the actual data source.
     * @param properties   General properties used in the base class
     *
     * @throws IOException
     */
    public GeoGridDataSource(DataSourceDescriptor descriptor,
                             String filename, Hashtable properties)
            throws IOException {
        //Make sure we pass filename up here - as opposed to calling
        //this (new File (filename)) because the new File (filename).getPath () != filename
        super(descriptor, filename, "Geogrid data source", properties);
    }


    /**
     * Create a GeoGridDataSource from the filename.
     * @param descriptor   Describes this data source, has a label etc.
     * @param files List of files or urls
     * @param properties   General properties used in the base class
     *
     * @throws IOException
     */
    public GeoGridDataSource(DataSourceDescriptor descriptor, List files,
                             Hashtable properties)
            throws IOException {

        //Make sure we pass filename up here - as opposed to calling
        //this (new File (filename)) because the new File (filename).getPath () != filename
        super(descriptor, files, "Geogrid data source", properties);
    }


    /**
     * Set the default selection bounds
     *
     * @param rect rectangle
     */
    public void setDefaultSelectionBounds(Rectangle2D rect) {
        getDataSelection().getGeoSelection(true).setLatLonRect(rect);
    }


    /**
     * Load any subset info in field mask xml
     *
     * @param root xml root
     */
    protected void applyFieldMask(Element root) {
        super.applyFieldMask(root);
        GeoSelection geoSubset = getDataSelection().getGeoSelection();
        if (geoSubset == null) {
            geoSubset = new GeoSelection();
            getDataSelection().setGeoSelection(geoSubset);
        }
        Element stride = XmlUtil.getElement(root, "stride");
        if (stride != null) {
            geoSubset.setXStride(XmlUtil.getAttribute(stride, ATTR_X,
                    geoSubset.getXStride()));
            geoSubset.setYStride(XmlUtil.getAttribute(stride, ATTR_Y,
                    geoSubset.getYStride()));
            geoSubset.setZStride(XmlUtil.getAttribute(stride, ATTR_Z,
                    geoSubset.getZStride()));
        }
        Element subset = XmlUtil.getElement(root, "subset");
        if (subset != null) {
            geoSubset.setBoundingBox(
                new GeoLocationInfo(
                    XmlUtil.getAttribute(subset, ATTR_NORTH, 0.0),
                    XmlUtil.getAttribute(subset, ATTR_WEST, 0.0),
                    XmlUtil.getAttribute(subset, ATTR_SOUTH, 0.0),
                    XmlUtil.getAttribute(subset, ATTR_EAST, 0.0)));
        }

    }


    /**
     * Can we mask the data?
     *
     * @return  true if we can
     */
    protected boolean canDoFieldMask() {
        return true;
    }




    /**
     * Write out the field mask file
     *
     * @param doc   doc to write to
     * @param root  root node
     */
    protected void writeFieldMaskFile(Document doc, Element root) {
        GeoSelection geoSubset = getDataSelection().getGeoSelection();
        if (geoSubset != null) {
            Element stride = doc.createElement("stride");
            root.appendChild(stride);
            if (geoSubset.getXStride() > 1) {
                stride.setAttribute(ATTR_X, geoSubset.getXStride() + "");
            }
            if (geoSubset.getYStride() > 1) {
                stride.setAttribute(ATTR_Y, geoSubset.getYStride() + "");
            }
            if (geoSubset.getZStride() > 1) {
                stride.setAttribute(ATTR_Z, geoSubset.getZStride() + "");
            }
            GeoLocationInfo bbox = geoSubset.getBoundingBox();
            if (bbox != null) {
                Element subset = doc.createElement("subset");
                subset.setAttribute(ATTR_NORTH, bbox.getMaxLat() + "");
                subset.setAttribute(ATTR_SOUTH, bbox.getMinLat() + "");
                subset.setAttribute(ATTR_WEST, bbox.getMinLon() + "");
                subset.setAttribute(ATTR_EAST, bbox.getMaxLon() + "");
                root.appendChild(subset);
            }

        }

    }


    /**
     * Initialize if being unpersisted.
     */
    public void initAfterUnpersistence() {
        //Support legacy bundles
        if ( !haveSources() && (oldSourceFromBundles == null)
                && !hasPollingInfo()) {
            oldSourceFromBundles = getName();
        }
        super.initAfterUnpersistence();
        resolvePath();
        //Call getDataset to see if we have a valid file
        getDataset();
    }


    /**
     * Add in the spatial dimensions label
     *
     * @return The subset properties component
     */
    protected JComponent doMakeGeoSubsetPropertiesComponent() {
        JComponent comp = super.doMakeGeoSubsetPropertiesComponent();
        if ((dataset == null) || (dimensionsLabel == null)) {
            return comp;
        }

        JLabel label = new JLabel(dimensionsLabel);
        return GuiUtils.topCenter(GuiUtils.left(GuiUtils.inset(label, 5)),
                                  comp);
    }


    /**
     * Get the extra label that is shown in the geo-subset panel
     *
     * @return Extra label for geosubset panel
     */
    protected JComponent getExtraGeoSelectionComponent() {
        if (dimensionsLabel == null) {
            return super.getExtraGeoSelectionComponent();
        }
        String tmp = dimensionsLabel;
        int    idx = tmp.indexOf("#");
        if (idx > 0) {
            tmp = tmp.substring(0, idx);
        }
        return new JLabel(tmp);
    }


    /**
     * Add any extra tabs into the properties tab
     *
     * @param tabbedPane The properties tab
     */
    public void addPropertiesTabs(JTabbedPane tabbedPane) {
        super.addPropertiesTabs(tabbedPane);
        if (dataset == null) {
            return;
        }
        int height = 300;
        int width  = 400;

        /*
        JTextArea infoText = new JTextArea();
        infoText.setText(dataset.getInfo());
        infoText.setFont(Font.decode("monospaced"));

        JScrollPane infoScroller = GuiUtils.makeScrollPane(infoText, width, height);
        infoScroller.setPreferredSize(new Dimension(width, height));
        infoScroller.setMinimumSize(new Dimension(width, height));
        tabbedPane.add("Info", GuiUtils.inset(infoScroller, 5));
        */


        JTextArea    dumpText = new JTextArea();
        TextSearcher searcher = new TextSearcher(dumpText);
        dumpText.setFont(Font.decode("monospaced"));
        //ByteArrayOutputStream bos = new ByteArrayOutputStream();
        StringWriter bos = new StringWriter();
        try {
            ucar.nc2.NCdumpW.print(dataset.getNetcdfDataset(), "", bos, null);
        } catch (IOException ioe) {
            logException("Dumping netcdf file", ioe);
        }
        dumpText.setText(bos.toString());
        JScrollPane scroller = GuiUtils.makeScrollPane(dumpText, width,
                                   height);
        scroller.setPreferredSize(new Dimension(width, height));
        scroller.setMinimumSize(new Dimension(width, height));
        tabbedPane.add("Metadata",
                       GuiUtils.inset(GuiUtils.centerBottom(scroller,
                           searcher), 5));
    }





    /**
     * Initialize after we have been created.
     */
    public void initAfterCreation() {
        super.initAfterCreation();
        resolvePath();
            return;
        //Call getDataset to see if we have a valid file
        getDataset();
    }


    /**
     * What should be changed by the user when in data relative mode
     *
     * @return paths to changed
     */
    public List getDataPaths() {
        String resolverUrl = (String) getProperty(PROP_RESOLVERURL);
        if ((resolverUrl != null) && (resolverUrl.length() > 0)) {
            return Misc.newList(resolverUrl);
        }
        return super.getDataPaths();
    }

    /**
     * Update the state
     *
     * @param newObject new object
     * @param newProperties  properties
     */
    public void updateState(Object newObject, Hashtable newProperties) {
        removeProperty(PROP_RESOLVERURL);
        super.updateState(newObject, newProperties);
    }


    /**
     * Set what the user has changed
     *
     * @param paths new paths
     */
    public void setTmpPaths(List paths) {
        //TODO: Figure out what to do here
        String resolverUrl = (String) getProperty(PROP_RESOLVERURL);
        oldResolverUrl = resolverUrl;
        if (((paths != null) && (paths.size() > 0)) && (resolverUrl != null)
                && (resolverUrl.length() > 0)) {
            Hashtable properties = getProperties();
            if (properties == null) {
                properties = new Hashtable();
            }
            String firstone = paths.get(0).toString();
            //If we are being saved as a zidv then we remove the resolverurl
            if (firstone
                    .indexOf(ucar.unidata.idv.IdvPersistenceManager
                        .PROP_ZIDVPATH) >= 0) {
                getProperties().remove(PROP_RESOLVERURL);
            } else {
                String resolvedUrl = CatalogUtil.resolveUrl(firstone,
                                         properties);
                if (resolvedUrl != null) {
                    setProperty(PROP_RESOLVERURL, firstone);
                }
            }
        }
        super.setTmpPaths(paths);
    }



    /**
     * Resolve the url if we have to
     */
    protected void resolvePath() {
        //Do we have a resolver
        String resolverUrl = (String) getProperty(PROP_RESOLVERURL);
        if ((resolverUrl != null) && (resolverUrl.length() > 0)) {
            Hashtable properties = getProperties();
            if (properties == null) {
                properties = new Hashtable();
            }
            String resolvedUrl = CatalogUtil.resolveUrl(resolverUrl,
                                     properties);
            if (resolvedUrl == null) {
                setInError(true);
                return;
            }
            //            System.err.println("    got resolved path:" + resolvedUrl);
            sources = Misc.newList(resolvedUrl);
        }
    }


    /**
     * The source has changed
     */
    protected void sourcesChanged() {
        dataset   = null;
        gcsVsTime = new Hashtable();
        super.sourcesChanged();
    }


    /**
     * Clear out the data set
     */
    public void reloadData() {
        myTimes   = null;
        dataset   = null;
        gcsVsTime = new Hashtable();
        resolvePath();
        dataChoices = null;
        //        doMakeDataChoices();
        getDataChoices();
        super.reloadData();

        /**
         *  not sure if we want to do this since we might have
         *  cachedflatfields out there that are pointing at the old
         *  directory
         *  clearFileCache();
         */

    }




    /**
     * Called when Datasource is removed.
     */
    public void doRemove() {
        super.doRemove();
        try {
            if (dataset != null) {
                dataset.close();
            }
        } catch (IOException ioe) {}
        dataset   = null;
        gcsVsTime = null;
    }


    /**
     * Can this DataSource save data to local disk?
     *
     * @return true if this DataSource can save data to local disk?
     */
    public boolean canSaveDataToLocalDisk() {
        if (isFileBased()) {
            //            return false;
        }
        return true;
    }


    /**
     * Get the label for the save data file option
     *
     * @return label
     */
    protected String getSaveDataFileLabel() {
        return (isFileBased()
                ? "Writing grid file"
                : getSaveDataFileLabel());
    }

    /**
     * Make savel local actions
     *
     * @param actions list of actions
     */
    protected void makeSaveLocalActions(List actions) {
        String         lbl = (isFileBased()
                              ? "Subset and Write Grid"
                              : "Write Local Grid");
        AbstractAction a   = new AbstractAction(lbl) {
            public void actionPerformed(ActionEvent ae) {
                Misc.run(new Runnable() {
                    public void run() {
                        try {
                            saveDataToLocalDisk();
                        } catch (Exception exc) {
                            logException("Writing data to local disk", exc);
                        }
                    }
                });
            }
        };
        actions.add(a);
    }




    /**
     * Overwrite this method so we don't show the loading dialog
     *
     * @param msg The msg to show in the dialog
     *
     * @return The jobmanager loadid
     */
    protected Object beginWritingDataToLocalDisk(String msg) {
        final Object loadId = JobManager.getManager().startLoad(msg, false,
                                  false);
        return loadId;
    }



    /**
     * Save the data to local disk.
     *
     *
     * @param prefix  the prefix for the local file name
     * @param loadId  the load id (for cancelling)
     * @param changeLinks true to change the links
     *
     * @return The list of files
     *
     * @throws Exception  problem saving data.
     */
    protected List saveDataToLocalDisk(String prefix, Object loadId,
                                       boolean changeLinks)
            throws Exception {


        List                  choices            = getDataChoices();
        final List checkboxes         = new ArrayList();
        List                  categories         = new ArrayList();
        Hashtable             catMap             = new Hashtable();
        Hashtable             currentDataChoices = new Hashtable();


        List                  displays = getIdv().getDisplayControls();
        for (int i = 0; i < displays.size(); i++) {
            List dataChoices =
                ((DisplayControl) displays.get(i)).getDataChoices();
            if (dataChoices == null) {
                continue;
            }
            List finalOnes = new ArrayList();
            for (int j = 0; j < dataChoices.size(); j++) {
                ((DataChoice) dataChoices.get(j)).getFinalDataChoices(
                    finalOnes);
            }
            for (int dcIdx = 0; dcIdx < finalOnes.size(); dcIdx++) {
                DataChoice dc = (DataChoice) finalOnes.get(dcIdx);
                if ( !(dc instanceof DirectDataChoice)) {
                    continue;
                }
                DirectDataChoice ddc = (DirectDataChoice) dc;
                if (ddc.getDataSource() != this) {
                    continue;
                }
                currentDataChoices.put(ddc.getName(), "");
            }
        }

        if (getDefaultSave()) {
            List varNames = new ArrayList();

            for (int i = 0; i < dataChoices.size(); i++) {
                DataChoice dataChoice = (DataChoice) dataChoices.get(i);
                if ( !(dataChoice instanceof DirectDataChoice)) {
                    continue;
                }
                String name = dataChoice.getName();
                //hack, hack, hack,
                if (name.startsWith(PREFIX_GRIDRELATIVE)) {
                    name = name.substring(PREFIX_GRIDRELATIVE.length());
                }
                if (currentDataChoices.get(name) != null) {
                    varNames.add(name);
                }
            }
            return (currentDataChoices.size() > 0)
                ? writeNc(prefix, changeLinks, varNames)
                : null;
        }


        for (int i = 0; i < dataChoices.size(); i++) {
            DataChoice dataChoice = (DataChoice) dataChoices.get(i);
            if ( !(dataChoice instanceof DirectDataChoice)) {
                continue;
            }
            String label = dataChoice.getDescription();
            if (label.length() > 30) {
                label = label.substring(0, 29) + "...";
            }
            JCheckBox cbx =
                new JCheckBox(label,
                              currentDataChoices.get(dataChoice.getName())
                              != null);
            ThreeDSize size =
                (ThreeDSize) dataChoice.getProperty(PROP_GRIDSIZE);
            cbx.setToolTipText(dataChoice.getName());
            checkboxes.add(cbx);
            DataCategory dc    = dataChoice.getDisplayCategory();
            List         comps = (List) catMap.get(dc);
            if (comps == null) {
                comps = new ArrayList();
                catMap.put(dc, comps);
                categories.add(dc);
            }
            comps.add(cbx);
            comps.add(GuiUtils.filler());
            if (size != null) {
                JLabel sizeLabel = GuiUtils.rLabel(size.getSize() + "  ");
                sizeLabel.setToolTipText(size.getLabel());
                comps.add(sizeLabel);
            } else {
                comps.add(new JLabel(""));
            }
        }
        final JCheckBox allCbx = new JCheckBox("Select All");
        allCbx.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                for (JCheckBox cbx : checkboxes) {
                    cbx.setSelected(allCbx.isSelected());
                }
            }
        });
        List        catComps = new ArrayList();
        JTabbedPane tab      = new JTabbedPane(JTabbedPane.LEFT);

        for (int i = 0; i < categories.size(); i++) {
            List comps = (List) catMap.get(categories.get(i));
            JPanel innerPanel = GuiUtils.doLayout(comps, 3, GuiUtils.WT_NYN,
                                    GuiUtils.WT_N);
            JScrollPane sp = new JScrollPane(GuiUtils.top(innerPanel));
            sp.setPreferredSize(new Dimension(500, 400));
            JPanel top =
                GuiUtils.right(GuiUtils.rLabel("Grid Size (Points)  "));
            JComponent inner = GuiUtils.inset(GuiUtils.topCenter(top, sp), 5);
            tab.addTab(categories.get(i).toString(), inner);
            //            catComps.add();
        }


        //        JComponent contents = GuiUtils.hbox(catComps);
        JComponent contents = tab;
        contents = GuiUtils.topCenter(
            GuiUtils.inset(
                GuiUtils.leftRight(
                    new JLabel("Select the fields to download"),
                    allCbx), 5), contents);
        JLabel label = new JLabel(getNameForDataSource(this, 50, true));
        contents = GuiUtils.topCenter(label, contents);
        contents = GuiUtils.inset(contents, 5);
        if ( !GuiUtils.showOkCancelDialog(null, "", contents, null)) {
            return null;
        }


        List varNames = new ArrayList();
        for (int i = 0; i < dataChoices.size(); i++) {
            DataChoice dataChoice = (DataChoice) dataChoices.get(i);
            if ( !(dataChoice instanceof DirectDataChoice)) {
                continue;
            }
            JCheckBox cbx = (JCheckBox) checkboxes.get(i);
            if ( !cbx.isSelected()) {
                continue;
            }
            String name = dataChoice.getName();
            //hack, hack, hack,
            if (name.startsWith(PREFIX_GRIDRELATIVE)) {
                name = name.substring(PREFIX_GRIDRELATIVE.length());
            }
            varNames.add(name);
        }
        if (varNames.size() == 0) {
            return null;
        }

        return writeNc(prefix, changeLinks, varNames);
    }

    /**
     * Write netCDF file.
     *
     * @param prefix  the prefix for the local file name
     * @param changeLinks true to change the links
     * @param varNames the var names to write
     *
     * @return The list of files
     */
    private List writeNc(String prefix, boolean changeLinks, List varNames) {
        Object       loadId;
        LatLonRect   llr           = null;
        int          hStride       = 1;
        int          zStride       = 1;
        int          timeStride    = 1;
        GeoSelection geoSubset     = getDataSelection().getGeoSelection();
        boolean      includeLatLon = false;

        if (geoSubset != null) {
            if (geoSubset.getBoundingBox() != null) {
                llr = geoSubset.getBoundingBox().getLatLonRect();
            }
            if (geoSubset.hasXStride()) {
                hStride = geoSubset.getXStride();
            }
            if (geoSubset.hasZStride()) {
                zStride = geoSubset.getZStride();
            }
        }

        String         path   = prefix;
        NetcdfCFWriter writer = new NetcdfCFWriter();

        //Start the load, showing the dialog
        loadId = JobManager.getManager().startLoad("Copying data", true,
                true);
        try {
            writer.makeFile(path, dataset, varNames, llr, dateRange,
                            includeLatLon, hStride, zStride, timeStride);
        } catch (Exception exc) {
            logException("Error writing local netcdf file.\nData:"
                         + getFilePath() + "\nVariables:" + varNames, exc);
            return null;
        } finally {
            JobManager.getManager().stopLoad(loadId);
        }


        if (geoSubset != null) {
            geoSubset.clearStride();
            geoSubset.setBoundingBox(null);
            if (geoSelectionPanel != null) {
                geoSelectionPanel.initWith(doMakeGeoSelectionPanel());
            }
        }

        List newFiles = Misc.newList(path);
        if (changeLinks) {
            //Get rid of the resolver URL
            getProperties().remove(PROP_RESOLVERURL);
            setNewFiles(newFiles);
        }
        return newFiles;
    }




    /**
     * Are we a local file
     *
     * @return is a local file
     */
    public boolean isLocalFile() {
        return new File(getFilePath()).exists();
    }


    /**
     *  Overwrite setNewFiles so we clear out the resolverurl
     *
     * @param files The list of new files to use
     */
    public void setNewFiles(List files) {
        getProperties().remove(PROP_RESOLVERURL);
        super.setNewFiles(files);
    }


    /**
     * Get the local directory
     *
     * @param label   a label
     * @param prefix  the prefix
     *
     * @return the path
     */
    protected String getLocalDirectory(String label, String prefix) {
        changeDataPathsCbx.setToolTipText(
            "Should this data source also be changed");
        return FileManager.getWriteFile(FileManager.FILTER_NETCDF, null,
                                        GuiUtils.top(changeDataPathsCbx));
    }


    /**
     * Get the full description of the grid
     *
     * @return  the description
     */
    public String getFullDescription() {
        String       desc        = super.getFullDescription();
        StringBuffer sb2d        = null;
        StringBuffer sb3d        = null;
        List         dataChoices = getDataChoices();
        for (int i = 0; i < dataChoices.size(); i++) {
            DataChoice dataChoice = (DataChoice) dataChoices.get(i);
            if ( !(dataChoice instanceof DirectDataChoice)) {
                continue;
            }
            ThreeDSize size =
                (ThreeDSize) dataChoice.getProperty(PROP_GRIDSIZE);
            Integer timeSize =
                (Integer) dataChoice.getProperty(PROP_TIMESIZE);
            if (size != null) {
                long         total     = size.getSizeY() * size.getSizeX();
                StringBuffer theSb     = null;
                String       sizeEntry = null;
                if (size.getSizeZ() > 1) {
                    if (sb3d == null) {
                        sb3d = new StringBuffer();
                    }
                    total *= size.getSizeZ();
        }
                    theSb = sb3d;
                    sizeEntry = size.getSizeX() + "x" + size.getSizeY() + "x"
                                + size.getSizeZ();
                } else {
                    if (sb2d == null) {
                        sb2d = new StringBuffer();
                    }
                    theSb     = sb2d;
                    sizeEntry = size.getSizeX() + "x" + size.getSizeY();
                }
                theSb.append("
" + dataChoice.getName() + "" return; + dataChoice.getDescription() + "" + sizeEntry + ""); if (timeSize != null) { int times = timeSize.intValue(); if (times > 0) { total *= timeSize.intValue(); theSb.append("" + timeSize); } } theSb.append("" + total + "
\n"); } if (sb2d != null) { sb.append(sb2d); } if (sb3d != null) { sb.append(sb3d); } if (sb != null) { sb.append("
FieldDescriptionDimensions#Times#Points
\n"); } if ((sb != null) && (myLevels != null) && (myLevels.size() > 0)) { sb.append("

Levels

"); for (Object o : myLevels) { sb.append("" + o); sb.append("
"); } } if (sb == null) { return desc; } return sb.toString(); } /** old resolver URL */ private String oldResolverUrl; /** * Reset the tmp state */ public void resetTmpState() { super.resetTmpState(); if (oldResolverUrl != null) { setProperty(PROP_RESOLVERURL, oldResolverUrl); } } /** * Create the dataset from the name of this DataSource. * * @return new GridDataset */ protected GridDataset doMakeDataSet() { checkForInitAfterUnPersistence(); String file = getFilePath(); if (file == null) { if (haveBeenUnPersisted) { file = getName(); } } if (file == null) { return null; } if (sources == null) { sources = new ArrayList(); sources.add(file); } //Make sythetic data ncml file if (sources.size() > 1) { String timeName = getProperty(PROP_TIMEVAR, "time"); StringBuffer sb = new StringBuffer(); if (myDataset == null) { sb.append("\n"); sb.append( "\n"); sb.append("\n"); for (int i = 0; i < sources.size(); i++) { String s = sources.get(i).toString(); try { if (s.startsWith("http") && s.endsWith("entry.das")) { // opendap from ramadda s = DODSNetcdfFile.canonicalURL(s); sb.append(XmlUtil.tag("netcdf", XmlUtil.attrs("location", s, "enhance", "true"), "")); } else { sb.append(XmlUtil.tag("netcdf", XmlUtil.attrs("location", IOUtil.getURL(s, getClass()).toString(), "enhance", "true"), "")); } } catch (IOException ioe) { setInError(true); throw new WrapperException( "Grid data source failed aggregating resource: " + s, ioe); } } sb.append("\n\n"); file = getDataContext().getObjectStore().getUniqueTmpFile( "multigrid_" + UUID.randomUUID().toString(), ".ncml"); try { IOUtil.writeFile(file, sb.toString()); } catch (IOException ioe) { logException("Unable to write file: " + file, ioe); return null; } log_.debug("" + sb); } try { file = convertSourceFile(file); Trace.msg("GeoGridDataSource: opening file " + file); if (file.startsWith("http") && file.endsWith("entry.das")) { // opendap from ramadda file = DODSNetcdfFile.canonicalURL(file); } GridDataset gds = GridDataset.open(file); return gds; } catch (java.io.FileNotFoundException fnfe) { setInError(true); LogUtil.consoleMessage("Original error:\n" + fnfe.toString() + "\n" + LogUtil.getStackTrace(fnfe)); throw new BadDataException("Unable to open grid:\n" + file); } catch (Exception exc) { setInError(true); throw new WrapperException( "Grid data source failed making data set: " + file, exc); } } /** * Return the GridDataset associated with this DataSource. * * @return dataset */ public GridDataset getDataset() { if (dataset == null) { Trace.call1("GeoGridDataSource.getDataSet", " name = " + sources); dataset = doMakeDataSet(); Trace.call2("GeoGridDataSource.getDataSet"); } return dataset; } /** * Return the sample projection * * @return the sample projection */ protected ProjectionImpl getSampleDataProjection() { return sampleProjection; } /** * This method is called at initialization time and * creates a set of {@link ucar.unidata.data.DirectDataChoice}-s * and adds them into the base class managed list of DataChoice-s * with the method addDataChoice. */ protected void doMakeDataChoices() { GridDataset myDataset = getDataset(); max3DX = -1; max3DY = -1; max3DZ = -1; max3D = -1; boolean gridRelativeWind = false; NetcdfDataset ncFile = myDataset.getNetcdfDataset(); Variable windFlag = ncFile.findVariable("ResCompFlag"); if (windFlag != null) { // found it try { Array array = windFlag.read(); gridRelativeWind = !((array.getInt(array.getIndex()) & 1 << 3) == 0); } catch (IOException ioe) { LogUtil.printException(log_, "Couldn't read variable ", ioe); } } Iterator iter = myDataset.getGrids().iterator(); SortedSet uniqueTimes = Collections.synchronizedSortedSet(new TreeSet()); while (iter.hasNext()) { GeoGrid cfield = (GeoGrid) iter.next(); if (sampleProjection == null) { sampleProjection = cfield.getProjection(); // System.err.println ("The sample projection is:" + sampleProjection); } // System.out.println("llr:" + cfield.getProjection().getDefaultMapAreaLL()); GridCoordSystem gcs = cfield.getCoordinateSystem(); CoordinateAxis1D zaxis = gcs.getVerticalAxis(); if ( !isZAxisOk(zaxis)) { continue; } CoordinateAxis1DTime tAxis = gcs.getTimeAxis1D(); List geoTimes = getGeoGridTimes(tAxis); uniqueTimes.addAll(geoTimes); } if ( !uniqueTimes.isEmpty()) { myTimes = new ArrayList(uniqueTimes); } else { myTimes = new ArrayList(); } DataChoice choice; // for each GeoGridImpl in the dataset iter = myDataset.getGrids().iterator(); Hashtable timeToIndex = new Hashtable(); for (int i = 0; i < myTimes.size(); i++) { timeToIndex.put(myTimes.get(i), new Integer(i)); } int cnt = 0; while (iter.hasNext()) { GeoGrid cfield = (GeoGrid) iter.next(); choice = makeDataChoiceFromGeoGrid(cfield, myTimes, timeToIndex); if (choice != null) { cnt++; if (gridRelativeWind == true) { if ((choice.getDescription().indexOf("u-component") >= 0) || (choice.getDescription().indexOf( "v-component") >= 0)) { choice.setName(PREFIX_GRIDRELATIVE + choice.getName()); } } /*else { // check for GRIB definition String canonical = DataAlias.aliasToCanonical(choice.getName()); if (Misc.equals(canonical, "U") || Misc.equals(canonical, "V")) { Attribute vecFlag = cfield.findAttributeIgnoreCase("GRIB_VectorComponentFlag"); if (vecFlag != null) { String vecFlagVal = vecFlag.getStringValue(); if (vecFlagVal.equals("gridRelative")) { choice.setName(PREFIX_GRIDRELATIVE + choice.getName()); } } } } */ addDataChoice(choice); } } //Check if we found any grids if (cnt == 0) { if (LogUtil.getInteractiveMode() && GuiUtils.showOkCancelDialog(null, "No Gridded Data", GuiUtils.inset(new JLabel("No gridded data found for:

  " + this + "

Do you want to try to load this as another data type?"), 5), null)) { getIdv().getDataManager().createDataSourceAndAskForType( getFilePath(), getProperties()); setInError(true, false, ""); } else { //For now just bail out setInError(true, false, ""); } } if (max3D > 0) { threeDDimensionsLabel = "Max grid size: x: " + max3DX + " y: " + max3DY + " z: " + max3DZ + " #points: " + (max3DX * max3DY * max3DZ); } if (threeDDimensionsLabel != null) { dimensionsLabel = threeDDimensionsLabel; } else { dimensionsLabel = twoDDimensionsLabel; } } /** * Get the Data object specified by the particular selection criteria. * * @param dataChoice DataChoice to select. * @param category DataCategory (unused at present) * @param givenDataSelection DataSelection criteria for this request. * @param requestProperties extra request selection properties (not used * in this class) * * @return Data object corresponding to the data choice * * @throws VisADException couldn't create Data object * @throws RemoteException couldn't create remote Data object */ protected Data getDataInner(DataChoice dataChoice, DataCategory category, DataSelection givenDataSelection, Hashtable requestProperties) throws VisADException, RemoteException { // synchronized (readLock) { // System.err.println("getData:" + getFilePath() +" field="+dataChoice); boolean isPR = givenDataSelection.getProperty(DataSelection.PROP_PROGRESSIVERESOLUTION, false); boolean fromBundle = getIdv().getStateManager().getProperty( IdvConstants.PROP_LOADINGXML, false); if(isPR && fromBundle){ // ucar.unidata.geoloc.LatLonPoint[] llp0 = givenDataSelection.getGeoSelection().getRubberBandBoxPoints(); GeoLocationInfo gInfo = givenDataSelection.getGeoSelection().getBoundingBox(); if(gInfo != null) { //GeoLocationInfo gInfo1 = new GeoLocationInfo( // llp0[0].getLatitude(), llp0[0].getLongitude(), // llp0[1].getLatitude(), llp0[1].getLongitude()); givenDataSelection.getGeoSelection().setBoundingBox(gInfo); } } Data data = makeFieldImpl(dataChoice, givenDataSelection, requestProperties); return data; // } } /** * Get the list of parameters * * @return the list */ public List listParameters() { List params = new ArrayList(); for (DataChoice dc : (List) getDataChoices()) { params.add(dc.getName()); } return params; } /** * Get the data for a particular parameter * * @param parameter the parameter name * * @return the Data or null * * * @throws RemoteException Java RMI Error * @throws VisADException VisAD Error */ public Data getData(String parameter) throws VisADException, RemoteException { DataChoice dataChoice = findDataChoice(parameter); if (dataChoice == null) { return null; } DataSelection dataSelection = new DataSelection(); // dataSelection.setTimes(Misc.newList(new Integer(0))); Data data = makeFieldImpl(dataChoice, dataSelection, new Hashtable()); return data; // } } /** * Return the list of DateTime-s associated with this DataSource. * * @return List of DateTime-s. */ protected List doMakeDateTimes() { return myTimes; } /** * Get the list of all levels available from this DataSource * * * @param dataChoice The data choice we are getting levels for * @param dataSelection the data selection * @return List of all available levels */ public List getAllLevels(DataChoice dataChoice, DataSelection dataSelection) { try { dataSelection = DataSelection.merge(dataSelection, getDataSelection()); // System.err.println("levels:" + dataSelection.getFromLevel()); Object fromLevel = dataSelection.getFromLevel(); Object toLevel = dataSelection.getToLevel(); int fromLevelIndex = -1; int toLevelIndex = -1; if ((fromLevel != null) && (toLevel != null)) { long t1 = System.currentTimeMillis(); List allLevels = getAllLevels(dataChoice, new DataSelection(GeoSelection.STRIDE_BASE)); long t2 = System.currentTimeMillis(); // System.err.println("time 1:" + (t2-t1)); fromLevelIndex = indexOf(fromLevel, allLevels); toLevelIndex = indexOf(toLevel, allLevels); } long t1 = System.currentTimeMillis(); GeoGridAdapter geoGridAdapter = makeGeoGridAdapter(dataChoice, dataSelection, null, fromLevelIndex, toLevelIndex, true); long t2 = System.currentTimeMillis(); // System.err.println("time 2:" + (t2-t1)); if (geoGridAdapter != null) { List tmpLevels = geoGridAdapter.getLevels(); myLevels = tmpLevels; return tmpLevels; } return myLevels; } catch (VisADException vae) { throw new ucar.unidata.util.WrapperException(vae); } catch (HugeSizeException hse) { return null; } } /** * We can do geo selection in the properties gui * * @return can do geo selection */ public boolean canDoGeoSelection() { return true; } /** * Can this data source cache its * * @return can cache data to disk */ public boolean canCacheDataToDisk() { return true; } /** * Class description * * @version Enter version here..., Wed, Nov 28, '12 * @author Enter your name here... */ public static class HugeSizeException extends Exception {} /** * Utility to create a new GeoGridAdapter for the given choice and data selection and * level indices * * @param dataChoice The data choice * @param givenDataSelection Data selection * @param requestProperties request properties * @param fromLevelIndex First level index. -1 if it is undefined * @param toLevelIndex Second level index. -1 if it is undefined * @param forMetaData true if we are using this to get metadata instead of * reading data. * * @return The GeoGridAdapter * * * @throws HugeSizeException _more_ * @throws VisADException On badness */ private GeoGridAdapter makeGeoGridAdapter(DataChoice dataChoice, DataSelection givenDataSelection, Hashtable requestProperties, int fromLevelIndex, int toLevelIndex, boolean forMetaData) throws VisADException, HugeSizeException { boolean readingFullGrid = !forMetaData; int numLevels = -1; if ((fromLevelIndex >= 0) && (toLevelIndex >= 0)) { numLevels = Math.abs(toLevelIndex - fromLevelIndex) + 1; } GridDataset myDataset = getDataset(); if (myDataset == null) { return null; } Object extraCacheKey = null; GeoGrid geoGrid = findGridForDataChoice(myDataset, dataChoice); String paramName = dataChoice.getStringId(); if (geoGrid == null) { return null; } ucar.nc2.Dimension ensDim = geoGrid.getEnsembleDimension(); GeoSelection geoSelection = ((givenDataSelection != null) ? givenDataSelection .getGeoSelection() : null); boolean needVolume = ((geoGrid.getCoordinateSystem().getVerticalTransform() != null) && ((requestProperties != null) && (requestProperties.get( DerivedDataChoice.PROP_FROMDERIVED) != null))); // System.out.println("need volume = " + needVolume + " " + geoGrid.getCoordinateSystem().getVerticalTransform()); StringBuffer filename = new StringBuffer("grid_" + paramName); String regionOption = null; regionOption = givenDataSelection.getProperty(DataSelection.PROP_REGIONOPTION, DataSelection.PROP_USEDEFAULTAREA); boolean isProgressiveResolution = givenDataSelection.getProperty( DataSelection.PROP_PROGRESSIVERESOLUTION, false); if(!isProgressiveResolution && dataChoice.getDataSelection() != null){ isProgressiveResolution = dataChoice.getDataSelection().getProperty(DataSelection.PROP_PROGRESSIVERESOLUTION, false); } try { Range ensRange = makeRange(ensDim, null, 1); Range timeRange = null; Range levelRange = null; Range xRange = null; Range yRange = null; if ((fromLevelIndex >= 0) && (toLevelIndex >= 0) && !needVolume) { levelRange = new Range(fromLevelIndex, toLevelIndex); filename.append("_r_" + fromLevelIndex + "_" + toLevelIndex); } /* if(geoSelection != null){ LatLonPoint[] llp0 = geoSelection.getRubberBandBoxPoints(); if(llp0 != null){ if(isReload || (this.haveBeenUnPersisted)) { GeoLocationInfo gInfo = new GeoLocationInfo(llp0[0].getLatitude(), llp0[0].getLongitude(), llp0[1].getLatitude(), llp0[1].getLongitude()); } geoSelection.setBoundingBox(gInfo); } } } */ if (isProgressiveResolution) { int xLenght = geoGrid.getXDimension().getLength(); int yLength = geoGrid.getYDimension().getLength(); if (geoSelection.getLatLonRect() != null) { // spatial subset or usedisplayarea LatLonRect bbox = geoSelection.getLatLonRect(); List yx_ranges = geoGrid.getCoordinateSystem().getRangesFromLatLonRect( bbox); yRange = makeRange(geoGrid.getYDimension(), (Range) yx_ranges.get(0), 1); xRange = makeRange(geoGrid.getXDimension(), (Range) yx_ranges.get(1), 1); yLength = yRange.length(); xLenght = xRange.length(); } Rectangle2D rect = geoSelection.getScreenBound(); int xstride = calculateStrideFactor(xLenght, (int) rect.getWidth()); int ystride = calculateStrideFactor(yLength, (int) rect.getHeight()); if (xstride == 1) { xstride = 0; } if (ystride == 1) { ystride = 0; } geoSelection.setXStride(xstride); geoSelection.setYStride(ystride); } System.out.println("new x y strides: " + geoSelection.getXStride() + " " + geoSelection.getYStride()); if ((geoSelection != null) && (geoSelection.hasSpatialSubset() || geoSelection.getHasNonOneStride())) { //TODO: We should determine the size of the subset grid and use that. //readingFullGrid = false; //System.err.println("subsetting using:" + geoSelection.getLatLonRect()); extraCacheKey = geoSelection; if (levelRange != null) { extraCacheKey = Misc.newList(extraCacheKey, levelRange); } filename.append("_x_" + geoSelection.getXStrideToUse()); filename.append("_y_" + geoSelection.getYStrideToUse()); filename.append("_z_" + geoSelection.getZStrideToUse()); if (geoSelection.getLatLonRect() != null) { LatLonRect bbox = geoSelection.getLatLonRect(); filename.append("_rect_" + cleanBBoxName(bbox)); List yx_ranges = geoGrid.getCoordinateSystem().getRangesFromLatLonRect( bbox); yRange = makeRange(geoGrid.getYDimension(), (Range) yx_ranges.get(0), geoSelection.getYStrideToUse()); xRange = makeRange(geoGrid.getXDimension(), (Range) yx_ranges.get(1), geoSelection.getYStrideToUse()); } else if (geoSelection.getHasNonOneStride()) { yRange = makeRange(geoGrid.getYDimension(), yRange, geoSelection.getYStrideToUse()); xRange = makeRange(geoGrid.getXDimension(), xRange, geoSelection.getYStrideToUse()); } // Z stride is ignored if if ((levelRange != null) && geoSelection.hasZStride() && (geoSelection.getZStrideToUse() > 1)) { levelRange = new Range(fromLevelIndex, toLevelIndex, geoSelection.getZStrideToUse()); } // System.out.println("level range(1): " + levelRange); geoGrid = (GeoGrid) geoGrid.makeSubset(null, ensRange, null, levelRange, yRange, xRange); /* geoGrid = geoGrid.subset(null, levelRange, geoSelection.getLatLonRect(), geoSelection.getZStrideToUse(), geoSelection.getYStrideToUse(), geoSelection.getXStrideToUse()); */ } else if (levelRange != null) { } extraCacheKey = levelRange; // System.out.println("level range(2): " + levelRange); //geoGrid = geoGrid.subset(null, levelRange, null, null); geoGrid = (GeoGrid) geoGrid.makeSubset(null, ensRange, null, levelRange, yRange, xRange); } } catch (InvalidRangeException ire) { throw new IllegalArgumentException("Invalid range:" + ire); } // check to see if user wants to be warned about download size boolean warn = getIdv().getStore().get(PREF_LARGE_REMOTE_DATA_WARN, false); boolean fromBundle = this.haveBeenUnPersisted; // just prior to loading data if ((readingFullGrid) && ( !fromBundle) && (warn)) { // check if interactive, if restoring from a bundle, and if file being loaded is remote if (getIdv().getInteractiveMode() && ( !isLocalFile())) { long total = 1; // get dimensions (note that the time dimension returned does not take into // account subsetting! List dims = geoGrid.getDimensions(); // grab spatial dimension indices. List geoDims = Arrays.asList(geoGrid.getXDimensionIndex(), geoGrid.getYDimensionIndex(), geoGrid.getZDimensionIndex()); for (int d = 0; d < geoDims.size(); d++) { if (geoDims.get(d) != -1) { ucar.nc2.Dimension dim = (ucar.nc2.Dimension) dims.get(geoDims.get(d)); total *= dim.getLength(); } } // check if there is a time dimension, and if so, take into account for number of points if (geoGrid.getTimeDimension() != null) { try { total *= givenDataSelection.getTimes().size(); } catch (NullPointerException npe) { // if use default is selected in field selector for time, then // the getTimes() on the given data source throws and NPE and we // need to go to geoGrid to get the number of times. Note that // that getTimes() on geoGrid does not reflect any temporal subsetting // which is why we check the givenDataSelection first... total *= geoGrid.getTimes().size(); } } // compute size in megabytes of request (minus overhead of network protocol) double mb = (total * geoGrid.getDataType().getSize()); mb = (mb / 1048576.); if (mb > SIZE_THRESHOLD) { JCheckBox askCbx = new JCheckBox("Don't show this again", !warn); JComponent msgContents = GuiUtils .vbox(GuiUtils .inset(new JLabel("You are about to load " + ((int) mb) + " MB of data.
Are you sure you want to do this?


" + "
Consider subsetting for better performance!

"), 5), GuiUtils .inset(askCbx, new Insets(5, 0, 0, 0))); /** * JComponent msgContents = * new JLabel( * "You are about to load " + ((int) mb) * + " MB of data.
Are you sure you want to do this?


" * + "
Consider subsetting for better performance!

"); */ if (askCbx.isSelected()) { getIdv().getStore().put(PREF_LARGE_REMOTE_DATA_WARN, false); } if ( !GuiUtils.askOkCancel( "Large Remote Data Request Warning", msgContents)) { throw new HugeSizeException(); } } } GeoGridAdapter adapter = new GeoGridAdapter(this, geoGrid, dataChoice.getName(), dataset.getNetcdfDataset(), extraCacheKey); adapter.cacheFile = filename.toString(); return adapter; } /** * _more_ * * @param dataPoints _more_ * @param displayPoints _more_ * * @return _more_ */ public int calculateStrideFactor(int dataPoints, int displayPoints) { if (dataPoints <= displayPoints) { return 0; } else { int factor = (int) Math.floor((1.0 * dataPoints) / (1.0 * displayPoints) + 0.8); return factor; } } /** * Make a range for the given parameters * * @param dim The dimension to subset * @param range an existing subset * @param stride the stride * * @return the corresponding Range * * @throws InvalidRangeException not a valid range */ private Range makeRange(ucar.nc2.Dimension dim, Range range, int stride) throws InvalidRangeException { if (dim == null) { return null; } if (range == null) { range = new Range(0, dim.getLength() - 1, stride); } else { range = new Range(range.first(), range.last(), stride); } return range; } /** * Clean up the bounding box name so it can be used in a file name. * change : and + and any other strange chars to _ * * @param bbox bounding box * * @return cleaned up name */ private String cleanBBoxName(LatLonRect bbox) { String name = Util.cleanName(bbox.toString()); name = name.replaceAll(":", "_"); name = name.replaceAll("\\+", "_"); return name; } /** * Find the index of the given object in the list of levels. If its * a Real then check values * * @param o Object * @param levels levels * * @return index * * @throws VisADException On badness */ private int indexOf(Object o, List levels) throws VisADException { if (o instanceof TwoFacedObject) { o = ((TwoFacedObject) o).getId(); } if (o instanceof Integer) { return ((Integer) o).intValue(); } if (o instanceof String) { try { o = ucar.visad.Util.toReal(o.toString()); } catch (Exception ignoreThis) {} if (o instanceof String) { String s = (String) o; if (s.startsWith("#")) { int index = new Integer(s.substring(1).trim()).intValue(); return index; } o = new Real(new Double(s).doubleValue()); } if ((o instanceof Real) && (levels.size() > 0) && (levels.get(0) instanceof Real)) { Real r = (Real) o; for (int i = 0; i < levels.size(); i++) { //TODO: Check if the units are convertible Real lr = (Real) levels.get(i); if (r.getValue(lr.getUnit()) == lr.getValue()) { return i; } } return -1; } return levels.indexOf(o); } /** * Make the FieldImpl corresponding to the DataChoice and * specified DataSelection (times). * * @param dataChoice DataChoice. * @param givenDataSelection specified selection criteria. * @param requestProperties request properties * * @return the grid of data corresponding to the choice * * @throws VisADException couldn't create Data object * @throws RemoteException couldn't create remote Data object */ private FieldImpl makeFieldImpl(DataChoice dataChoice, DataSelection givenDataSelection, Hashtable requestProperties) throws VisADException, RemoteException { long millis = System.currentTimeMillis(); List allLevels = getAllLevels(dataChoice, new DataSelection(GeoSelection.STRIDE_BASE)); Trace.call1("GeoGridDataSource.makeField"); Object fromLevel = givenDataSelection.getFromLevel(); Object toLevel = givenDataSelection.getToLevel(); int fromLevelIndex = -1; int toLevelIndex = -1; if ((fromLevel != null) && (toLevel != null)) { fromLevelIndex = indexOf(fromLevel, allLevels); // System.err.println ("fromLevel index:" + fromLevelIndex); toLevelIndex = indexOf(toLevel, allLevels); if ((toLevelIndex < 0) || (fromLevelIndex < 0)) { System.err.println("Did not find level indices: fromLevel:" + fromLevel + " index:" + fromLevelIndex + " toLevel:" + toLevel + " index:" + toLevelIndex + "\nLevels:" + allLevels); if ((allLevels != null) && !allLevels.isEmpty()) { System.err.println("fromLevel is a " + fromLevel.getClass().getName() + ", toLevel is a " + toLevel.getClass().getName()); System.err.println( "levels are " + allLevels.get(0).getClass().getName()); } } } long starttime = System.currentTimeMillis(); FieldImpl fieldImpl = null; //GridDataset myDataset = getDataset(); //if (myDataset == null) { // return null; //} //GeoGrid geoGrid = findGridForDataChoice(myDataset, dataChoice); //String paramName = dataChoice.getStringId(); Trace.call1("GeoGridDataSource.make GeoGridAdapter"); // System.err.println("levels:" + fromLevelIndex +" " + toLevelIndex); GeoGridAdapter adapter = null; try { adapter = makeGeoGridAdapter(dataChoice, givenDataSelection, requestProperties, fromLevelIndex, toLevelIndex, false); } catch (HugeSizeException hse) { return null; } if (adapter == null) { throw new BadDataException("Could not find field:" + dataChoice.getStringId()); } Trace.call2("GeoGridDataSource.make GeoGridAdapter"); GeoGrid geoGrid = adapter.getGeoGrid(); String paramName = dataChoice.getStringId(); Trace.call1("GeoGridDataSource.make times"); List times = getTimesFromDataSelection(givenDataSelection, dataChoice); // Datasource overrides data selection List members = getEnsembleSelection(); if (members == null) { members = (List) givenDataSelection.getProperty(PROP_ENSEMBLEMEMBERS); } int[] membersIndices = null; if (members != null) { int msize = members.size(); membersIndices = new int[msize]; for (int i = 0; i < msize; i++) { membersIndices[i] = ((Integer) members.get(i)).intValue(); } } int[] timeIndices = null; List allTimes = null; if (times != null) { timeIndices = new int[times.size()]; allTimes = getGeoGridTimes((CoordinateAxis1DTime) geoGrid .getCoordinateSystem().getTimeAxis1D()); int numTimes = allTimes.size(); if (holdsIndices(times)) { for (int i = 0; i < times.size(); i++) { int index = ((Integer) times.get(i)).intValue(); if (getReverseTimes()) { index = numTimes - index - 1; } timeIndices[i] = index; } } else { for (int i = 0; i < times.size(); i++) { int index = allTimes.indexOf(times.get(i)); if (getReverseTimes()) { index = numTimes - index - 1; } timeIndices[i] = index; } } } Trace.call2("GeoGridDataSource.make times"); /* System.err.print("times:"); for(int i=0;i 0)) { fieldImpl = adapter.getSequence(timeIndices, membersIndices, loadId); } else { fieldImpl = adapter.getSequence(timeIndices, loadId); } } if (fieldImpl == null) { // System.err.println ("data selection:" + givenDataSelection); // System.err.println ("data selection times:" + times); // System.err.println ("allTimes:" + allTimes); // Misc.printArray("timeIndices", timeIndices); } boolean useDriverTime = false; if (givenDataSelection != null) { useDriverTime = givenDataSelection.getProperty( DataSelection.PROP_USESTIMEDRIVER, false); } if ((givenDataSelection != null && times.size() > 0)) { CalendarDateTime t0 = new CalendarDateTime((DateTime) times.get(0)); CalendarDate dt0 = t0.getCalendarDate(); CalendarDateTime t1 = new CalendarDateTime((DateTime) times.get(times.size() - 1)); CalendarDate dt1 = t1.getCalendarDate(); dateRange = CalendarDateRange.of(dt0, dt1); } else { dateRange = null; } Trace.call2("GeoGridDataSource.getSequence"); // if made a non-null FlatField, this is displayable as 3D data; // if not, quit. if (fieldImpl == null) { if ( !JobManager.getManager().canContinue(loadId)) { return null; } LogUtil.userMessage(log_, "Unable to load field: " + paramName + " from:" + getFilePath(), true); return null; } JobManager.getManager().stopLoad(loadId); Trace.call2("GeoGridDataSource.makeField"); LogUtil.message(""); log_.debug("Read grid in " + (System.currentTimeMillis() - millis)); return fieldImpl; } // end makeField /** * Find the grid in the dataset from the DataChoice * * @param ds the grid dataset * @param dc the data choice * @return the GeoGrid or null dataset doesn't exist or if variable not found */ public GeoGrid findGridForDataChoice(GridDataset ds, DataChoice dc) { if (ds == null) { return null; } String name = dc.getStringId(); /** * * Look for new name for parameter * If name already exists in dataset, then the old name is returned * */ /** handles general variable renaming */ List newName = gribRenamer.matchNcepNames(ds, name); List userRemappedNames = DataManager.getNewVariableName(name); // check IDV variable renaming first if ((userRemappedNames != null) && !userRemappedNames.isEmpty()) { for (String remappedName : userRemappedNames) { GeoGrid tmpGeoGrid = ds.findGridByName(remappedName); if (tmpGeoGrid != null) { name = remappedName; dc.setId(name); dc.setName(name); return tmpGeoGrid; } } } // ok, match not found in the user remapping...let's try netCDF-Java's map if (newName.size() == 1) { // a unique name has been returned from netCDF-Java - use it! name = newName.get(0); } else if (newName.size() > 0) { // netCDF-Java returned more than one match (no match was found in the // IDV tables)...ask user which one is correct LogUtil.printMessage("Multiple Matches Found for " + name); LogUtil.printMessage("Possible new variable names are:"); List newDescription = new ArrayList(); for (String possibleNewName : newName) { LogUtil.printMessage(" " + possibleNewName); newDescription.add( ds.getDataVariable(possibleNewName).getDescription()); } LogUtil.printMessage("Please update your bundle."); if (getIdv().getInteractiveMode()) { //Misc.printStack("findgrid", 10); String msg1 = "The variable name has changed. Please select a new match.

"; String msg2 = "Possible new names for the variable " + dc.getDescription() + " are:

"; CoordinateAxis1D zaxis = gcs.getVerticalAxis(); String msg3 = StringUtil.join("
", newDescription); String label = "" + msg1 + msg2 + "" + msg3 + ""; List cats = new ArrayList(); for (String possibleNewName : newName) { cats.add(new DataCategory("param:" + possibleNewName, false)); } } DataOperand operand = new DataOperand(name, label, cats, false); DataTreeDialog dataDialog = new DataTreeDialog(getIdv(), null, Misc.newList(operand), Misc.newList(this), Misc.newList(dc), false); List choices = dataDialog.getSelected(); if ((choices != null) && (choices.size() > 0)) { DataChoice dc_new = (DataChoice) ((List) choices.get(0)).get(0); name = dc_new.getStringId(); dc.setId(name); dc.setName(name); GeoGrid geoGrid = ds.findGridByName(name); return geoGrid; } } } else { // ok, no matches found anywhere...return null return null; } dc.setId(name); dc.setName(name); GeoGrid geoGrid = ds.findGridByName(name); return geoGrid; } /** * Utility to check if we should ignore the given z axis * * @param zaxis given z axis * * @return Is ok */ protected boolean isZAxisOk(CoordinateAxis1D zaxis) { return GeoGridAdapter.isZAxisOk(zaxis); } /** * Override the base class method to return the times for the data choice * * @param dataChoice DataChoice in question * @return List of all times for that choice */ public List getAllDateTimes(DataChoice dataChoice) { return (List) timeMap.get(dataChoice.getId()); } /** * Create a DataChoice corresponding to the GeoGrid. This is the * workhorse of the initialization. * * @param cfield GeoGrid * @param allTimes set of times to use * @param timeToIndex a mapping of time to index * * @return corresponding DataChoice */ private DataChoice makeDataChoiceFromGeoGrid(GeoGrid cfield, List allTimes, Hashtable timeToIndex) { GridCoordSystem gcs = cfield.getCoordinateSystem(); LatLonRect llr = gcs.getLatLonBoundingBox(); LatLonPointImpl lleft = llr.getLowerLeftPoint(); LatLonPointImpl uright = llr.getUpperRightPoint(); double centerLat = lleft.getLatitude() + (uright.getLatitude() - lleft.getLatitude()) / 2.0; EarthLocation elt = null; Hashtable ht = new Hashtable(); try { elt = new EarthLocationTuple(centerLat, llr.getCenterLon(), 0); ht.put(IdvConstants.INITIAL_PROBE_EARTHLOCATION, elt); } catch (Exception e) {} //int zIndex = gcs.getZdim(); //int yIndex = gcs.getYdim(); //int xIndex = gcs.getXdim(); CoordinateAxis xaxis = gcs.getXHorizAxis(); CoordinateAxis yaxis = gcs.getYHorizAxis(); // get dimensions of coordinate axes. long sizeZ = 0; if (zaxis != null) { sizeZ = (int) zaxis.getSize(); } Hashtable threeDProps = Misc.newHashtable(DataChoice.PROP_ICON, "/auxdata/ui/icons/3D.gif"); Hashtable twoDProps = Misc.newHashtable(DataChoice.PROP_ICON, "/auxdata/ui/icons/2D.gif"); if (ht != null) { threeDProps.putAll(ht); twoDProps.putAll(ht); } DirectDataChoice choice = null; if (sizeZ < 0) { log_.error(" weird Geogrid -- it claims size Z<0; parm " + cfield.getName()); return null; } else if ( !isZAxisOk(zaxis)) { // do not use grid with "Hybrid", potential temp or // boundary layer vertical axis coordinate } else { // might know how to handle this. String parmName = cfield.getName(); String pseudoName = parmName; String description = cfield.getDescription(); if ((description == null) || description.equals("")) { description = parmName; } CoordinateAxis1DTime tAxis = gcs.getTimeAxis1D(); List geoTimes = getGeoGridTimes(tAxis); timeMap.put(parmName, geoTimes); // List indexList = Misc.getIndexList(geoTimes, allTimes); List indexList = new ArrayList(); if ((geoTimes != null) && (allTimes != null)) { for (int i = 0; i < geoTimes.size(); i++) { Integer timeIndex = (Integer) timeToIndex.get(geoTimes.get(i)); indexList.add(timeIndex); } } // none or only one level, we'll call it a 2D grid DataSelection dataSelection = DataSelection.NULL; if (false && !indexList.isEmpty()) { dataSelection = new DataSelection(indexList, DataSelection.TIMESMODE_USETHIS); } List categories = null; Hashtable props = null; if ((sizeZ == 0) || (sizeZ == 1)) { //if (sizeZ == 0) { int xLength = cfield.getXDimension().getLength(); int yLength = cfield.getYDimension().getLength(); ucar.nc2.Dimension ensDim = cfield.getEnsembleDimension(); if (twoDDimensionsLabel == null) { twoDDimensionsLabel = "Total grid size: x: " + xLength + " y: " + yLength + " #points: " + (xLength * yLength); } props = new Hashtable(twoDProps); props.put(PROP_GRIDSIZE, new ThreeDSize(xLength, yLength)); if (geoTimes != null) { props.put(PROP_TIMESIZE, new Integer(geoTimes.size())); } if ((ensDim != null) && (ensDim.getLength() > 1)) { List ensMembers = null; CoordinateAxis1D eAxis = gcs.getEnsembleAxis(); int numEns = ensDim.getLength(); if ((ensMembers == null) && (eAxis != null)) { ensMembers = eAxis.getNames(); } int[] ids = new int[numEns]; String[] enames = new String[numEns]; for (int i = 0; i < numEns; i++) { ids[i] = i; NamedAnything na = (NamedAnything) ensMembers.get(i); /** if (isNumeric(na.toString())) { enames[i] = "Member " + na.toString(); } else { enames[i] = na.toString(); } } List ensSet = TwoFacedObject.createList(ids, enames); props.put(PROP_ENSEMBLEMEMBERS, ensSet); if ((ensDim != null) && (ensDim.getLength() > 1)) { categories = (tAxis == null) ? getTwoDCategories() : getTwoDEnsTimeSeriesCategories(); } else { categories = (tAxis == null) ? getTwoDCategories() : getTwoDTimeSeriesCategories(); } /* choice = new DirectDataChoice(this, parmName, pseudoName, description, (taxis == null) ? getTwoDCategories() : getTwoDTimeSeriesCategories(), dataSelection, props); */ } else { // if (sizeZ > 1) // Have 3D field (we expect); usually sizeZ > 1: int xLength = cfield.getXDimension().getLength(); int yLength = cfield.getYDimension().getLength(); int zLength = cfield.getZDimension().getLength(); ucar.nc2.Dimension ensDim = cfield.getEnsembleDimension(); if (xLength * yLength * zLength > max3D) { max3D = xLength * yLength * zLength; max3DX = xLength; max3DY = yLength; max3DZ = zLength; } ThreeDSize size = new ThreeDSize(xLength, yLength, zLength); props = new Hashtable(threeDProps); props.put(PROP_GRIDSIZE, size); if (geoTimes != null) { props.put(PROP_TIMESIZE, new Integer(geoTimes.size())); } if ((ensDim != null) && (ensDim.getLength() > 1)) { List ensMembers = null; CoordinateAxis1D eAxis = gcs.getEnsembleAxis(); int numEns = ensDim.getLength(); if ((ensMembers == null) && (eAxis != null)) { ensMembers = eAxis.getNames(); } int[] ids = new int[numEns]; String[] enames = new String[numEns]; for (int i = 0; i < numEns; i++) { ids[i] = i; NamedAnything na = (NamedAnything) ensMembers.get(i); enames[i] = na.toString(); } List ensSet = TwoFacedObject.createList(ids, enames); props.put(PROP_ENSEMBLEMEMBERS, ensSet); } /* choice = new DirectDataChoice(this, parmName, pseudoName, description, (tAxis == null) ? getThreeDCategories() : getThreeDTimeSeriesCategories(), dataSelection, props); */ if ((ensDim != null) && (ensDim.getLength() > 1)) { categories = (tAxis == null) ? getThreeDCategories() : getThreeDEnsTimeSeriesCategories(); } else { categories = (tAxis == null) ? getThreeDCategories() : getThreeDTimeSeriesCategories(); } } // see if we have any categorization Attribute attr = null; for (int i = 0; (i < categoryAttributes.length) && (attr == null); i++) { attr = cfield.findAttributeIgnoreCase(categoryAttributes[i]); } if (attr != null) { String append = attr.getStringValue(); if (append != null) { append = append.replaceAll(DataCategory.DIVIDER, "_"); } } DataCategory cat = (DataCategory) categories.get(0); cat = cat.copyAndAppend(append); List newCategories = new ArrayList(); newCategories.add(cat); for (int i = 1; i < categories.size(); i++) { newCategories.add(categories.get(i)); } categories = newCategories; } // see if we have any categorization Group group = null; VariableEnhanced variable = cfield.getVariable(); if (variable != null) { group = variable.getParentGroup(); if ((group != null) && !group.equals("")) { String append = group.getName(); if (append != null) { append = append.replaceAll("/", ""); append = append.replaceAll(DataCategory.DIVIDER, "_"); } DataCategory cat = (DataCategory) categories.get(0); cat = cat.copyAndAppend(append); List newCategories = new ArrayList(); newCategories.add(cat); for (int i = 1; i < categories.size(); i++) { newCategories.add(categories.get(i)); } categories = newCategories; } } choice = new DirectDataChoice(this, parmName, pseudoName, description, categories, dataSelection, props); } return choice; } /** * check if a input is only numeric number * * @param str is this numeric * * @return true if numeric */ public static boolean isNumeric(String str) { try { double d = Double.parseDouble(str); } catch (NumberFormatException nfe) { return false; } return true; } /** * make a list of DateTime-s from a GeoGrid timeAxis * * @param timeAxis - GeoGrid time CoordinateAxis * @return corresponding List of DateTime-s. */ private List getGeoGridTimes(CoordinateAxis1DTime timeAxis) { if ((timeAxis == null) || (timeAxis.getSize() == 0)) { return new ArrayList(0); } List times = (List) gcsVsTime.get(timeAxis); if (times != null) { return times; } try { times = DataUtil.makeDateTimes(timeAxis); gcsVsTime.put(timeAxis, times); } catch (Exception e) { System.out.println("getGeoGridTimes() " + e); } return times; } /** for test */ private static boolean forceSubset = false; /** for test */ public static boolean testMode = false; /* public void putCache(Object key, Object value) { if(testMode) return; super.putCache(key,value); }*/ /** * Test this class by running * "java ucar.unidata.data.grid.GeoGridDataSource " * * @param args filename * * @throws Exception some error occurred */ public static void main(String[] args) throws Exception { /* if (true) { int j=0; // int []bufferSizes = {100,250,500,750,1000,5000,8092}; int []bufferSizes = {500,500,500,500}; for(int i=0;i<100;i++) { // for(j=0;j<2;j++) { ucar.grib.grib2.Grib2BitMapSection.SKIPIT = (j==0); for(String arg: args) { // ucar.unidata.io.RandomAccessFile.BUFFERSIZE = bufferSizes[i]; ucar.unidata.io.RandomAccessFile.BUFFERSIZE = 500; GridDataset gds = GridDataset.open(arg); gds.close(); File gbxFile = new File(arg+".gbx"); gbxFile.delete(); } // } } return; } */ // dead dataset... String leadUrl = "dods://lead.unidata.ucar.edu:8080/thredds/dodsC/model/NCEP/NAM/CONUS_80km/NAM_CONUS_80km_20071002_1200.grib1"; // dead dataset... String mlodeUrl = "dods://motherlode.ucar.edu:8080/thredds/dodsC/model/NCEP/NAM/CONUS_80km/NAM_CONUS_80km_20071002_1200.grib1"; String url = ((args.length == 0) ? leadUrl : mlodeUrl); String[] urls = { url }; testMode = true; for (int i = 0; i < 10000; i++) { for (int urlIdx = 0; urlIdx < urls.length; urlIdx++) { System.err.println("Reading data:" + i + " " + urls[urlIdx]); GeoGridDataSource ggds = new GeoGridDataSource(null, urls[urlIdx], null); ggds.doMakeDataChoices(); DataChoice dataChoice = ggds.findDataChoice("Temperature"); if (dataChoice == null) { dataChoice = ggds.findDataChoice("T"); } // System.err.println ("" + dataChoice.getProperties()); ggds.makeFieldImpl(dataChoice, ggds.getDataSelection(), null); } } /* GridDataset dataset = GridDataset.open("elev.nc"); GeoGrid geoGrid = findGridForDataChoice(dataset, "foo"); GeoGrid geoGrid50 = geoGrid.subset(null, null, null, 0, 50, 50); GeoGrid geoGrid100 = geoGrid.subset(null, null, null, 0, 100, 100); System.exit(0); */ /** * * testMode = true; * forceSubset = false; * LogUtil.setTestMode(true); * LogUtil.startOutputBuffer(); * CacheManager.setDoCache(false); * * if (args.length == 0) { * System.out.println("Must supply a file name"); * System.exit(1); * } * boolean verbose = false; * StringBuffer titleBuffer = new StringBuffer(); * StringBuffer errors = new StringBuffer(); * StringBuffer buffer = new StringBuffer(); * boolean doAll = false; * boolean nextOneTitle = false; * List nots = new ArrayList(); * for (int i = 0; i < args.length; i++) { * if (nextOneTitle) { * titleBuffer.append(args[i] + "
"); * nextOneTitle = false; * continue; * } * * if (args[i].startsWith("-not:")) { * nots.add(args[i].substring(5)); * } * * if (args[i].equals("-verbose")) { * verbose = true; * continue; * } * if (args[i].equals("-title")) { * nextOneTitle = true; * continue; * } * if (args[i].equals("-doall")) { * doAll = true; * continue; * } * if (args[i].equals("-subset")) { * forceSubset = true; * buffer.append("Doing subsetting

\n"); * continue; * } * if (args[i].equals("-nosubset")) { * forceSubset = false; * buffer.append("Not doing subsetting

\n"); * continue; * } * boolean shouldProcess = true; * for (int notIdx = 0; notIdx < nots.size(); notIdx++) { * if (args[i].indexOf(nots.get(notIdx).toString()) >= 0) { * shouldProcess = false; * } * } * if ( !shouldProcess) { * continue; * } * * String name = "file" + i; * boolean fileOk = true; * StringBuffer fileBuffer = new StringBuffer(); * fileBuffer.append("


\nFile: " * + args[i] * + "
\n"); * DataChoice dataChoice = null; * GeoGridDataSource ggds = null; * try { * ggds = new GeoGridDataSource(null, args[i], null); * } catch (Throwable exc) { * errors.append("" + args[i] * + "
"); * fileBuffer.append("Failed to open file:"); * fileBuffer.append("
");
         *       fileBuffer.append(LogUtil.getStackTrace(exc));
         *       fileBuffer.append("
"); * fileBuffer.append("
\n"); * buffer.append(fileBuffer.toString()); * continue; * } * List dataChoices = ggds.getDataChoices(); * if (dataChoices.size() == 0) { * errors.append("" + args[i] * + "
"); * fileBuffer.append("No data choices\n"); * fileBuffer.append("
\n"); * buffer.append(fileBuffer.toString()); * continue; * } * StringBuffer okBuffer = new StringBuffer(); * StringBuffer notokBuffer = new StringBuffer(); * boolean fieldOk = true; * LogUtil.println(args[i]); * for (int dcIdx = 0; fieldOk && (dcIdx < dataChoices.size()); * dcIdx++) { * if ( !doAll && (dcIdx > 0)) { * break; * } * // LogUtil.println("\tLoop:" + dcIdx); * * dataChoice = (DataChoice) dataChoices.get(dcIdx); * DataSelection dataSelection = ggds.getDataSelection(); * Data testData = null; * try { * testData = ggds.makeFieldImpl(dataChoice, dataSelection); * } catch (Throwable exc) { * fileOk = false; * fieldOk = false; * notokBuffer.append("Exception reading field:" * + dataChoice + ""); * notokBuffer.append(preText(LogUtil.getStackTrace(exc))); * } * String s = LogUtil.getOutputBuffer(true).trim(); * if (s.length() > 0) { * boolean showError = true; * if ( !verbose) { * List lines = StringUtil.split(s, "\n", true, true); * if (lines.size() == 1) { * if (s.indexOf("Unable to load field") >= 0) { * showError = false; * } * if (s.indexOf("Unknown unit") >= 0) { * showError = false; * } * } * } * * if (showError) { * fileOk = false; * notokBuffer.append("Reading: " * + dataChoice.getId() + "" * + preText(s)); * } * } else { * okBuffer.append(dataChoice.getId() + " "); * } * if (notokBuffer.indexOf("Exception") >= 0) { * notokBuffer.append("*** Stopping here ***"); * break; * } * * } * if ( !fileOk) { * errors.append("" + args[i] * + "
"); * } * if (okBuffer.toString().length() > 0) { * // fileBuffer.append("OK: " + okBuffer.toString() + "

"); * } * if (notokBuffer.toString().length() > 0) { * fileBuffer.append(notokBuffer.toString()); * } * fileBuffer.append("

\n"); * if ((notokBuffer.toString().length() > 0) || !fileOk) { * buffer.append(fileBuffer.toString()); * } * } * LogUtil.stopOutputBuffer(); * System.out.println("

Geogrid test

\n"); * System.out.println(titleBuffer.toString()); * if (errors.toString().length() > 0) { * System.out.println("Errors:
"); * System.out.println(errors.toString()); * } * * System.out.println(buffer.toString()); * * System.exit(0); */ } /** * for test * * @param s string to format * * @return formatted string */ private static String preText(String s) { s = StringUtil.replace(s, "<", "<"); s = StringUtil.replace(s, ">", ">"); s = StringUtil.replace(s, "\n", "
\n"); s = StringUtil.replace(s, "\t", " "); s = StringUtil.replace(s, " ", " "); return "
" + s + "
"; } /** * Set the FileNameOrUrl property. * * @param value The new value for FileNameOrUrl */ public void setFileNameOrUrl(String value) { oldSourceFromBundles = value; } * Apply the properties * * @return everything ok */ public boolean applyProperties() { if ( !super.applyProperties()) { return false; } if (reverseTimesCheckbox != null) { reverseTimes = reverseTimesCheckbox.isSelected(); } return true; /** * Add the reverse times checkbox * * @return extra comp */ protected JComponent getExtraTimesComponent() { reverseTimesCheckbox = new JCheckBox("Reverse Times", reverseTimes); reverseTimesCheckbox.setToolTipText( "If you have selected the first time then really use the last time"); return GuiUtils.right(reverseTimesCheckbox); } /** * Set the ReverseTimes property. * * @param value The new value for ReverseTimes */ public void setReverseTimes(boolean value) { reverseTimes = value; } /** * Get the ReverseTimes property. * * @return The ReverseTimes */ public boolean getReverseTimes() { return reverseTimes; } } ======= /* * Copyright 1997-2012 Unidata Program Center/University Corporation for * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307, * support@unidata.ucar.edu. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or (at * your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package ucar.unidata.data.grid; import java.awt.Dimension; import java.awt.Font; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.Rectangle2D; import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import java.util.UUID; import javax.swing.AbstractAction; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTextArea; import org.w3c.dom.Document; import org.w3c.dom.Element; import ucar.ma2.Array; import ucar.ma2.InvalidRangeException; import ucar.ma2.Range; import ucar.nc2.Attribute; import ucar.nc2.Group; import ucar.nc2.Variable; import ucar.nc2.dataset.CoordinateAxis; import ucar.nc2.dataset.CoordinateAxis1D; import ucar.nc2.dataset.CoordinateAxis1DTime; import ucar.nc2.dataset.NetcdfDataset; import ucar.nc2.dataset.VariableEnhanced; import ucar.nc2.dods.DODSNetcdfFile; import ucar.nc2.dt.GridCoordSystem; import ucar.nc2.dt.grid.GeoGrid; import ucar.nc2.dt.grid.GridDataset; import ucar.nc2.dt.grid.NetcdfCFWriter; import ucar.nc2.grib.GribVariableRenamer; import ucar.nc2.time.CalendarDate; import ucar.nc2.time.CalendarDateRange; import ucar.nc2.util.NamedAnything; import ucar.unidata.data.BadDataException; import ucar.unidata.data.DataCategory; import ucar.unidata.data.DataChoice; import ucar.unidata.data.DataManager; import ucar.unidata.data.DataOperand; import ucar.unidata.data.DataSelection; import ucar.unidata.data.DataSourceDescriptor; import ucar.unidata.data.DataUtil; import ucar.unidata.data.DerivedDataChoice; import ucar.unidata.data.DirectDataChoice; import ucar.unidata.data.GeoLocationInfo; import ucar.unidata.data.GeoSelection; import ucar.unidata.geoloc.LatLonPointImpl; import ucar.unidata.geoloc.LatLonRect; import ucar.unidata.geoloc.ProjectionImpl; import ucar.unidata.idv.DisplayControl; import ucar.unidata.idv.IdvConstants; import ucar.unidata.idv.ui.DataTreeDialog; import ucar.unidata.ui.TextSearcher; import ucar.unidata.util.CatalogUtil; import ucar.unidata.util.FileManager; import ucar.unidata.util.GuiUtils; import ucar.unidata.util.IOUtil; import ucar.unidata.util.JobManager; import ucar.unidata.util.LogUtil; import ucar.unidata.util.Misc; import ucar.unidata.util.StringUtil; import ucar.unidata.util.ThreeDSize; import ucar.unidata.util.Trace; import ucar.unidata.util.TwoFacedObject; import ucar.unidata.util.WrapperException; import ucar.unidata.xml.XmlUtil; import ucar.visad.Util; import ucar.visad.data.CalendarDateTime; import visad.Data; import visad.DateTime; import visad.FieldImpl; import visad.Real; import visad.VisADException; import visad.georef.EarthLocation; import visad.georef.EarthLocationTuple; /** * Handles gridded files * * @author IDV Development Team */ public class GeoGridDataSource extends GridDataSource { /** Used to synchronize the geogridadapter */ protected final Object DOMAIN_SET_MUTEX = new Object(); /** Throw an error when loading a grid bigger than this in megabytes */ //private static final int SIZE_THRESHOLD = 500000000; //Note that 60 MB is twice the limit of image data from ADDE private static final int SIZE_THRESHOLD = 120; /** The prefix we hack onto the u and v variables */ private static final String PREFIX_GRIDRELATIVE = "GridRelative_"; /** Preference */ public static final String PREF_VERTICALCS = IdvConstants.PREF_VERTICALCS; /** Preference - warn users for large remote data requests */ public static final String PREF_LARGE_REMOTE_DATA_WARN = IdvConstants.PREF_LARGE_REMOTE_DATA_WARN; /** grid size */ public static final String PROP_GRIDSIZE = "prop.gridsize"; /** property timesize */ public static final String PROP_TIMESIZE = "prop.timesize"; /** property time variable */ public static final String PROP_TIMEVAR = "timeVariable"; /** This is used to synchronize geogrid read access */ protected final Object readLock = new Object(); /** logging category */ } static ucar.unidata.util.LogUtil.LogCategory log_ = ucar.unidata.util.LogUtil.getLogInstance( GeoGridDataSource.class.getName()); /** the dataset */ private GridDataset dataset; /** list of times for this dataset */ private List myTimes = new ArrayList(); /** list of levels for this dataset */ private List myLevels = new ArrayList(); /** hashtable of coordinate systems and times */ private Hashtable gcsVsTime = new Hashtable(); /** Keep track of each data choices times */ private Hashtable timeMap = new Hashtable(); /** The first projection we find */ private ProjectionImpl sampleProjection; /** Keep track of the spatial dimensions */ private String dimensionsLabel; /** Keep track of the spatial dimensions */ private String threeDDimensionsLabel = null; /** Keep track of the spatial dimensions */ private String twoDDimensionsLabel = null; /** Keep track of the max grid size */ private int max3DX; /** Keep track of the max grid size */ private int max3DY; /** Keep track of the max grid size */ private int max3DZ; /** Keep track of the max grid size */ private int max3D; /** category attributes */ private static String[] categoryAttributes = { "GRIB_param_category", "Grib2_Parameter_Category" }; /** Do we really reverse the time indices */ private boolean reverseTimes = false; /** for properties */ private JCheckBox reverseTimesCheckbox; /** for zidv */ private CalendarDateRange dateRange; /** handles the grib variable renaming */ private static GribVariableRenamer gribRenamer = new GribVariableRenamer(); /** * Default constructor */ public GeoGridDataSource() {} /** * Construct a GeoGridDataSource * * @param descriptor the data source descriptor * @param gds The GridDataset * @param name A name * @param filename the filename */ public GeoGridDataSource(DataSourceDescriptor descriptor, GridDataset gds, String name, String filename) { super(descriptor, filename, name, (Hashtable) null); dataset = gds; } /** * Create a GeoGridDataSource from the GridDataset * * @param gds the GridDataset */ public GeoGridDataSource(GridDataset gds) { dataset = gds; } /** * Create a GeoGridDataSource from a File. * * @param descriptor Describes this data source, has a label etc. * @param file This is the file that points to the actual * data source. * @param properties General properties used in the base class * * @throws IOException problem opening file */ public GeoGridDataSource(DataSourceDescriptor descriptor, File file, Hashtable properties) throws IOException { this(descriptor, file.getPath(), properties); /** * Create a GeoGridDataSource from the filename. * @param descriptor Describes this data source, has a label etc. * @param filename This is the filename (or url) that points * to the actual data source. * @param properties General properties used in the base class * * @throws IOException */ public GeoGridDataSource(DataSourceDescriptor descriptor, String filename, Hashtable properties) throws IOException { //Make sure we pass filename up here - as opposed to calling //this (new File (filename)) because the new File (filename).getPath () != filename super(descriptor, filename, "Geogrid data source", properties); } /** * Create a GeoGridDataSource from the filename. * @param descriptor Describes this data source, has a label etc. * @param files List of files or urls * @param properties General properties used in the base class * * @throws IOException */ public GeoGridDataSource(DataSourceDescriptor descriptor, List files, Hashtable properties) throws IOException { //Make sure we pass filename up here - as opposed to calling //this (new File (filename)) because the new File (filename).getPath () != filename super(descriptor, files, "Geogrid data source", properties); } /** * Set the default selection bounds * * @param rect rectangle */ public void setDefaultSelectionBounds(Rectangle2D.Float rect) { getDataSelection().getGeoSelection(true).setLatLonRect(rect); } /** * Load any subset info in field mask xml * * @param root xml root */ protected void applyFieldMask(Element root) { super.applyFieldMask(root); GeoSelection geoSubset = getDataSelection().getGeoSelection(); if (geoSubset == null) { geoSubset = new GeoSelection(); getDataSelection().setGeoSelection(geoSubset); } Element stride = XmlUtil.getElement(root, "stride"); if (stride != null) { geoSubset.setXStride(XmlUtil.getAttribute(stride, ATTR_X, geoSubset.getXStride())); geoSubset.setYStride(XmlUtil.getAttribute(stride, ATTR_Y, geoSubset.getYStride())); geoSubset.setZStride(XmlUtil.getAttribute(stride, ATTR_Z, geoSubset.getZStride())); } Element subset = XmlUtil.getElement(root, "subset"); if (subset != null) { geoSubset.setBoundingBox( new GeoLocationInfo( XmlUtil.getAttribute(subset, ATTR_NORTH, 0.0), XmlUtil.getAttribute(subset, ATTR_WEST, 0.0), XmlUtil.getAttribute(subset, ATTR_SOUTH, 0.0), XmlUtil.getAttribute(subset, ATTR_EAST, 0.0))); } } /** * Can we mask the data? * * @return true if we can */ protected boolean canDoFieldMask() { return true; } /** * Write out the field mask file * * @param doc doc to write to * @param root root node */ protected void writeFieldMaskFile(Document doc, Element root) { GeoSelection geoSubset = getDataSelection().getGeoSelection(); if (geoSubset != null) { Element stride = doc.createElement("stride"); root.appendChild(stride); if (geoSubset.getXStride() > 1) { stride.setAttribute(ATTR_X, geoSubset.getXStride() + ""); } if (geoSubset.getYStride() > 1) { stride.setAttribute(ATTR_Y, geoSubset.getYStride() + ""); } if (geoSubset.getZStride() > 1) { stride.setAttribute(ATTR_Z, geoSubset.getZStride() + ""); } GeoLocationInfo bbox = geoSubset.getBoundingBox(); if (bbox != null) { Element subset = doc.createElement("subset"); subset.setAttribute(ATTR_NORTH, bbox.getMaxLat() + ""); subset.setAttribute(ATTR_SOUTH, bbox.getMinLat() + ""); subset.setAttribute(ATTR_WEST, bbox.getMinLon() + ""); subset.setAttribute(ATTR_EAST, bbox.getMaxLon() + ""); root.appendChild(subset); } } } /** * Initialize if being unpersisted. */ public void initAfterUnpersistence() { //Support legacy bundles if ( !haveSources() && (oldSourceFromBundles == null) && !hasPollingInfo()) { oldSourceFromBundles = getName(); } super.initAfterUnpersistence(); resolvePath(); //Call getDataset to see if we have a valid file getDataset(); } /** * Add in the spatial dimensions label * * @return The subset properties component */ protected JComponent doMakeGeoSubsetPropertiesComponent() { JComponent comp = super.doMakeGeoSubsetPropertiesComponent(); if ((dataset == null) || (dimensionsLabel == null)) { return comp; } JLabel label = new JLabel(dimensionsLabel); return GuiUtils.topCenter(GuiUtils.left(GuiUtils.inset(label, 5)), comp); } /** * Get the extra label that is shown in the geo-subset panel * * @return Extra label for geosubset panel */ protected JComponent getExtraGeoSelectionComponent() { if (dimensionsLabel == null) { return super.getExtraGeoSelectionComponent(); } String tmp = dimensionsLabel; int idx = tmp.indexOf("#"); if (idx > 0) { tmp = tmp.substring(0, idx); } return new JLabel(tmp); } /** * Add any extra tabs into the properties tab * * @param tabbedPane The properties tab */ public void addPropertiesTabs(JTabbedPane tabbedPane) { super.addPropertiesTabs(tabbedPane); if (dataset == null) { return; } int height = 300; int width = 400; /* JTextArea infoText = new JTextArea(); infoText.setText(dataset.getInfo()); infoText.setFont(Font.decode("monospaced")); JScrollPane infoScroller = GuiUtils.makeScrollPane(infoText, width, height); infoScroller.setPreferredSize(new Dimension(width, height)); infoScroller.setMinimumSize(new Dimension(width, height)); tabbedPane.add("Info", GuiUtils.inset(infoScroller, 5)); */ JTextArea dumpText = new JTextArea(); TextSearcher searcher = new TextSearcher(dumpText); dumpText.setFont(Font.decode("monospaced")); //ByteArrayOutputStream bos = new ByteArrayOutputStream(); StringWriter bos = new StringWriter(); try { ucar.nc2.NCdumpW.print(dataset.getNetcdfDataset(), "", bos, null); } catch (IOException ioe) { logException("Dumping netcdf file", ioe); } dumpText.setText(bos.toString()); JScrollPane scroller = GuiUtils.makeScrollPane(dumpText, width, height); scroller.setPreferredSize(new Dimension(width, height)); scroller.setMinimumSize(new Dimension(width, height)); tabbedPane.add("Metadata", GuiUtils.inset(GuiUtils.centerBottom(scroller, searcher), 5)); } /** * Initialize after we have been created. */ public void initAfterCreation() { super.initAfterCreation(); resolvePath(); //Call getDataset to see if we have a valid file getDataset(); } /** * What should be changed by the user when in data relative mode * * @return paths to changed */ public List getDataPaths() { String resolverUrl = (String) getProperty(PROP_RESOLVERURL); if ((resolverUrl != null) && (resolverUrl.length() > 0)) { return Misc.newList(resolverUrl); } return super.getDataPaths(); } /** * Update the state * * @param newObject new object * @param newProperties properties */ public void updateState(Object newObject, Hashtable newProperties) { removeProperty(PROP_RESOLVERURL); super.updateState(newObject, newProperties); } /** * Set what the user has changed * * @param paths new paths */ public void setTmpPaths(List paths) { //TODO: Figure out what to do here String resolverUrl = (String) getProperty(PROP_RESOLVERURL); oldResolverUrl = resolverUrl; if (((paths != null) && (paths.size() > 0)) && (resolverUrl != null) && (resolverUrl.length() > 0)) { Hashtable properties = getProperties(); if (properties == null) { properties = new Hashtable(); } String firstone = paths.get(0).toString(); //If we are being saved as a zidv then we remove the resolverurl if (firstone .indexOf(ucar.unidata.idv.IdvPersistenceManager .PROP_ZIDVPATH) >= 0) { getProperties().remove(PROP_RESOLVERURL); } else { String resolvedUrl = CatalogUtil.resolveUrl(firstone, properties); if (resolvedUrl != null) { setProperty(PROP_RESOLVERURL, firstone); } } } super.setTmpPaths(paths); } /** * Resolve the url if we have to */ protected void resolvePath() { //Do we have a resolver } String resolverUrl = (String) getProperty(PROP_RESOLVERURL); if ((resolverUrl != null) && (resolverUrl.length() > 0)) { Hashtable properties = getProperties(); if (properties == null) { properties = new Hashtable(); } String resolvedUrl = CatalogUtil.resolveUrl(resolverUrl, properties); if (resolvedUrl == null) { setInError(true); return; } // System.err.println(" got resolved path:" + resolvedUrl); sources = Misc.newList(resolvedUrl); } } /** * The source has changed */ protected void sourcesChanged() { dataset = null; gcsVsTime = new Hashtable(); super.sourcesChanged(); } /** * Clear out the data set */ public void reloadData() { myTimes = null; dataset = null; gcsVsTime = new Hashtable(); resolvePath(); dataChoices = null; // doMakeDataChoices(); getDataChoices(); super.reloadData(); /** * not sure if we want to do this since we might have * cachedflatfields out there that are pointing at the old * directory * clearFileCache(); */ } /** * Called when Datasource is removed. */ public void doRemove() { super.doRemove(); try { if (dataset != null) { dataset.close(); } } catch (IOException ioe) {} dataset = null; gcsVsTime = null; } /** * Can this DataSource save data to local disk? * * @return true if this DataSource can save data to local disk? */ public boolean canSaveDataToLocalDisk() { if (isFileBased()) { // return false; } return true; } /** * Get the label for the save data file option * * @return label */ protected String getSaveDataFileLabel() { return (isFileBased() ? "Writing grid file" : getSaveDataFileLabel()); } /** * Make savel local actions * * @param actions list of actions */ protected void makeSaveLocalActions(List actions) { String lbl = (isFileBased() ? "Subset and Write Grid" : "Write Local Grid"); AbstractAction a = new AbstractAction(lbl) { public void actionPerformed(ActionEvent ae) { Misc.run(new Runnable() { public void run() { try { saveDataToLocalDisk(); } catch (Exception exc) { logException("Writing data to local disk", exc); } }); } }; actions.add(a); } /** * Overwrite this method so we don't show the loading dialog * * @param msg The msg to show in the dialog * * @return The jobmanager loadid */ protected Object beginWritingDataToLocalDisk(String msg) { final Object loadId = JobManager.getManager().startLoad(msg, false, false); return loadId; } /** * Save the data to local disk. * * * @param prefix the prefix for the local file name * @param loadId the load id (for cancelling) * @param changeLinks true to change the links * * @return The list of files * * @throws Exception problem saving data. */ protected List saveDataToLocalDisk(String prefix, Object loadId, boolean changeLinks) throws Exception { List choices = getDataChoices(); final List checkboxes = new ArrayList(); List categories = new ArrayList(); Hashtable catMap = new Hashtable(); Hashtable currentDataChoices = new Hashtable(); List displays = getIdv().getDisplayControls(); for (int i = 0; i < displays.size(); i++) { List dataChoices = ((DisplayControl) displays.get(i)).getDataChoices(); if (dataChoices == null) { continue; } List finalOnes = new ArrayList(); for (int j = 0; j < dataChoices.size(); j++) { ((DataChoice) dataChoices.get(j)).getFinalDataChoices( finalOnes); } for (int dcIdx = 0; dcIdx < finalOnes.size(); dcIdx++) { DataChoice dc = (DataChoice) finalOnes.get(dcIdx); if ( !(dc instanceof DirectDataChoice)) { continue; } DirectDataChoice ddc = (DirectDataChoice) dc; if (ddc.getDataSource() != this) { continue; } currentDataChoices.put(ddc.getName(), ""); } } if (getDefaultSave()) { List varNames = new ArrayList(); for (int i = 0; i < dataChoices.size(); i++) { DataChoice dataChoice = (DataChoice) dataChoices.get(i); if ( !(dataChoice instanceof DirectDataChoice)) { continue; } String name = dataChoice.getName(); //hack, hack, hack, if (name.startsWith(PREFIX_GRIDRELATIVE)) { name = name.substring(PREFIX_GRIDRELATIVE.length()); } if (currentDataChoices.get(name) != null) { varNames.add(name); } } return (currentDataChoices.size() > 0) ? writeNc(prefix, changeLinks, varNames) : null; } for (int i = 0; i < dataChoices.size(); i++) { DataChoice dataChoice = (DataChoice) dataChoices.get(i); if ( !(dataChoice instanceof DirectDataChoice)) { continue; } String label = dataChoice.getDescription(); if (label.length() > 30) { label = label.substring(0, 29) + "..."; } JCheckBox cbx = new JCheckBox(label, currentDataChoices.get(dataChoice.getName()) != null); ThreeDSize size = (ThreeDSize) dataChoice.getProperty(PROP_GRIDSIZE); cbx.setToolTipText(dataChoice.getName()); checkboxes.add(cbx); DataCategory dc = dataChoice.getDisplayCategory(); List comps = (List) catMap.get(dc); if (comps == null) { comps = new ArrayList(); catMap.put(dc, comps); categories.add(dc); } comps.add(cbx); comps.add(GuiUtils.filler()); if (size != null) { JLabel sizeLabel = GuiUtils.rLabel(size.getSize() + " "); sizeLabel.setToolTipText(size.getLabel()); comps.add(sizeLabel); } else { comps.add(new JLabel("")); } } final JCheckBox allCbx = new JCheckBox("Select All"); allCbx.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { for (JCheckBox cbx : checkboxes) { cbx.setSelected(allCbx.isSelected()); } } }); List catComps = new ArrayList(); JTabbedPane tab = new JTabbedPane(JTabbedPane.LEFT); for (int i = 0; i < categories.size(); i++) { List comps = (List) catMap.get(categories.get(i)); JPanel innerPanel = GuiUtils.doLayout(comps, 3, GuiUtils.WT_NYN, GuiUtils.WT_N); JScrollPane sp = new JScrollPane(GuiUtils.top(innerPanel)); sp.setPreferredSize(new Dimension(500, 400)); JPanel top = GuiUtils.right(GuiUtils.rLabel("Grid Size (Points) ")); JComponent inner = GuiUtils.inset(GuiUtils.topCenter(top, sp), 5); tab.addTab(categories.get(i).toString(), inner); // catComps.add(); } // JComponent contents = GuiUtils.hbox(catComps); JComponent contents = tab; contents = GuiUtils.topCenter( GuiUtils.inset( GuiUtils.leftRight( new JLabel("Select the fields to download"), allCbx), 5), contents); JLabel label = new JLabel(getNameForDataSource(this, 50, true)); contents = GuiUtils.topCenter(label, contents); contents = GuiUtils.inset(contents, 5); if ( !GuiUtils.showOkCancelDialog(null, "", contents, null)) { return null; } List varNames = new ArrayList(); for (int i = 0; i < dataChoices.size(); i++) { DataChoice dataChoice = (DataChoice) dataChoices.get(i); if ( !(dataChoice instanceof DirectDataChoice)) { continue; } JCheckBox cbx = (JCheckBox) checkboxes.get(i); if ( !cbx.isSelected()) { continue; } String name = dataChoice.getName(); //hack, hack, hack, if (name.startsWith(PREFIX_GRIDRELATIVE)) { name = name.substring(PREFIX_GRIDRELATIVE.length()); } varNames.add(name); } if (varNames.size() == 0) { return null; } return writeNc(prefix, changeLinks, varNames); } /** * Write netCDF file. * * @param prefix the prefix for the local file name * @param changeLinks true to change the links * @param varNames the var names to write * * @return The list of files */ private List writeNc(String prefix, boolean changeLinks, List varNames) { Object loadId; LatLonRect llr = null; int hStride = 1; int zStride = 1; int timeStride = 1; GeoSelection geoSubset = getDataSelection().getGeoSelection(); boolean includeLatLon = false; if (geoSubset != null) { if (geoSubset.getBoundingBox() != null) { llr = geoSubset.getBoundingBox().getLatLonRect(); } if (geoSubset.hasXStride()) { hStride = geoSubset.getXStride(); } if (geoSubset.hasZStride()) { zStride = geoSubset.getZStride(); } } String path = prefix; NetcdfCFWriter writer = new NetcdfCFWriter(); //Start the load, showing the dialog loadId = JobManager.getManager().startLoad("Copying data", true, true); try { writer.makeFile(path, dataset, varNames, llr, dateRange, includeLatLon, hStride, zStride, timeStride); } catch (Exception exc) { logException("Error writing local netcdf file.\nData:" + getFilePath() + "\nVariables:" + varNames, exc); return null; } finally { JobManager.getManager().stopLoad(loadId); } if (geoSubset != null) { geoSubset.clearStride(); geoSubset.setBoundingBox(null); if (geoSelectionPanel != null) { geoSelectionPanel.initWith(doMakeGeoSelectionPanel()); } } List newFiles = Misc.newList(path); if (changeLinks) { //Get rid of the resolver URL getProperties().remove(PROP_RESOLVERURL); setNewFiles(newFiles); } return newFiles; } /** * Are we a local file * * @return is a local file */ public boolean isLocalFile() { return new File(getFilePath()).exists(); } /** * Overwrite setNewFiles so we clear out the resolverurl * * @param files The list of new files to use */ public void setNewFiles(List files) { getProperties().remove(PROP_RESOLVERURL); super.setNewFiles(files); } /** * Get the local directory * * @param label a label * @param prefix the prefix * * @return the path */ protected String getLocalDirectory(String label, String prefix) { changeDataPathsCbx.setToolTipText( "Should this data source also be changed"); return FileManager.getWriteFile(FileManager.FILTER_NETCDF, null, GuiUtils.top(changeDataPathsCbx)); } /** * Get the full description of the grid * * @return the description */ public String getFullDescription() { String desc = super.getFullDescription(); StringBuffer sb2d = null; StringBuffer sb3d = null; List dataChoices = getDataChoices(); for (int i = 0; i < dataChoices.size(); i++) { DataChoice dataChoice = (DataChoice) dataChoices.get(i); if ( !(dataChoice instanceof DirectDataChoice)) { continue; } ThreeDSize size = (ThreeDSize) dataChoice.getProperty(PROP_GRIDSIZE); Integer timeSize = (Integer) dataChoice.getProperty(PROP_TIMESIZE); if (size != null) { long total = size.getSizeY() * size.getSizeX(); StringBuffer theSb = null; String sizeEntry = null; if (size.getSizeZ() > 1) { if (sb3d == null) { sb3d = new StringBuffer(); } total *= size.getSizeZ(); theSb = sb3d; sizeEntry = size.getSizeX() + "x" + size.getSizeY() + "x" + size.getSizeZ(); } else { if (sb2d == null) { sb2d = new StringBuffer(); } theSb = sb2d; sizeEntry = size.getSizeX() + "x" + size.getSizeY(); } theSb.append("" + dataChoice.getName() + "" + dataChoice.getDescription() + "" + sizeEntry + ""); if (timeSize != null) { int times = timeSize.intValue(); if (times > 0) { total *= timeSize.intValue(); theSb.append("" + timeSize); } } theSb.append(""); theSb.append("" + total + ""); } } StringBuffer sb = null; if ((sb2d != null) || (sb3d != null)) { sb = new StringBuffer(desc); String resolverUrl = (String) getProperty(PROP_RESOLVERURL); if (resolverUrl != null) { sb.append("

"); sb.append("Resolver URL:" + resolverUrl); } sb.append( "\n

\n"); } if (sb2d != null) { sb.append(sb2d); } if (sb3d != null) { sb.append(sb3d); } if (sb != null) { sb.append("
FieldDescriptionDimensions#Times#Points
\n"); } if ((sb != null) && (myLevels != null) && (myLevels.size() > 0)) { sb.append("

Levels

"); for (Object o : myLevels) { sb.append("" + o); sb.append("
"); } } if (sb == null) { return desc; } return sb.toString(); } /** old resolver URL */ private String oldResolverUrl; /** * Reset the tmp state */ public void resetTmpState() { super.resetTmpState(); if (oldResolverUrl != null) { setProperty(PROP_RESOLVERURL, oldResolverUrl); } } /** * Create the dataset from the name of this DataSource. * * @return new GridDataset */ protected GridDataset doMakeDataSet() { checkForInitAfterUnPersistence(); String file = getFilePath(); if (file == null) { if (haveBeenUnPersisted) { file = getName(); } } if (file == null) { return null; } if (sources == null) { sources = new ArrayList(); sources.add(file); } //Make sythetic data ncml file if (sources.size() > 1) { String timeName = getProperty(PROP_TIMEVAR, "time"); StringBuffer sb = new StringBuffer(); sb.append("\n"); sb.append( "\n"); sb.append("\n"); for (int i = 0; i < sources.size(); i++) { String s = sources.get(i).toString(); try { if (s.startsWith("http") && s.endsWith("entry.das")) { // opendap from ramadda s = DODSNetcdfFile.canonicalURL(s); sb.append(XmlUtil.tag("netcdf", XmlUtil.attrs("location", s, "enhance", "true"), "")); } else { sb.append(XmlUtil.tag("netcdf", XmlUtil.attrs("location", IOUtil.getURL(s, getClass()).toString(), "enhance", "true"), "")); } } catch (IOException ioe) { setInError(true); throw new WrapperException( "Grid data source failed aggregating resource: " + s, ioe); } } sb.append("\n\n"); file = getDataContext().getObjectStore().getUniqueTmpFile( "multigrid_" + UUID.randomUUID().toString(), ".ncml"); try { IOUtil.writeFile(file, sb.toString()); } catch (IOException ioe) { logException("Unable to write file: " + file, ioe); return null; } log_.debug("" + sb); } try { file = convertSourceFile(file); Trace.msg("GeoGridDataSource: opening file " + file); if (file.startsWith("http") && file.endsWith("entry.das")) { // opendap from ramadda file = DODSNetcdfFile.canonicalURL(file); } GridDataset gds = GridDataset.open(file); return gds; } catch (java.io.FileNotFoundException fnfe) { setInError(true); LogUtil.consoleMessage("Original error:\n" + fnfe.toString() + "\n" + LogUtil.getStackTrace(fnfe)); throw new BadDataException("Unable to open grid:\n" + file); } catch (Exception exc) { setInError(true); throw new WrapperException( "Grid data source failed making data set: " + file, exc); } } */ /** * Return the GridDataset associated with this DataSource. * * @return dataset */ public GridDataset getDataset() { if (dataset == null) { Trace.call1("GeoGridDataSource.getDataSet", " name = " + sources); dataset = doMakeDataSet(); Trace.call2("GeoGridDataSource.getDataSet"); } return dataset; } /** * Return the sample projection * * @return the sample projection */ protected ProjectionImpl getSampleDataProjection() { return sampleProjection; } /** * This method is called at initialization time and * creates a set of {@link ucar.unidata.data.DirectDataChoice}-s * and adds them into the base class managed list of DataChoice-s * with the method addDataChoice. */ protected void doMakeDataChoices() { GridDataset myDataset = getDataset(); if (myDataset == null) { return; } max3DX = -1; max3DY = -1; max3DZ = -1; max3D = -1; boolean gridRelativeWind = false; NetcdfDataset ncFile = myDataset.getNetcdfDataset(); Variable windFlag = ncFile.findVariable("ResCompFlag"); if (windFlag != null) { // found it try { Array array = windFlag.read(); gridRelativeWind = !((array.getInt(array.getIndex()) & 1 << 3) == 0); } catch (IOException ioe) { LogUtil.printException(log_, "Couldn't read variable ", ioe); } } Iterator iter = myDataset.getGrids().iterator(); SortedSet uniqueTimes = Collections.synchronizedSortedSet(new TreeSet()); while (iter.hasNext()) { GeoGrid cfield = (GeoGrid) iter.next(); if (sampleProjection == null) { sampleProjection = cfield.getProjection(); // System.err.println ("The sample projection is:" + sampleProjection); } // System.out.println("llr:" + cfield.getProjection().getDefaultMapAreaLL()); GridCoordSystem gcs = cfield.getCoordinateSystem(); CoordinateAxis1D zaxis = gcs.getVerticalAxis(); if ( !isZAxisOk(zaxis)) { continue; } CoordinateAxis1DTime tAxis = gcs.getTimeAxis1D(); List geoTimes = getGeoGridTimes(tAxis); uniqueTimes.addAll(geoTimes); } if ( !uniqueTimes.isEmpty()) { myTimes = new ArrayList(uniqueTimes); } else { myTimes = new ArrayList(); } DataChoice choice; // for each GeoGridImpl in the dataset iter = myDataset.getGrids().iterator(); Hashtable timeToIndex = new Hashtable(); for (int i = 0; i < myTimes.size(); i++) { timeToIndex.put(myTimes.get(i), new Integer(i)); } int cnt = 0; while (iter.hasNext()) { GeoGrid cfield = (GeoGrid) iter.next(); choice = makeDataChoiceFromGeoGrid(cfield, myTimes, timeToIndex); if (choice != null) { cnt++; if (gridRelativeWind == true) { if ((choice.getDescription().indexOf("u-component") >= 0) || (choice.getDescription().indexOf( "v-component") >= 0)) { choice.setName(PREFIX_GRIDRELATIVE + choice.getName()); } } /*else { // check for GRIB definition String canonical = DataAlias.aliasToCanonical(choice.getName()); if (Misc.equals(canonical, "U") || Misc.equals(canonical, "V")) { Attribute vecFlag = cfield.findAttributeIgnoreCase("GRIB_VectorComponentFlag"); if (vecFlag != null) { String vecFlagVal = vecFlag.getStringValue(); if (vecFlagVal.equals("gridRelative")) { choice.setName(PREFIX_GRIDRELATIVE + choice.getName()); } } } } */ addDataChoice(choice); } } //Check if we found any grids if (cnt == 0) { if (LogUtil.getInteractiveMode() && GuiUtils.showOkCancelDialog(null, "No Gridded Data", GuiUtils.inset(new JLabel("No gridded data found for:

  " + this + "

Do you want to try to load this as another data type?"), 5), null)) { getIdv().getDataManager().createDataSourceAndAskForType( getFilePath(), getProperties()); setInError(true, false, ""); } else { //For now just bail out setInError(true, false, ""); } return; } if (max3D > 0) { threeDDimensionsLabel = "Max grid size: x: " + max3DX + " y: " + max3DY + " z: " + max3DZ + " #points: " + (max3DX * max3DY * max3DZ); } if (threeDDimensionsLabel != null) { dimensionsLabel = threeDDimensionsLabel; } else { dimensionsLabel = twoDDimensionsLabel; } } /** * Get the Data object specified by the particular selection criteria. * * @param dataChoice DataChoice to select. * @param category DataCategory (unused at present) * @param givenDataSelection DataSelection criteria for this request. * @param requestProperties extra request selection properties (not used * in this class) * * @return Data object corresponding to the data choice * * @throws VisADException couldn't create Data object * @throws RemoteException couldn't create remote Data object */ protected Data getDataInner(DataChoice dataChoice, DataCategory category, DataSelection givenDataSelection, Hashtable requestProperties) throws VisADException, RemoteException { // synchronized (readLock) { // System.err.println("getData:" + getFilePath() +" field="+dataChoice); Data data = makeFieldImpl(dataChoice, givenDataSelection, requestProperties); return data; // } } /** } * Get the list of parameters * * @return the list */ public List listParameters() { List params = new ArrayList(); for (DataChoice dc : (List) getDataChoices()) { params.add(dc.getName()); } return params; } /** * Get the data for a particular parameter * } * @param parameter the parameter name * * @return the Data or null * * @throws RemoteException Java RMI Error * @throws VisADException VisAD Error */ public Data getData(String parameter) throws VisADException, RemoteException { DataChoice dataChoice = findDataChoice(parameter); if (dataChoice == null) { return null; } DataSelection dataSelection = new DataSelection(); // dataSelection.setTimes(Misc.newList(new Integer(0))); Data data = makeFieldImpl(dataChoice, dataSelection, new Hashtable()); return data; // } } /** * Return the list of DateTime-s associated with this DataSource. * * @return List of DateTime-s. */ protected List doMakeDateTimes() { return myTimes; } /** * Get the list of all levels available from this DataSource * * * @param dataChoice The data choice we are getting levels for * @param dataSelection the data selection * @return List of all available levels */ public List getAllLevels(DataChoice dataChoice, DataSelection dataSelection) { try { dataSelection = DataSelection.merge(dataSelection, getDataSelection()); // System.err.println("levels:" + dataSelection.getFromLevel()); Object fromLevel = dataSelection.getFromLevel(); Object toLevel = dataSelection.getToLevel(); int fromLevelIndex = -1; int toLevelIndex = -1; if ((fromLevel != null) && (toLevel != null)) { long t1 = System.currentTimeMillis(); List allLevels = getAllLevels(dataChoice, new DataSelection(GeoSelection.STRIDE_BASE)); long t2 = System.currentTimeMillis(); // System.err.println("time 1:" + (t2-t1)); fromLevelIndex = indexOf(fromLevel, allLevels); toLevelIndex = indexOf(toLevel, allLevels); } long t1 = System.currentTimeMillis(); GeoGridAdapter geoGridAdapter = makeGeoGridAdapter(dataChoice, dataSelection, null, fromLevelIndex, toLevelIndex, true); long t2 = System.currentTimeMillis(); // System.err.println("time 2:" + (t2-t1)); if (geoGridAdapter != null) { List tmpLevels = geoGridAdapter.getLevels(); myLevels = tmpLevels; return tmpLevels; } return myLevels; } catch (VisADException vae) { throw new ucar.unidata.util.WrapperException(vae); } catch (HugeSizeException hse) { return null; } } /** * We can do geo selection in the properties gui * * @return can do geo selection */ public boolean canDoGeoSelection() { return true; } /** * Can this data source cache its * * @return can cache data to disk */ public boolean canCacheDataToDisk() { return true; /** * Class description * * * @version Enter version here..., Wed, Nov 28, '12 * @author Enter your name here... */ public static class HugeSizeException extends Exception {} /** * Utility to create a new GeoGridAdapter for the given choice and data selection and * level indices * * @param dataChoice The data choice * @param givenDataSelection Data selection * @param requestProperties request properties * @param fromLevelIndex First level index. -1 if it is undefined * @param toLevelIndex Second level index. -1 if it is undefined * @param forMetaData true if we are using this to get metadata instead of * reading data. * * @return The GeoGridAdapter * * * @throws HugeSizeException _more_ * @throws VisADException On badness */ private GeoGridAdapter makeGeoGridAdapter(DataChoice dataChoice, DataSelection givenDataSelection, Hashtable requestProperties, int fromLevelIndex, int toLevelIndex, boolean forMetaData) throws VisADException, HugeSizeException { boolean readingFullGrid = !forMetaData; int numLevels = -1; if ((fromLevelIndex >= 0) && (toLevelIndex >= 0)) { numLevels = Math.abs(toLevelIndex - fromLevelIndex) + 1; } GridDataset myDataset = getDataset(); if (myDataset == null) { return null; } Object extraCacheKey = null; GeoGrid geoGrid = findGridForDataChoice(myDataset, dataChoice); String paramName = dataChoice.getStringId(); if (geoGrid == null) { return null; } ucar.nc2.Dimension ensDim = geoGrid.getEnsembleDimension(); GeoSelection geoSelection = ((givenDataSelection != null) ? givenDataSelection.getGeoSelection() : null); boolean needVolume = ((geoGrid.getCoordinateSystem().getVerticalTransform() != null) && ((requestProperties != null) && (requestProperties.get( DerivedDataChoice.PROP_FROMDERIVED) != null))); // System.out.println("need volume = " + needVolume + " " + geoGrid.getCoordinateSystem().getVerticalTransform()); StringBuffer filename = new StringBuffer("grid_" + paramName); try { Range ensRange = makeRange(ensDim, null, 1); Range timeRange = null; Range levelRange = null; Range xRange = null; Range yRange = null; if ((fromLevelIndex >= 0) && (toLevelIndex >= 0) && !needVolume) { levelRange = new Range(fromLevelIndex, toLevelIndex); filename.append("_r_" + fromLevelIndex + "_" + toLevelIndex); } if ((geoSelection != null) && (geoSelection.hasSpatialSubset() || geoSelection.getHasNonOneStride())) { //TODO: We should determine the size of the subset grid and use that. //readingFullGrid = false; //System.err.println("subsetting using:" + geoSelection.getLatLonRect()); extraCacheKey = geoSelection; if (levelRange != null) { extraCacheKey = Misc.newList(extraCacheKey, levelRange); } filename.append("_x_" + geoSelection.getXStrideToUse()); filename.append("_y_" + geoSelection.getYStrideToUse()); filename.append("_z_" + geoSelection.getZStrideToUse()); if (geoSelection.getLatLonRect() != null) { * @param levels levels LatLonRect bbox = geoSelection.getLatLonRect(); filename.append("_rect_" + cleanBBoxName(bbox)); List yx_ranges = geoGrid.getCoordinateSystem().getRangesFromLatLonRect( bbox); yRange = makeRange(geoGrid.getYDimension(), (Range) yx_ranges.get(0), geoSelection.getYStrideToUse()); xRange = makeRange(geoGrid.getXDimension(), (Range) yx_ranges.get(1), geoSelection.getYStrideToUse()); } else if (geoSelection.getHasNonOneStride()) { yRange = makeRange(geoGrid.getYDimension(), yRange, geoSelection.getYStrideToUse()); xRange = makeRange(geoGrid.getXDimension(), xRange, geoSelection.getYStrideToUse()); } // Z stride is ignored if if ((levelRange != null) && geoSelection.hasZStride() && (geoSelection.getZStrideToUse() > 1)) { levelRange = new Range(fromLevelIndex, toLevelIndex, geoSelection.getZStrideToUse()); } // System.out.println("level range(1): " + levelRange); geoGrid = (GeoGrid) geoGrid.makeSubset(null, ensRange, null, levelRange, yRange, xRange); /* geoGrid = geoGrid.subset(null, levelRange, geoSelection.getLatLonRect(), geoSelection.getZStrideToUse(), geoSelection.getYStrideToUse(), geoSelection.getXStrideToUse()); */ } else if (levelRange != null) { extraCacheKey = levelRange; // System.out.println("level range(2): " + levelRange); //geoGrid = geoGrid.subset(null, levelRange, null, null); geoGrid = (GeoGrid) geoGrid.makeSubset(null, ensRange, null, levelRange, yRange, xRange); } } catch (InvalidRangeException ire) { throw new IllegalArgumentException("Invalid range:" + ire); } // check to see if user wants to be warned about download size boolean warn = getIdv().getStore().get(PREF_LARGE_REMOTE_DATA_WARN, false); boolean fromBundle = this.haveBeenUnPersisted; // just prior to loading data if ((readingFullGrid) && ( !fromBundle) && (warn)) { // check if interactive, if restoring from a bundle, and if file being loaded is remote if (getIdv().getInteractiveMode() && ( !isLocalFile())) { long total = 1; // get dimensions (note that the time dimension returned does not take into // account subsetting! List dims = geoGrid.getDimensions(); // grab spatial dimension indices. List geoDims = Arrays.asList(geoGrid.getXDimensionIndex(), geoGrid.getYDimensionIndex(), geoGrid.getZDimensionIndex()); for (int d = 0; d < geoDims.size(); d++) { if (geoDims.get(d) != -1) { ucar.nc2.Dimension dim = (ucar.nc2.Dimension) dims.get(geoDims.get(d)); total *= dim.getLength(); } } * // check if there is a time dimension, and if so, take into account for number of points if (geoGrid.getTimeDimension() != null) { try { total *= givenDataSelection.getTimes().size(); } catch (NullPointerException npe) { // if use default is selected in field selector for time, then // the getTimes() on the given data source throws and NPE and we // need to go to geoGrid to get the number of times. Note that * @return index // that getTimes() on geoGrid does not reflect any temporal subsetting // which is why we check the givenDataSelection first... total *= geoGrid.getTimes().size(); } } // compute size in megabytes of request (minus overhead of network protocol) double mb = (total * geoGrid.getDataType().getSize()); mb = (mb / 1048576.); if (mb > SIZE_THRESHOLD) { JCheckBox askCbx = new JCheckBox("Don't show this again", !warn); JComponent msgContents = GuiUtils .vbox(GuiUtils .inset(new JLabel("You are about to load " + ((int) mb) + " MB of data.
Are you sure you want to do this?


" + "
Consider subsetting for better performance!

"), 5), GuiUtils .inset(askCbx, new Insets(5, 0, 0, 0))); /** * JComponent msgContents = * new JLabel( * "You are about to load " + ((int) mb) * + " MB of data.
Are you sure you want to do this?


" * + "
Consider subsetting for better performance!

"); */ if (askCbx.isSelected()) { getIdv().getStore().put(PREF_LARGE_REMOTE_DATA_WARN, false); } if ( !GuiUtils.askOkCancel( "Large Remote Data Request Warning", msgContents)) { throw new HugeSizeException(); } } } } GeoGridAdapter adapter = new GeoGridAdapter(this, geoGrid, dataChoice.getName(), dataset.getNetcdfDataset(), extraCacheKey); adapter.cacheFile = filename.toString(); return adapter; } /** * Make a range for the given parameters * * @param dim The dimension to subset * @param range an existing subset * @param stride the stride * * @return the corresponding Range * * @throws InvalidRangeException not a valid range */ private Range makeRange(ucar.nc2.Dimension dim, Range range, int stride) throws InvalidRangeException { if (dim == null) { return null; } if (range == null) { range = new Range(0, dim.getLength() - 1, stride); } else { range = new Range(range.first(), range.last(), stride); } return range; } /** * Clean up the bounding box name so it can be used in a file name. * change : and + and any other strange chars to _ * * @param bbox bounding box * * @return cleaned up name private String cleanBBoxName(LatLonRect bbox) { String name = Util.cleanName(bbox.toString()); name = name.replaceAll(":", "_"); name = name.replaceAll("\\+", "_"); return name; } /** * Find the index of the given object in the list of levels. If its * a Real then check values * * @param o Object * * @throws VisADException On badness */ private int indexOf(Object o, List levels) throws VisADException { if (o instanceof TwoFacedObject) { o = ((TwoFacedObject) o).getId(); } if (o instanceof Integer) { return ((Integer) o).intValue(); } if (o instanceof String) { try { o = ucar.visad.Util.toReal(o.toString()); } catch (Exception ignoreThis) {} } if (o instanceof String) { String s = (String) o; if (s.startsWith("#")) { int index = new Integer(s.substring(1).trim()).intValue(); return index; } o = new Real(new Double(s).doubleValue()); } if ((o instanceof Real) && (levels.size() > 0) && (levels.get(0) instanceof Real)) { Real r = (Real) o; for (int i = 0; i < levels.size(); i++) { //TODO: Check if the units are convertible Real lr = (Real) levels.get(i); if (r.getValue(lr.getUnit()) == lr.getValue()) { return i; } } return -1; } return levels.indexOf(o); } /** * Make the FieldImpl corresponding to the DataChoice and * specified DataSelection (times). * * @param dataChoice DataChoice. * @param givenDataSelection specified selection criteria. * @param requestProperties request properties * * @return the grid of data corresponding to the choice * * @throws VisADException couldn't create Data object * @throws RemoteException couldn't create remote Data object */ private FieldImpl makeFieldImpl(DataChoice dataChoice, DataSelection givenDataSelection, Hashtable requestProperties) throws VisADException, RemoteException { long millis = System.currentTimeMillis(); List allLevels = getAllLevels(dataChoice, new DataSelection(GeoSelection.STRIDE_BASE)); Trace.call1("GeoGridDataSource.makeField"); Object fromLevel = givenDataSelection.getFromLevel(); Object toLevel = givenDataSelection.getToLevel(); int fromLevelIndex = -1; int toLevelIndex = -1; if ((fromLevel != null) && (toLevel != null)) { fromLevelIndex = indexOf(fromLevel, allLevels); // System.err.println ("fromLevel index:" + fromLevelIndex); toLevelIndex = indexOf(toLevel, allLevels); if ((toLevelIndex < 0) || (fromLevelIndex < 0)) { System.err.println("Did not find level indices: fromLevel:" + fromLevel + " index:" + fromLevelIndex + " toLevel:" + toLevel + " index:" } + toLevelIndex + "\nLevels:" + allLevels); if ((allLevels != null) && !allLevels.isEmpty()) { System.err.println("fromLevel is a " + fromLevel.getClass().getName() + ", toLevel is a " + toLevel.getClass().getName()); System.err.println( "levels are " + allLevels.get(0).getClass().getName()); } } } long starttime = System.currentTimeMillis(); FieldImpl fieldImpl = null; //GridDataset myDataset = getDataset(); //if (myDataset == null) { // return null; //} //GeoGrid geoGrid = findGridForDataChoice(myDataset, dataChoice); //String paramName = dataChoice.getStringId(); Trace.call1("GeoGridDataSource.make GeoGridAdapter"); // System.err.println("levels:" + fromLevelIndex +" " + toLevelIndex); GeoGridAdapter adapter = null; try { adapter = makeGeoGridAdapter(dataChoice, givenDataSelection, requestProperties, fromLevelIndex, toLevelIndex, false); } catch (HugeSizeException hse) { return null; } if (adapter == null) { throw new BadDataException("Could not find field:" + dataChoice.getStringId()); } Trace.call2("GeoGridDataSource.make GeoGridAdapter"); GeoGrid geoGrid = adapter.getGeoGrid(); String paramName = dataChoice.getStringId(); Trace.call1("GeoGridDataSource.make times"); List times = getTimesFromDataSelection(givenDataSelection, dataChoice); // Datasource overrides data selection List members = getEnsembleSelection(); if (members == null) { members = (List) givenDataSelection.getProperty(PROP_ENSEMBLEMEMBERS); } int[] membersIndices = null; if (members != null) { int msize = members.size(); membersIndices = new int[msize]; for (int i = 0; i < msize; i++) { membersIndices[i] = ((Integer) members.get(i)).intValue(); } } int[] timeIndices = null; List allTimes = null; if (times != null) { timeIndices = new int[times.size()]; allTimes = getGeoGridTimes((CoordinateAxis1DTime) geoGrid .getCoordinateSystem().getTimeAxis1D()); int numTimes = allTimes.size(); if (holdsIndices(times)) { for (int i = 0; i < times.size(); i++) { int index = ((Integer) times.get(i)).intValue(); if (getReverseTimes()) { index = numTimes - index - 1; } timeIndices[i] = index; } } else { for (int i = 0; i < times.size(); i++) { int index = allTimes.indexOf(times.get(i)); if (getReverseTimes()) { index = numTimes - index - 1; } timeIndices[i] = index; } } } Trace.call2("GeoGridDataSource.make times"); /* System.err.print("times:"); for(int i=0;i 0)) { fieldImpl = adapter.getSequence(timeIndices, membersIndices, loadId); } else { } fieldImpl = adapter.getSequence(timeIndices, loadId); } } if (fieldImpl == null) { // System.err.println ("data selection:" + givenDataSelection); // System.err.println ("data selection times:" + times); // System.err.println ("allTimes:" + allTimes); // Misc.printArray("timeIndices", timeIndices); } boolean useDriverTime = false; if (givenDataSelection != null) { useDriverTime = givenDataSelection.getProperty( DataSelection.PROP_USESTIMEDRIVER, false); } if ((givenDataSelection != null) && !times.isEmpty()) { CalendarDateTime t0 = new CalendarDateTime((DateTime) times.get(0)); CalendarDate dt0 = t0.getCalendarDate(); CalendarDateTime t1 = new CalendarDateTime((DateTime) times.get(times.size() - 1)); CalendarDate dt1 = t1.getCalendarDate(); dateRange = CalendarDateRange.of(dt0, dt1); } else { dateRange = null; } Trace.call2("GeoGridDataSource.getSequence"); // if made a non-null FlatField, this is displayable as 3D data; // if not, quit. if (fieldImpl == null) { if ( !JobManager.getManager().canContinue(loadId)) { return null; } LogUtil.userMessage(log_, "Unable to load field: " + paramName + " from:" + getFilePath(), true); return null; } JobManager.getManager().stopLoad(loadId); Trace.call2("GeoGridDataSource.makeField"); LogUtil.message(""); log_.debug("Read grid in " + (System.currentTimeMillis() - millis)); return fieldImpl; } // end makeField /** * Find the grid in the dataset from the DataChoice * * @param ds the grid dataset * @param dc the data choice * @return the GeoGrid or null dataset doesn't exist or if variable not found */ public GeoGrid findGridForDataChoice(GridDataset ds, DataChoice dc) { if (ds == null) { return null; } String name = dc.getStringId(); /** * * Look for new name for parameter * If name already exists in dataset, then the old name is returned * */ /** handles general variable renaming */ List newName = gribRenamer.matchNcepNames(ds, name); List userRemappedNames = DataManager.getNewVariableName(name); // check IDV variable renaming first if ((userRemappedNames != null) && !userRemappedNames.isEmpty()) { for (String remappedName : userRemappedNames) { GeoGrid tmpGeoGrid = ds.findGridByName(remappedName); if (tmpGeoGrid != null) { name = remappedName; dc.setId(name); dc.setName(name); return tmpGeoGrid; // ok, match not found in the user remapping...let's try netCDF-Java's map if (newName.size() == 1) { // a unique name has been returned from netCDF-Java - use it! name = newName.get(0); } else if (newName.size() > 0) { // netCDF-Java returned more than one match (no match was found in the // IDV tables)...ask user which one is correct LogUtil.printMessage("Multiple Matches Found for " + name); LogUtil.printMessage("Possible new variable names are:"); * List newDescription = new ArrayList(); for (String possibleNewName : newName) { LogUtil.printMessage(" " + possibleNewName); newDescription.add( ds.getDataVariable(possibleNewName).getDescription()); } LogUtil.printMessage("Please update your bundle."); if (getIdv().getInteractiveMode()) { //Misc.printStack("findgrid", 10); String msg1 = "The variable name has changed. Please select a new match.

"; String msg2 = "Possible new names for the variable " + dc.getDescription() + " are:

"; String msg3 = StringUtil.join("
", newDescription); String label = "" + msg1 + msg2 + "" + msg3 + ""; List cats = new ArrayList(); for (String possibleNewName : newName) { cats.add(new DataCategory("param:" + possibleNewName, false)); } DataOperand operand = new DataOperand(name, label, cats, false); DataTreeDialog dataDialog = new DataTreeDialog(getIdv(), null, Misc.newList(operand), Misc.newList(this), Misc.newList(dc), false); List choices = dataDialog.getSelected(); if ((choices != null) && (choices.size() > 0)) { DataChoice dc_new = (DataChoice) ((List) choices.get(0)).get(0); name = dc_new.getStringId(); dc.setId(name); dc.setName(name); GeoGrid geoGrid = ds.findGridByName(name); return geoGrid; } } } else { // ok, no matches found anywhere...return null return null; } dc.setId(name); dc.setName(name); GeoGrid geoGrid = ds.findGridByName(name); return geoGrid; } /** * Utility to check if we should ignore the given z axis * * @param zaxis given z axis * * @return Is ok */ protected boolean isZAxisOk(CoordinateAxis1D zaxis) { return GeoGridAdapter.isZAxisOk(zaxis); } /** * Override the base class method to return the times for the data choice * * @param dataChoice DataChoice in question * @return List of all times for that choice */ public List getAllDateTimes(DataChoice dataChoice) { return (List) timeMap.get(dataChoice.getId()); } /** * Create a DataChoice corresponding to the GeoGrid. This is the * workhorse of the initialization. * * @param cfield GeoGrid * @param allTimes set of times to use * @param timeToIndex a mapping of time to index * * @return corresponding DataChoice */ private DataChoice makeDataChoiceFromGeoGrid(GeoGrid cfield, List allTimes, Hashtable timeToIndex) { GridCoordSystem gcs = cfield.getCoordinateSystem(); LatLonRect llr = gcs.getLatLonBoundingBox(); LatLonPointImpl lleft = llr.getLowerLeftPoint(); LatLonPointImpl uright = llr.getUpperRightPoint(); double centerLat = lleft.getLatitude() + (uright.getLatitude() - lleft.getLatitude()) / 2.0; EarthLocation elt = null; Hashtable ht = new Hashtable(); try { elt = new EarthLocationTuple(centerLat, llr.getCenterLon(), 0); ht.put(IdvConstants.INITIAL_PROBE_EARTHLOCATION, elt); } catch (Exception e) {} //int zIndex = gcs.getZdim(); //int yIndex = gcs.getYdim(); //int xIndex = gcs.getXdim(); CoordinateAxis xaxis = gcs.getXHorizAxis(); CoordinateAxis yaxis = gcs.getYHorizAxis(); CoordinateAxis1D zaxis = gcs.getVerticalAxis(); // get dimensions of coordinate axes. long sizeZ = 0; if (zaxis != null) { sizeZ = (int) zaxis.getSize(); } Hashtable threeDProps = Misc.newHashtable(DataChoice.PROP_ICON, "/auxdata/ui/icons/3D.gif"); Hashtable twoDProps = Misc.newHashtable(DataChoice.PROP_ICON, "/auxdata/ui/icons/2D.gif"); if (ht != null) { threeDProps.putAll(ht); twoDProps.putAll(ht); } DirectDataChoice choice = null; if (sizeZ < 0) { log_.error(" weird Geogrid -- it claims size Z<0; parm " + cfield.getName()); return null; } else if ( !isZAxisOk(zaxis)) { // do not use grid with "Hybrid", potential temp or // boundary layer vertical axis coordinate } else { // might know how to handle this. String parmName = cfield.getName(); String pseudoName = parmName; String description = cfield.getDescription(); if ((description == null) || description.equals("")) { description = parmName; } CoordinateAxis1DTime tAxis = gcs.getTimeAxis1D(); List geoTimes = getGeoGridTimes(tAxis); timeMap.put(parmName, geoTimes); // List indexList = Misc.getIndexList(geoTimes, allTimes); List indexList = new ArrayList(); if ((geoTimes != null) && (allTimes != null)) { for (int i = 0; i < geoTimes.size(); i++) { Integer timeIndex = (Integer) timeToIndex.get(geoTimes.get(i)); indexList.add(timeIndex); } } // none or only one level, we'll call it a 2D grid DataSelection dataSelection = DataSelection.NULL; if (false && !indexList.isEmpty()) { dataSelection = new DataSelection(indexList, DataSelection.TIMESMODE_USETHIS); } List categories = null; Hashtable props = null; if ((sizeZ == 0) || (sizeZ == 1)) { //if (sizeZ == 0) { int xLength = cfield.getXDimension().getLength(); int yLength = cfield.getYDimension().getLength(); ucar.nc2.Dimension ensDim = cfield.getEnsembleDimension(); if (twoDDimensionsLabel == null) { twoDDimensionsLabel = "Total grid size: x: " + xLength + " y: " + yLength + " #points: " + (xLength * yLength); } props = new Hashtable(twoDProps); props.put(PROP_GRIDSIZE, new ThreeDSize(xLength, yLength)); if (geoTimes != null) { props.put(PROP_TIMESIZE, new Integer(geoTimes.size())); if ((ensDim != null) && (ensDim.getLength() > 1)) { List ensMembers = null; CoordinateAxis1D eAxis = gcs.getEnsembleAxis(); int numEns = ensDim.getLength(); if ((ensMembers == null) && (eAxis != null)) { ensMembers = eAxis.getNames(); } int[] ids = new int[numEns]; String[] enames = new String[numEns]; for (int i = 0; i < numEns; i++) { ids[i] = i; NamedAnything na = (NamedAnything) ensMembers.get(i); if (isNumeric(na.toString())) { enames[i] = "Member " + na.toString(); } else { enames[i] = na.toString(); } } List ensSet = TwoFacedObject.createList(ids, enames); props.put(PROP_ENSEMBLEMEMBERS, ensSet); } if ((ensDim != null) && (ensDim.getLength() > 1)) { categories = (tAxis == null) ? getTwoDCategories() : getTwoDEnsTimeSeriesCategories(); } else { categories = (tAxis == null) ? getTwoDCategories() : getTwoDTimeSeriesCategories(); } /* choice = new DirectDataChoice(this, parmName, pseudoName, description, (taxis == null) ? getTwoDCategories() : getTwoDTimeSeriesCategories(), dataSelection, props); */ } else { // if (sizeZ > 1) // Have 3D field (we expect); usually sizeZ > 1: int xLength = cfield.getXDimension().getLength(); int yLength = cfield.getYDimension().getLength(); int zLength = cfield.getZDimension().getLength(); ucar.nc2.Dimension ensDim = cfield.getEnsembleDimension(); if (xLength * yLength * zLength > max3D) { max3D = xLength * yLength * zLength; max3DX = xLength; max3DY = yLength; max3DZ = zLength; } ThreeDSize size = new ThreeDSize(xLength, yLength, zLength); props = new Hashtable(threeDProps); props.put(PROP_GRIDSIZE, size); if (geoTimes != null) { props.put(PROP_TIMESIZE, new Integer(geoTimes.size())); } if ((ensDim != null) && (ensDim.getLength() > 1)) { List ensMembers = null; CoordinateAxis1D eAxis = gcs.getEnsembleAxis(); int numEns = ensDim.getLength(); if ((ensMembers == null) && (eAxis != null)) { ensMembers = eAxis.getNames(); } int[] ids = new int[numEns]; String[] enames = new String[numEns]; for (int i = 0; i < numEns; i++) { ids[i] = i; NamedAnything na = (NamedAnything) ensMembers.get(i); enames[i] = na.toString(); } List ensSet = TwoFacedObject.createList(ids, enames); props.put(PROP_ENSEMBLEMEMBERS, ensSet); } /* choice = new DirectDataChoice(this, parmName, pseudoName, description, (tAxis == null) ? getThreeDCategories() : getThreeDTimeSeriesCategories(), dataSelection, props); */ if ((ensDim != null) && (ensDim.getLength() > 1)) { categories = (tAxis == null) ? getThreeDCategories() : getThreeDEnsTimeSeriesCategories(); } else { categories = (tAxis == null) ? getThreeDCategories() : getThreeDTimeSeriesCategories(); } } // see if we have any categorization Attribute attr = null; for (int i = 0; (i < categoryAttributes.length) && (attr == null); i++) { attr = cfield.findAttributeIgnoreCase(categoryAttributes[i]); } if (attr != null) { String append = attr.getStringValue(); if (append != null) { append = append.replaceAll(DataCategory.DIVIDER, "_"); } DataCategory cat = (DataCategory) categories.get(0); cat = cat.copyAndAppend(append); List newCategories = new ArrayList(); newCategories.add(cat); for (int i = 1; i < categories.size(); i++) { newCategories.add(categories.get(i)); } categories = newCategories; } // see if we have any categorization Group group = null; VariableEnhanced variable = cfield.getVariable(); if (variable != null) { group = variable.getParentGroup(); if ((group != null) && !group.equals("")) { String append = group.getName(); if (append != null) { append = append.replaceAll("/", ""); append = append.replaceAll(DataCategory.DIVIDER, "_"); } DataCategory cat = (DataCategory) categories.get(0); cat = cat.copyAndAppend(append); List newCategories = new ArrayList(); newCategories.add(cat); for (int i = 1; i < categories.size(); i++) { newCategories.add(categories.get(i)); } categories = newCategories; } } choice = new DirectDataChoice(this, parmName, pseudoName, description, categories, dataSelection, props); } return choice; } /** * check if a input is only numeric number * * @param str is this numeric * * @return true if numeric */ public static boolean isNumeric(String str) { try { double d = Double.parseDouble(str); } catch (NumberFormatException nfe) { return false; } return true; } /** * make a list of DateTime-s from a GeoGrid timeAxis * * @param timeAxis - GeoGrid time CoordinateAxis * @return corresponding List of DateTime-s. */ private List getGeoGridTimes(CoordinateAxis1DTime timeAxis) { if ((timeAxis == null) || (timeAxis.getSize() == 0)) { return new ArrayList(0); } List times = (List) gcsVsTime.get(timeAxis); if (times != null) { return times; } try { times = DataUtil.makeDateTimes(timeAxis); gcsVsTime.put(timeAxis, times); } catch (Exception e) { System.out.println("getGeoGridTimes() " + e); } return times; } /** for test */ private static boolean forceSubset = false; /** for test */ public static boolean testMode = false; /* public void putCache(Object key, Object value) { if(testMode) return; super.putCache(key,value); }*/ /** * Test this class by running * "java ucar.unidata.data.grid.GeoGridDataSource " * * @param args filename * * @throws Exception some error occurred */ public static void main(String[] args) throws Exception { /* if (true) { int j=0; // int []bufferSizes = {100,250,500,750,1000,5000,8092}; int []bufferSizes = {500,500,500,500}; for(int i=0;i<100;i++) { // for(j=0;j<2;j++) { ucar.grib.grib2.Grib2BitMapSection.SKIPIT = (j==0); for(String arg: args) { // ucar.unidata.io.RandomAccessFile.BUFFERSIZE = bufferSizes[i]; ucar.unidata.io.RandomAccessFile.BUFFERSIZE = 500; GridDataset gds = GridDataset.open(arg); gds.close(); File gbxFile = new File(arg+".gbx"); gbxFile.delete(); } // } } return; } */ // dead dataset... String leadUrl = "dods://lead.unidata.ucar.edu:8080/thredds/dodsC/model/NCEP/NAM/CONUS_80km/NAM_CONUS_80km_20071002_1200.grib1"; // dead dataset... String mlodeUrl = "dods://motherlode.ucar.edu:8080/thredds/dodsC/model/NCEP/NAM/CONUS_80km/NAM_CONUS_80km_20071002_1200.grib1"; String url = ((args.length == 0) ? leadUrl : mlodeUrl); String[] urls = { url }; testMode = true; for (int i = 0; i < 10000; i++) { for (int urlIdx = 0; urlIdx < urls.length; urlIdx++) { System.err.println("Reading data:" + i + " " + urls[urlIdx]); GeoGridDataSource ggds = new GeoGridDataSource(null, urls[urlIdx], null); ggds.doMakeDataChoices(); DataChoice dataChoice = ggds.findDataChoice("Temperature"); if (dataChoice == null) { dataChoice = ggds.findDataChoice("T"); } // System.err.println ("" + dataChoice.getProperties()); ggds.makeFieldImpl(dataChoice, ggds.getDataSelection(), null); } } /* GridDataset dataset = GridDataset.open("elev.nc"); GeoGrid geoGrid = findGridForDataChoice(dataset, "foo"); GeoGrid geoGrid50 = geoGrid.subset(null, null, null, 0, 50, 50); GeoGrid geoGrid100 = geoGrid.subset(null, null, null, 0, 100, 100); System.exit(0); */ /** * testMode = true; * forceSubset = false; * LogUtil.setTestMode(true); * LogUtil.startOutputBuffer(); * CacheManager.setDoCache(false); * * if (args.length == 0) { * System.out.println("Must supply a file name"); * System.exit(1); * } * boolean verbose = false; * StringBuffer titleBuffer = new StringBuffer(); * StringBuffer errors = new StringBuffer(); * StringBuffer buffer = new StringBuffer(); * boolean doAll = false; * boolean nextOneTitle = false; * List nots = new ArrayList(); * for (int i = 0; i < args.length; i++) { * if (nextOneTitle) { * titleBuffer.append(args[i] + "
"); * nextOneTitle = false; * continue; * } * * if (args[i].startsWith("-not:")) { * nots.add(args[i].substring(5)); * } * * if (args[i].equals("-verbose")) { * verbose = true; * continue; * } * if (args[i].equals("-title")) { * nextOneTitle = true; * continue; * } * if (args[i].equals("-doall")) { * doAll = true; * continue; * } * if (args[i].equals("-subset")) { * forceSubset = true; * buffer.append("Doing subsetting

\n"); * continue; * } * if (args[i].equals("-nosubset")) { * forceSubset = false; * buffer.append("Not doing subsetting

\n"); * continue; * } * boolean shouldProcess = true; * for (int notIdx = 0; notIdx < nots.size(); notIdx++) { * if (args[i].indexOf(nots.get(notIdx).toString()) >= 0) { * shouldProcess = false; * } * } * if ( !shouldProcess) { * continue; * } * * String name = "file" + i; * boolean fileOk = true; * StringBuffer fileBuffer = new StringBuffer(); * fileBuffer.append("


\nFile: " * + args[i] * + "
\n"); * DataChoice dataChoice = null; * GeoGridDataSource ggds = null; * try { * ggds = new GeoGridDataSource(null, args[i], null); * } catch (Throwable exc) { * errors.append("" + args[i] * + "
"); * fileBuffer.append("Failed to open file:"); * fileBuffer.append("
");
         *       fileBuffer.append(LogUtil.getStackTrace(exc));
         *       fileBuffer.append("
"); * fileBuffer.append("
\n"); * buffer.append(fileBuffer.toString()); * continue; * } * List dataChoices = ggds.getDataChoices(); * if (dataChoices.size() == 0) { * errors.append("" + args[i] * + "
"); * fileBuffer.append("No data choices\n"); * fileBuffer.append("
\n"); * buffer.append(fileBuffer.toString()); * continue; * } * StringBuffer okBuffer = new StringBuffer(); * StringBuffer notokBuffer = new StringBuffer(); * boolean fieldOk = true; * LogUtil.println(args[i]); * for (int dcIdx = 0; fieldOk && (dcIdx < dataChoices.size()); * dcIdx++) { * if ( !doAll && (dcIdx > 0)) { * break; * } * // LogUtil.println("\tLoop:" + dcIdx); * * dataChoice = (DataChoice) dataChoices.get(dcIdx); * DataSelection dataSelection = ggds.getDataSelection(); * Data testData = null; * try { * testData = ggds.makeFieldImpl(dataChoice, dataSelection); * } catch (Throwable exc) { * fileOk = false; * fieldOk = false; * notokBuffer.append("Exception reading field:" * + dataChoice + ""); * notokBuffer.append(preText(LogUtil.getStackTrace(exc))); * } * String s = LogUtil.getOutputBuffer(true).trim(); * if (s.length() > 0) { * boolean showError = true; * if ( !verbose) { * List lines = StringUtil.split(s, "\n", true, true); * if (lines.size() == 1) { * if (s.indexOf("Unable to load field") >= 0) { * showError = false; * } * if (s.indexOf("Unknown unit") >= 0) { * showError = false; * } * } * } * * if (showError) { * fileOk = false; * notokBuffer.append("Reading: " * + dataChoice.getId() + "" * + preText(s)); * } * } else { * okBuffer.append(dataChoice.getId() + " "); * } * if (notokBuffer.indexOf("Exception") >= 0) { * notokBuffer.append("*** Stopping here ***"); * break; * } * * } * if ( !fileOk) { * errors.append("" + args[i] * + "
"); * } * if (okBuffer.toString().length() > 0) { * // fileBuffer.append("OK: " + okBuffer.toString() + "

"); * } * if (notokBuffer.toString().length() > 0) { * fileBuffer.append(notokBuffer.toString()); * } * fileBuffer.append("

\n"); * if ((notokBuffer.toString().length() > 0) || !fileOk) { * buffer.append(fileBuffer.toString()); * } * } * LogUtil.stopOutputBuffer(); * System.out.println("

Geogrid test

\n"); * System.out.println(titleBuffer.toString()); * if (errors.toString().length() > 0) { * System.out.println("Errors:
"); * System.out.println(errors.toString()); * } * * System.out.println(buffer.toString()); * * System.exit(0); */ } /** * for test * * @param s string to format * * @return formatted string */ private static String preText(String s) { s = StringUtil.replace(s, "<", "<"); s = StringUtil.replace(s, ">", ">"); s = StringUtil.replace(s, "\n", "
\n"); s = StringUtil.replace(s, "\t", " "); s = StringUtil.replace(s, " ", " "); return "
" + s + "
"; } /** * Set the FileNameOrUrl property. * * @param value The new value for FileNameOrUrl */ public void setFileNameOrUrl(String value) { oldSourceFromBundles = value; } /** * Apply the properties * * @return everything ok */ public boolean applyProperties() { if ( !super.applyProperties()) { return false; } if (reverseTimesCheckbox != null) { reverseTimes = reverseTimesCheckbox.isSelected(); } return true; } /** * Add the reverse times checkbox * * @return extra comp */ protected JComponent getExtraTimesComponent() { reverseTimesCheckbox = new JCheckBox("Reverse Times", reverseTimes); reverseTimesCheckbox.setToolTipText( "If you have selected the first time then really use the last time"); return GuiUtils.right(reverseTimesCheckbox); } /** * Set the ReverseTimes property. * * @param value The new value for ReverseTimes */ public void setReverseTimes(boolean value) { reverseTimes = value; } /** * Get the ReverseTimes property. * * @return The ReverseTimes */ public boolean getReverseTimes() { return reverseTimes; } } >>>>>>> 72c1c20662f16e1b98193bc09dd2baf4024fb28a "); theSb.append(""); } } StringBuffer sb = null; if ((sb2d != null) || (sb3d != null)) { sb = new StringBuffer(desc); String resolverUrl = (String) getProperty(PROP_RESOLVERURL); if (resolverUrl != null) { sb.append("

"); sb.append("Resolver URL:" + resolverUrl); } sb.append( "\n

Solution content
/*
 * Copyright 1997-2013 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 * 
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful, but
        }
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package ucar.unidata.data.grid;


import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.UUID;

import javax.swing.AbstractAction;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import ucar.ma2.Array;
import ucar.ma2.InvalidRangeException;
import ucar.ma2.Range;
import ucar.nc2.Attribute;
import ucar.nc2.Group;
import ucar.nc2.Variable;
import ucar.nc2.dataset.CoordinateAxis;
import ucar.nc2.dataset.CoordinateAxis1D;
import ucar.nc2.dataset.CoordinateAxis1DTime;
import ucar.nc2.dataset.NetcdfDataset;
import ucar.nc2.dataset.VariableEnhanced;
import ucar.nc2.dods.DODSNetcdfFile;
import ucar.nc2.dt.GridCoordSystem;
import ucar.nc2.dt.grid.GeoGrid;
import ucar.nc2.dt.grid.GridDataset;
import ucar.nc2.dt.grid.NetcdfCFWriter;
import ucar.nc2.grib.GribVariableRenamer;
import ucar.nc2.time.CalendarDate;
import ucar.nc2.time.CalendarDateRange;
import ucar.nc2.util.NamedAnything;
import ucar.unidata.data.BadDataException;
import ucar.unidata.data.DataCategory;
import ucar.unidata.data.DataChoice;
import ucar.unidata.data.DataManager;
import ucar.unidata.data.DataOperand;
import ucar.unidata.data.DataSelection;
import ucar.unidata.data.DataSourceDescriptor;
import ucar.unidata.data.DataUtil;
import ucar.unidata.data.DerivedDataChoice;
import ucar.unidata.data.DirectDataChoice;
import ucar.unidata.data.GeoLocationInfo;
import ucar.unidata.data.GeoSelection;
import ucar.unidata.geoloc.LatLonPointImpl;
import ucar.unidata.geoloc.LatLonRect;
import ucar.unidata.geoloc.ProjectionImpl;
import ucar.unidata.idv.DisplayControl;
import ucar.unidata.idv.IdvConstants;
import ucar.unidata.idv.ui.DataTreeDialog;
import ucar.unidata.ui.TextSearcher;
import ucar.unidata.util.CatalogUtil;
import ucar.unidata.util.FileManager;
import ucar.unidata.util.GuiUtils;
import ucar.unidata.util.IOUtil;
import ucar.unidata.util.JobManager;
import ucar.unidata.util.LogUtil;
import ucar.unidata.util.Misc;
import ucar.unidata.util.StringUtil;
import ucar.unidata.util.ThreeDSize;
import ucar.unidata.util.Trace;
import ucar.unidata.util.TwoFacedObject;
import ucar.unidata.util.WrapperException;
import ucar.unidata.xml.XmlUtil;
import ucar.visad.Util;
import ucar.visad.data.CalendarDateTime;
import visad.Data;
import visad.DateTime;
import visad.FieldImpl;
import visad.Real;
import visad.VisADException;
import visad.georef.EarthLocation;
import visad.georef.EarthLocationTuple;


/**
 * Handles gridded files
 *
 * @author IDV Development Team
 */

public class GeoGridDataSource extends GridDataSource {


    /** Used to synchronize the geogridadapter */
    protected final Object DOMAIN_SET_MUTEX = new Object();

    /** Throw an error when loading a grid bigger than this in megabytes */
    //private static final int SIZE_THRESHOLD = 500000000;
    //Note that 60 MB is twice the limit of image data from ADDE
    private static final int SIZE_THRESHOLD = 120;

    /** The prefix we hack onto the u and v  variables */
    private static final String PREFIX_GRIDRELATIVE = "GridRelative_";

    /** Preference */
    public static final String PREF_VERTICALCS = IdvConstants.PREF_VERTICALCS;

    /** Preference - warn users for large remote data requests */
    public static final String PREF_LARGE_REMOTE_DATA_WARN =
        IdvConstants.PREF_LARGE_REMOTE_DATA_WARN;

    /** grid size */
    public static final String PROP_GRIDSIZE = "prop.gridsize";

    /** property timesize */
    public static final String PROP_TIMESIZE = "prop.timesize";

    /** property time variable */
    public static final String PROP_TIMEVAR = "timeVariable";

    /** This is used to synchronize geogrid read access */
    protected final Object readLock = new Object();

    /** logging category */
    static ucar.unidata.util.LogUtil.LogCategory log_ =
        ucar.unidata.util.LogUtil.getLogInstance(
            GeoGridDataSource.class.getName());

    /** the dataset */
    private GridDataset dataset;

    /** list of times for this dataset */
    private List myTimes = new ArrayList();

    /** list of levels for this dataset */
    private List myLevels = new ArrayList();

    /** hashtable of coordinate systems and times */
    private Hashtable gcsVsTime = new Hashtable();

    /** Keep track of each data choices times */
    private Hashtable timeMap = new Hashtable();

    /** The first projection we find */
    private ProjectionImpl sampleProjection;

    /** Keep track of the spatial dimensions */
    private String dimensionsLabel;

    /** Keep track of the spatial dimensions */
    private String threeDDimensionsLabel = null;

    /** Keep track of the spatial dimensions */
    private String twoDDimensionsLabel = null;

    /** Keep track of the max grid size */
    private int max3DX;

    /** Keep track of the max grid size */
    private int max3DY;

    /** Keep track of the max grid size */
    private int max3DZ;

    /** Keep track of the max grid size */
    private int max3D;

    /** category attributes */
    private static String[] categoryAttributes = { "GRIB_param_category",
            "Grib2_Parameter_Category" };


    /** Do we really reverse the time indices */
    private boolean reverseTimes = false;

    /** for properties */
    private JCheckBox reverseTimesCheckbox;

    /** for zidv */
    private CalendarDateRange dateRange;

    /** handles the grib variable renaming */
    private static GribVariableRenamer gribRenamer =
        new GribVariableRenamer();

    /**
     * Default constructor
     */
    public GeoGridDataSource() {}


    /**
     * Construct a GeoGridDataSource
     *
     * @param descriptor   the data source descriptor
     * @param gds          The GridDataset
     * @param name         A name
     * @param filename     the filename
     */
    public GeoGridDataSource(DataSourceDescriptor descriptor,
                             GridDataset gds, String name, String filename) {
        super(descriptor, filename, name, (Hashtable) null);
        dataset = gds;
    }


    /**
     * Create a GeoGridDataSource from the GridDataset
     *
     * @param gds  the GridDataset
     */
    public GeoGridDataSource(GridDataset gds) {
        dataset = gds;
    }


    /**
     * Create a GeoGridDataSource from a File.
     *
     * @param descriptor   Describes this data source, has a label etc.
     * @param file         This is the file that points to the actual
     *                     data source.
     * @param properties   General properties used in the base class
     *
     * @throws IOException  problem opening file
     */
    public GeoGridDataSource(DataSourceDescriptor descriptor, File file,
                             Hashtable properties)
            throws IOException {
        this(descriptor, file.getPath(), properties);
    }



    /**
     * Create a GeoGridDataSource from the filename.
     * @param descriptor   Describes this data source, has a label etc.
     * @param filename     This is the filename (or url) that points
     *                     to the actual data source.
     * @param properties   General properties used in the base class
     *
     * @throws IOException
     */
    public GeoGridDataSource(DataSourceDescriptor descriptor,
                             String filename, Hashtable properties)
            throws IOException {
        //Make sure we pass filename up here - as opposed to calling
        //this (new File (filename)) because the new File (filename).getPath () != filename
        super(descriptor, filename, "Geogrid data source", properties);
    }


    /**
     * Create a GeoGridDataSource from the filename.
     * @param descriptor   Describes this data source, has a label etc.
     * @param files List of files or urls
     * @param properties   General properties used in the base class
     *
     * @throws IOException
     */
    public GeoGridDataSource(DataSourceDescriptor descriptor, List files,
                             Hashtable properties)
            throws IOException {
        //Make sure we pass filename up here - as opposed to calling
        //this (new File (filename)) because the new File (filename).getPath () != filename
        super(descriptor, files, "Geogrid data source", properties);
    }


    /**
     * Set the default selection bounds
     *
     * @param rect rectangle
     */
    public void setDefaultSelectionBounds(Rectangle2D.Float rect) {
        getDataSelection().getGeoSelection(true).setLatLonRect(rect);
    }


    /**
     * Load any subset info in field mask xml
     *
     * @param root xml root
     */
    protected void applyFieldMask(Element root) {
        super.applyFieldMask(root);
        GeoSelection geoSubset = getDataSelection().getGeoSelection();
        if (geoSubset == null) {
            geoSubset = new GeoSelection();
            getDataSelection().setGeoSelection(geoSubset);
        }
        Element stride = XmlUtil.getElement(root, "stride");
        if (stride != null) {
            geoSubset.setXStride(XmlUtil.getAttribute(stride, ATTR_X,
                    geoSubset.getXStride()));
            geoSubset.setYStride(XmlUtil.getAttribute(stride, ATTR_Y,
                    geoSubset.getYStride()));
            geoSubset.setZStride(XmlUtil.getAttribute(stride, ATTR_Z,
                    geoSubset.getZStride()));
        }
        Element subset = XmlUtil.getElement(root, "subset");
        if (subset != null) {
            geoSubset.setBoundingBox(
                new GeoLocationInfo(
                    XmlUtil.getAttribute(subset, ATTR_NORTH, 0.0),
                    XmlUtil.getAttribute(subset, ATTR_WEST, 0.0),
                    XmlUtil.getAttribute(subset, ATTR_SOUTH, 0.0),
                    XmlUtil.getAttribute(subset, ATTR_EAST, 0.0)));
        }

    }


    /**
     * Can we mask the data?
     *
     * @return  true if we can
     */
    protected boolean canDoFieldMask() {
        return true;
    }




    /**
     * Write out the field mask file
     *
     * @param doc   doc to write to
     * @param root  root node
     */
    protected void writeFieldMaskFile(Document doc, Element root) {
        GeoSelection geoSubset = getDataSelection().getGeoSelection();
        if (geoSubset != null) {
            Element stride = doc.createElement("stride");
            root.appendChild(stride);
            if (geoSubset.getXStride() > 1) {
                stride.setAttribute(ATTR_X, geoSubset.getXStride() + "");
            }
            if (geoSubset.getYStride() > 1) {
                stride.setAttribute(ATTR_Y, geoSubset.getYStride() + "");
            }
            if (geoSubset.getZStride() > 1) {
                stride.setAttribute(ATTR_Z, geoSubset.getZStride() + "");
            }
            GeoLocationInfo bbox = geoSubset.getBoundingBox();
            if (bbox != null) {
                Element subset = doc.createElement("subset");
                subset.setAttribute(ATTR_NORTH, bbox.getMaxLat() + "");
                subset.setAttribute(ATTR_SOUTH, bbox.getMinLat() + "");
                subset.setAttribute(ATTR_WEST, bbox.getMinLon() + "");
                subset.setAttribute(ATTR_EAST, bbox.getMaxLon() + "");
                root.appendChild(subset);
            }

        }

    }


    /**
     * Initialize if being unpersisted.
     */
    public void initAfterUnpersistence() {
        //Support legacy bundles
        if ( !haveSources() && (oldSourceFromBundles == null)
                && !hasPollingInfo()) {
            oldSourceFromBundles = getName();
        }
        super.initAfterUnpersistence();
        resolvePath();
        //Call getDataset to see if we have a valid file
        getDataset();
    }


    /**
     * Add in the spatial dimensions label
     *
     * @return The subset properties component
     */
    protected JComponent doMakeGeoSubsetPropertiesComponent() {
        JComponent comp = super.doMakeGeoSubsetPropertiesComponent();
        if ((dataset == null) || (dimensionsLabel == null)) {
            return comp;
        }

        JLabel label = new JLabel(dimensionsLabel);
        return GuiUtils.topCenter(GuiUtils.left(GuiUtils.inset(label, 5)),
                                  comp);
    }


    /**
     * Get the extra label that is shown in the geo-subset panel
     *
     * @return Extra label for geosubset panel
     */
    protected JComponent getExtraGeoSelectionComponent() {
        if (dimensionsLabel == null) {
            return super.getExtraGeoSelectionComponent();
        }
        String tmp = dimensionsLabel;
        int    idx = tmp.indexOf("#");
        if (idx > 0) {
            tmp = tmp.substring(0, idx);
        }
        return new JLabel(tmp);
    }


    /**
     * Add any extra tabs into the properties tab
     *
     * @param tabbedPane The properties tab
     */
    public void addPropertiesTabs(JTabbedPane tabbedPane) {
        super.addPropertiesTabs(tabbedPane);
        if (dataset == null) {
            return;
        }
        int height = 300;
        int width  = 400;

        /*
        JTextArea infoText = new JTextArea();
        infoText.setText(dataset.getInfo());
        infoText.setFont(Font.decode("monospaced"));

        JScrollPane infoScroller = GuiUtils.makeScrollPane(infoText, width, height);
        infoScroller.setPreferredSize(new Dimension(width, height));
        infoScroller.setMinimumSize(new Dimension(width, height));
        tabbedPane.add("Info", GuiUtils.inset(infoScroller, 5));
        */


        JTextArea    dumpText = new JTextArea();
        TextSearcher searcher = new TextSearcher(dumpText);
        dumpText.setFont(Font.decode("monospaced"));
        //ByteArrayOutputStream bos = new ByteArrayOutputStream();
        StringWriter bos = new StringWriter();
        try {
            ucar.nc2.NCdumpW.print(dataset.getNetcdfDataset(), "", bos, null);
        } catch (IOException ioe) {
            logException("Dumping netcdf file", ioe);
        }
        dumpText.setText(bos.toString());
        JScrollPane scroller = GuiUtils.makeScrollPane(dumpText, width,
                                   height);
        scroller.setPreferredSize(new Dimension(width, height));
        scroller.setMinimumSize(new Dimension(width, height));
        tabbedPane.add("Metadata",
                       GuiUtils.inset(GuiUtils.centerBottom(scroller,
                           searcher), 5));
    }





    /**
     * Initialize after we have been created.
     */
    public void initAfterCreation() {
        super.initAfterCreation();
        resolvePath();
        //Call getDataset to see if we have a valid file
        getDataset();
    }


    /**
     * What should be changed by the user when in data relative mode
     *
     * @return paths to changed
     */
    public List getDataPaths() {
        String resolverUrl = (String) getProperty(PROP_RESOLVERURL);
        if ((resolverUrl != null) && (resolverUrl.length() > 0)) {
            return Misc.newList(resolverUrl);
        }
        return super.getDataPaths();
    }

    /**
     * Update the state
     *
     * @param newObject new object
     * @param newProperties  properties
     */
    public void updateState(Object newObject, Hashtable newProperties) {
        removeProperty(PROP_RESOLVERURL);
        super.updateState(newObject, newProperties);
    }


    /**
     * Set what the user has changed
     *
     * @param paths new paths
     */
    public void setTmpPaths(List paths) {
        //TODO: Figure out what to do here
        String resolverUrl = (String) getProperty(PROP_RESOLVERURL);
        oldResolverUrl = resolverUrl;
        if (((paths != null) && (paths.size() > 0)) && (resolverUrl != null)
                && (resolverUrl.length() > 0)) {
            Hashtable properties = getProperties();
            if (properties == null) {
                properties = new Hashtable();
            }
            String firstone = paths.get(0).toString();
            //If we are being saved as a zidv then we remove the resolverurl
            if (firstone
                    .indexOf(ucar.unidata.idv.IdvPersistenceManager
                        .PROP_ZIDVPATH) >= 0) {
                getProperties().remove(PROP_RESOLVERURL);
            } else {
                String resolvedUrl = CatalogUtil.resolveUrl(firstone,
                                         properties);
                if (resolvedUrl != null) {
                    setProperty(PROP_RESOLVERURL, firstone);
                }
            }
        }
        super.setTmpPaths(paths);
    }



    /**
     * Resolve the url if we have to
     */
    protected void resolvePath() {
        //Do we have a resolver
        String resolverUrl = (String) getProperty(PROP_RESOLVERURL);
        if ((resolverUrl != null) && (resolverUrl.length() > 0)) {
            Hashtable properties = getProperties();
            if (properties == null) {
                properties = new Hashtable();
            }
            String resolvedUrl = CatalogUtil.resolveUrl(resolverUrl,
                                     properties);
            if (resolvedUrl == null) {
                setInError(true);
                return;
            }
            //            System.err.println("    got resolved path:" + resolvedUrl);
            sources = Misc.newList(resolvedUrl);
    }


    /**
     * The source has changed
     */
    protected void sourcesChanged() {
        dataset   = null;
        gcsVsTime = new Hashtable();
        super.sourcesChanged();
    }


    /**
     * Clear out the data set
     */
    public void reloadData() {
        myTimes   = null;
        dataset   = null;
        gcsVsTime = new Hashtable();
        resolvePath();
        dataChoices = null;
        //        doMakeDataChoices();
        getDataChoices();
        super.reloadData();

        /**
         *  not sure if we want to do this since we might have
         *  cachedflatfields out there that are pointing at the old
         *  directory
         *  clearFileCache();
         */

    }




    /**
     * Called when Datasource is removed.
     */
    public void doRemove() {
        super.doRemove();
        try {
            if (dataset != null) {
                dataset.close();
            }
        } catch (IOException ioe) {}
        dataset   = null;
        gcsVsTime = null;
    }


    /**
     * Can this DataSource save data to local disk?
     *
     * @return true if this DataSource can save data to local disk?
     */
    public boolean canSaveDataToLocalDisk() {
        if (isFileBased()) {
            //            return false;
        }
        return true;
    }


    /**
     * Get the label for the save data file option
     *
     * @return label
     */
    protected String getSaveDataFileLabel() {
        return (isFileBased()
                ? "Writing grid file"
                : getSaveDataFileLabel());
    }

    /**
     * Make savel local actions
     *
     * @param actions list of actions
     */
    protected void makeSaveLocalActions(List actions) {
        String         lbl = (isFileBased()
                              ? "Subset and Write Grid"
                              : "Write Local Grid");
        AbstractAction a   = new AbstractAction(lbl) {
            public void actionPerformed(ActionEvent ae) {
                Misc.run(new Runnable() {
                    public void run() {
                        try {
                            saveDataToLocalDisk();
                        } catch (Exception exc) {
                            logException("Writing data to local disk", exc);
                        }
                    }
                });
            }
        };
        actions.add(a);
    }




    /**
     * Overwrite this method so we don't show the loading dialog
     *
     * @param msg The msg to show in the dialog
     *
     * @return The jobmanager loadid
     */
    protected Object beginWritingDataToLocalDisk(String msg) {
        final Object loadId = JobManager.getManager().startLoad(msg, false,
                                  false);
        return loadId;
    }



    /**
     * Save the data to local disk.
     *
     *
     * @param prefix  the prefix for the local file name
     * @param loadId  the load id (for cancelling)
     * @param changeLinks true to change the links
     *
     * @return The list of files
     *
     * @throws Exception  problem saving data.
     */
    protected List saveDataToLocalDisk(String prefix, Object loadId,
                                       boolean changeLinks)
            throws Exception {


        List                  choices            = getDataChoices();
        final List checkboxes         = new ArrayList();
        List                  categories         = new ArrayList();
        Hashtable             catMap             = new Hashtable();
        Hashtable             currentDataChoices = new Hashtable();


        List                  displays = getIdv().getDisplayControls();
        for (int i = 0; i < displays.size(); i++) {
            List dataChoices =
                ((DisplayControl) displays.get(i)).getDataChoices();
            if (dataChoices == null) {
                continue;
            }
            List finalOnes = new ArrayList();

            for (int j = 0; j < dataChoices.size(); j++) {
                ((DataChoice) dataChoices.get(j)).getFinalDataChoices(
                    finalOnes);
            }
            for (int dcIdx = 0; dcIdx < finalOnes.size(); dcIdx++) {
                DataChoice dc = (DataChoice) finalOnes.get(dcIdx);
                if ( !(dc instanceof DirectDataChoice)) {
                    continue;
                }
                DirectDataChoice ddc = (DirectDataChoice) dc;
                if (ddc.getDataSource() != this) {
                    continue;
                }
                currentDataChoices.put(ddc.getName(), "");
            }
        }

        if (getDefaultSave()) {
            List varNames = new ArrayList();

            for (int i = 0; i < dataChoices.size(); i++) {
                DataChoice dataChoice = (DataChoice) dataChoices.get(i);
                if ( !(dataChoice instanceof DirectDataChoice)) {
                    continue;
                }
                String name = dataChoice.getName();
                //hack, hack, hack,
                if (name.startsWith(PREFIX_GRIDRELATIVE)) {
                    name = name.substring(PREFIX_GRIDRELATIVE.length());
                }
                if (currentDataChoices.get(name) != null) {
                    varNames.add(name);
                }
            }
            return (currentDataChoices.size() > 0)
                ? writeNc(prefix, changeLinks, varNames)
                : null;
        }


        for (int i = 0; i < dataChoices.size(); i++) {
            DataChoice dataChoice = (DataChoice) dataChoices.get(i);
            if ( !(dataChoice instanceof DirectDataChoice)) {
                continue;
            }
            String label = dataChoice.getDescription();
            if (label.length() > 30) {
                label = label.substring(0, 29) + "...";
            }
            JCheckBox cbx =
                new JCheckBox(label,
                              currentDataChoices.get(dataChoice.getName())
                              != null);
            ThreeDSize size =
                (ThreeDSize) dataChoice.getProperty(PROP_GRIDSIZE);
            cbx.setToolTipText(dataChoice.getName());
            checkboxes.add(cbx);
    }
            DataCategory dc    = dataChoice.getDisplayCategory();
            List         comps = (List) catMap.get(dc);
            if (comps == null) {
                comps = new ArrayList();
                catMap.put(dc, comps);
                categories.add(dc);
            }
            comps.add(cbx);
            comps.add(GuiUtils.filler());
            if (size != null) {
                JLabel sizeLabel = GuiUtils.rLabel(size.getSize() + "  ");
                sizeLabel.setToolTipText(size.getLabel());
                comps.add(sizeLabel);
            } else {
                comps.add(new JLabel(""));
            }
        }

        final JCheckBox allCbx = new JCheckBox("Select All");
        allCbx.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                for (JCheckBox cbx : checkboxes) {
                    cbx.setSelected(allCbx.isSelected());
                }
            }
        });
        List        catComps = new ArrayList();
        JTabbedPane tab      = new JTabbedPane(JTabbedPane.LEFT);

        for (int i = 0; i < categories.size(); i++) {
            List   comps      = (List) catMap.get(categories.get(i));
            JPanel innerPanel = GuiUtils.doLayout(comps, 3, GuiUtils.WT_NYN,
                                    GuiUtils.WT_N);
            JScrollPane sp = new JScrollPane(GuiUtils.top(innerPanel));
            sp.setPreferredSize(new Dimension(500, 400));
            JPanel top =
                GuiUtils.right(GuiUtils.rLabel("Grid Size (Points)  "));
            JComponent inner = GuiUtils.inset(GuiUtils.topCenter(top, sp), 5);
            tab.addTab(categories.get(i).toString(), inner);
            //            catComps.add();
        }


        //        JComponent contents = GuiUtils.hbox(catComps);
        JComponent contents = tab;
        contents = GuiUtils.topCenter(
            GuiUtils.inset(
                GuiUtils.leftRight(
                    new JLabel("Select the fields to download"),
                    allCbx), 5), contents);
        JLabel label = new JLabel(getNameForDataSource(this, 50, true));
        contents = GuiUtils.topCenter(label, contents);
        contents = GuiUtils.inset(contents, 5);
        if ( !GuiUtils.showOkCancelDialog(null, "", contents, null)) {
            return null;
        }


        List varNames = new ArrayList();
        for (int i = 0; i < dataChoices.size(); i++) {
            DataChoice dataChoice = (DataChoice) dataChoices.get(i);
            if ( !(dataChoice instanceof DirectDataChoice)) {
                continue;
            }
            JCheckBox cbx = (JCheckBox) checkboxes.get(i);
            if ( !cbx.isSelected()) {
                continue;
            }
            String name = dataChoice.getName();
            //hack, hack, hack,
            if (name.startsWith(PREFIX_GRIDRELATIVE)) {
                name = name.substring(PREFIX_GRIDRELATIVE.length());
            }
            varNames.add(name);
        }
        if (varNames.size() == 0) {
            return null;
        }

        return writeNc(prefix, changeLinks, varNames);
    }

    /**
     * Write netCDF file.
     *
     * @param prefix  the prefix for the local file name
     * @param changeLinks true to change the links
     * @param varNames the var names to write
     *
     * @return The list of files
     */
    private List writeNc(String prefix, boolean changeLinks, List varNames) {
        Object       loadId;
        LatLonRect   llr           = null;
        int          hStride       = 1;
        int          zStride       = 1;
        int          timeStride    = 1;
        GeoSelection geoSubset     = getDataSelection().getGeoSelection();
        boolean      includeLatLon = false;

        if (geoSubset != null) {
            if (geoSubset.getBoundingBox() != null) {
                llr = geoSubset.getBoundingBox().getLatLonRect();
            }
            if (geoSubset.hasXStride()) {
                hStride = geoSubset.getXStride();
            }
            if (geoSubset.hasZStride()) {
                zStride = geoSubset.getZStride();
            }
        }

        String         path   = prefix;
        NetcdfCFWriter writer = new NetcdfCFWriter();

        //Start the load, showing the dialog
        loadId = JobManager.getManager().startLoad("Copying data", true,
                true);
        try {
            writer.makeFile(path, dataset, varNames, llr, dateRange,
                            includeLatLon, hStride, zStride, timeStride);
        } catch (Exception exc) {
            logException("Error writing local netcdf file.\nData:"
                         + getFilePath() + "\nVariables:" + varNames, exc);
            return null;
        } finally {
            JobManager.getManager().stopLoad(loadId);
        }


        if (geoSubset != null) {
            geoSubset.clearStride();
            geoSubset.setBoundingBox(null);
            if (geoSelectionPanel != null) {
                geoSelectionPanel.initWith(doMakeGeoSelectionPanel());
            }
        }

        List newFiles = Misc.newList(path);
        if (changeLinks) {
            //Get rid of the resolver URL
            getProperties().remove(PROP_RESOLVERURL);
            setNewFiles(newFiles);
        }
        return newFiles;
    }




    /**
     * Are we a local file
     *
     * @return is a local file
     */
    public boolean isLocalFile() {
        return new File(getFilePath()).exists();
    }


    /**
     *  Overwrite setNewFiles so we clear out the resolverurl
     *
     * @param files The list of new files to use
     */
    public void setNewFiles(List files) {
        getProperties().remove(PROP_RESOLVERURL);
        super.setNewFiles(files);
    }


    /**
     * Get the local directory
     *
     * @param label   a label
     * @param prefix  the prefix
     *
     * @return the path
     */
    protected String getLocalDirectory(String label, String prefix) {
        changeDataPathsCbx.setToolTipText(
            "Should this data source also be changed");
        return FileManager.getWriteFile(FileManager.FILTER_NETCDF, null,
                                        GuiUtils.top(changeDataPathsCbx));
    }


    /**
     * Get the full description of the grid
     *
     * @return  the description
     */
    public String getFullDescription() {
        String       desc        = super.getFullDescription();
        StringBuffer sb2d        = null;
        StringBuffer sb3d        = null;
        List         dataChoices = getDataChoices();
        for (int i = 0; i < dataChoices.size(); i++) {
            DataChoice dataChoice = (DataChoice) dataChoices.get(i);
            if ( !(dataChoice instanceof DirectDataChoice)) {
                continue;
            }
            ThreeDSize size =
                (ThreeDSize) dataChoice.getProperty(PROP_GRIDSIZE);
            Integer timeSize =
                (Integer) dataChoice.getProperty(PROP_TIMESIZE);
            if (size != null) {
                long         total     = size.getSizeY() * size.getSizeX();
                StringBuffer theSb     = null;
                String       sizeEntry = null;
                if (size.getSizeZ() > 1) {
                    if (sb3d == null) {
                        sb3d = new StringBuffer();
                    }
                    total     *= size.getSizeZ();
                    theSb     = sb3d;
                    sizeEntry = size.getSizeX() + "x" + size.getSizeY() + "x"
                                + size.getSizeZ();
                } else {
                    if (sb2d == null) {
                        sb2d = new StringBuffer();
                    }
                    theSb     = sb2d;
                    sizeEntry = size.getSizeX() + "x" + size.getSizeY();
                }
                theSb.append("
" + dataChoice.getName() + "" + dataChoice.getDescription() + "" + sizeEntry + ""); if (timeSize != null) { int times = timeSize.intValue(); if (times > 0) { total *= timeSize.intValue(); theSb.append("" + timeSize); } } theSb.append("" + total + "
\n"); } if (sb2d != null) { sb.append(sb2d); } if (sb3d != null) { sb.append(sb3d); } if (sb != null) { sb.append("
FieldDescriptionDimensions#Times#Points
\n"); } if ((sb != null) && (myLevels != null) && (myLevels.size() > 0)) { sb.append("

Levels

"); for (Object o : myLevels) { sb.append("" + o); sb.append("
"); } } if (sb == null) { return desc; } return sb.toString(); } /** old resolver URL */ private String oldResolverUrl; /** * Reset the tmp state */ public void resetTmpState() { super.resetTmpState(); if (oldResolverUrl != null) { setProperty(PROP_RESOLVERURL, oldResolverUrl); } } /** * Create the dataset from the name of this DataSource. * * @return new GridDataset */ protected GridDataset doMakeDataSet() { checkForInitAfterUnPersistence(); String file = getFilePath(); if (file == null) { if (haveBeenUnPersisted) { file = getName(); } } if (file == null) { return null; } if (sources == null) { sources = new ArrayList(); sources.add(file); } //Make sythetic data ncml file if (sources.size() > 1) { String timeName = getProperty(PROP_TIMEVAR, "time"); StringBuffer sb = new StringBuffer(); sb.append("\n"); sb.append( "\n"); sb.append("\n"); for (int i = 0; i < sources.size(); i++) { String s = sources.get(i).toString(); try { if (s.startsWith("http") && s.endsWith("entry.das")) { // opendap from ramadda s = DODSNetcdfFile.canonicalURL(s); sb.append(XmlUtil.tag("netcdf", XmlUtil.attrs("location", s, "enhance", "true"), "")); } else { sb.append(XmlUtil.tag("netcdf", XmlUtil.attrs("location", IOUtil.getURL(s, getClass()).toString(), "enhance", "true"), "")); } } catch (IOException ioe) { setInError(true); throw new WrapperException( "Grid data source failed aggregating resource: " + s, ioe); } } sb.append("\n\n"); file = getDataContext().getObjectStore().getUniqueTmpFile( "multigrid_" + UUID.randomUUID().toString(), ".ncml"); try { IOUtil.writeFile(file, sb.toString()); } catch (IOException ioe) { logException("Unable to write file: " + file, ioe); return null; } log_.debug("" + sb); } try { file = convertSourceFile(file); Trace.msg("GeoGridDataSource: opening file " + file); if (file.startsWith("http") && file.endsWith("entry.das")) { // opendap from ramadda file = DODSNetcdfFile.canonicalURL(file); } GridDataset gds = GridDataset.open(file); return gds; } catch (java.io.FileNotFoundException fnfe) { setInError(true); LogUtil.consoleMessage("Original error:\n" + fnfe.toString() + "\n" + LogUtil.getStackTrace(fnfe)); throw new BadDataException("Unable to open grid:\n" + file); } catch (Exception exc) { setInError(true); throw new WrapperException( "Grid data source failed making data set: " + file, exc); } } /** * Return the GridDataset associated with this DataSource. * * @return dataset */ public GridDataset getDataset() { if (dataset == null) { Trace.call1("GeoGridDataSource.getDataSet", " name = " + sources); dataset = doMakeDataSet(); Trace.call2("GeoGridDataSource.getDataSet"); } return dataset; } /** * Return the sample projection * * @return the sample projection */ protected ProjectionImpl getSampleDataProjection() { return sampleProjection; } /** * This method is called at initialization time and * creates a set of {@link ucar.unidata.data.DirectDataChoice}-s * and adds them into the base class managed list of DataChoice-s * with the method addDataChoice. */ protected void doMakeDataChoices() { GridDataset myDataset = getDataset(); if (myDataset == null) { return; } max3DX = -1; max3DY = -1; max3DZ = -1; max3D = -1; boolean gridRelativeWind = false; NetcdfDataset ncFile = myDataset.getNetcdfDataset(); Variable windFlag = ncFile.findVariable("ResCompFlag"); if (windFlag != null) { // found it try { Array array = windFlag.read(); gridRelativeWind = !((array.getInt(array.getIndex()) & 1 << 3) == 0); } catch (IOException ioe) { LogUtil.printException(log_, "Couldn't read variable ", ioe); } } Iterator iter = myDataset.getGrids().iterator(); SortedSet uniqueTimes = Collections.synchronizedSortedSet(new TreeSet()); while (iter.hasNext()) { GeoGrid cfield = (GeoGrid) iter.next(); if (sampleProjection == null) { sampleProjection = cfield.getProjection(); // System.err.println ("The sample projection is:" + sampleProjection); } // System.out.println("llr:" + cfield.getProjection().getDefaultMapAreaLL()); GridCoordSystem gcs = cfield.getCoordinateSystem(); CoordinateAxis1D zaxis = gcs.getVerticalAxis(); if ( !isZAxisOk(zaxis)) { continue; } CoordinateAxis1DTime tAxis = gcs.getTimeAxis1D(); List geoTimes = getGeoGridTimes(tAxis); uniqueTimes.addAll(geoTimes); } if ( !uniqueTimes.isEmpty()) { myTimes = new ArrayList(uniqueTimes); } else { myTimes = new ArrayList(); } DataChoice choice; // for each GeoGridImpl in the dataset iter = myDataset.getGrids().iterator(); Hashtable timeToIndex = new Hashtable(); for (int i = 0; i < myTimes.size(); i++) { timeToIndex.put(myTimes.get(i), new Integer(i)); } int cnt = 0; while (iter.hasNext()) { GeoGrid cfield = (GeoGrid) iter.next(); choice = makeDataChoiceFromGeoGrid(cfield, myTimes, timeToIndex); if (choice != null) { cnt++; if (gridRelativeWind == true) { if ((choice.getDescription().indexOf("u-component") >= 0) || (choice.getDescription().indexOf( "v-component") >= 0)) { choice.setName(PREFIX_GRIDRELATIVE + choice.getName()); } } /*else { // check for GRIB definition String canonical = DataAlias.aliasToCanonical(choice.getName()); if (Misc.equals(canonical, "U") || Misc.equals(canonical, "V")) { Attribute vecFlag = cfield.findAttributeIgnoreCase("GRIB_VectorComponentFlag"); if (vecFlag != null) { String vecFlagVal = vecFlag.getStringValue(); if (vecFlagVal.equals("gridRelative")) { choice.setName(PREFIX_GRIDRELATIVE + choice.getName()); } } } } */ addDataChoice(choice); } } //Check if we found any grids if (cnt == 0) { if (LogUtil.getInteractiveMode() && GuiUtils.showOkCancelDialog(null, "No Gridded Data", GuiUtils.inset(new JLabel("No gridded data found for:

  " + this + "

Do you want to try to load this as another data type?"), 5), null)) { getIdv().getDataManager().createDataSourceAndAskForType( getFilePath(), getProperties()); setInError(true, false, ""); } else { //For now just bail out setInError(true, false, ""); } return; } if (max3D > 0) { threeDDimensionsLabel = "Max grid size: x: " + max3DX + " y: " + max3DY + " z: " + max3DZ + " #points: " + (max3DX * max3DY * max3DZ); } if (threeDDimensionsLabel != null) { dimensionsLabel = threeDDimensionsLabel; } else { dimensionsLabel = twoDDimensionsLabel; } } /** * Get the Data object specified by the particular selection criteria. * * @param dataChoice DataChoice to select. * @param category DataCategory (unused at present) * @param givenDataSelection DataSelection criteria for this request. * @param requestProperties extra request selection properties (not used * in this class) * * @return Data object corresponding to the data choice * * @throws VisADException couldn't create Data object * @throws RemoteException couldn't create remote Data object */ protected Data getDataInner(DataChoice dataChoice, DataCategory category, DataSelection givenDataSelection, Hashtable requestProperties) throws VisADException, RemoteException { // synchronized (readLock) { // System.err.println("getData:" + getFilePath() +" field="+dataChoice); boolean isPR = givenDataSelection.getProperty(DataSelection.PROP_PROGRESSIVERESOLUTION, false); boolean fromBundle = getIdv().getStateManager().getProperty( IdvConstants.PROP_LOADINGXML, false); if(isPR && fromBundle){ // ucar.unidata.geoloc.LatLonPoint[] llp0 = givenDataSelection.getGeoSelection().getRubberBandBoxPoints(); GeoLocationInfo gInfo = givenDataSelection.getGeoSelection().getBoundingBox(); if(gInfo != null) { //GeoLocationInfo gInfo1 = new GeoLocationInfo( // llp0[0].getLatitude(), llp0[0].getLongitude(), // llp0[1].getLatitude(), llp0[1].getLongitude()); givenDataSelection.getGeoSelection().setBoundingBox(gInfo); } } Data data = makeFieldImpl(dataChoice, givenDataSelection, requestProperties); return data; // } /** * Get the list of parameters * * @return the list */ public List listParameters() { List params = new ArrayList(); for (DataChoice dc : (List) getDataChoices()) { params.add(dc.getName()); } return params; } /** * Get the data for a particular parameter * * @param parameter the parameter name * * @return the Data or null * * @throws RemoteException Java RMI Error * @throws VisADException VisAD Error */ public Data getData(String parameter) throws VisADException, RemoteException { DataChoice dataChoice = findDataChoice(parameter); if (dataChoice == null) { return null; } DataSelection dataSelection = new DataSelection(); // dataSelection.setTimes(Misc.newList(new Integer(0))); Data data = makeFieldImpl(dataChoice, dataSelection, new Hashtable()); return data; // } } /** * Return the list of DateTime-s associated with this DataSource. * * @return List of DateTime-s. */ protected List doMakeDateTimes() { return myTimes; } /** * Get the list of all levels available from this DataSource * * * @param dataChoice The data choice we are getting levels for * @param dataSelection the data selection * @return List of all available levels */ public List getAllLevels(DataChoice dataChoice, DataSelection dataSelection) { try { dataSelection = DataSelection.merge(dataSelection, getDataSelection()); // System.err.println("levels:" + dataSelection.getFromLevel()); Object fromLevel = dataSelection.getFromLevel(); Object toLevel = dataSelection.getToLevel(); int fromLevelIndex = -1; int toLevelIndex = -1; if ((fromLevel != null) && (toLevel != null)) { long t1 = System.currentTimeMillis(); List allLevels = getAllLevels(dataChoice, new DataSelection(GeoSelection.STRIDE_BASE)); long t2 = System.currentTimeMillis(); // System.err.println("time 1:" + (t2-t1)); fromLevelIndex = indexOf(fromLevel, allLevels); toLevelIndex = indexOf(toLevel, allLevels); } long t1 = System.currentTimeMillis(); GeoGridAdapter geoGridAdapter = makeGeoGridAdapter(dataChoice, dataSelection, null, fromLevelIndex, toLevelIndex, true); long t2 = System.currentTimeMillis(); // System.err.println("time 2:" + (t2-t1)); if (geoGridAdapter != null) { List tmpLevels = geoGridAdapter.getLevels(); myLevels = tmpLevels; return tmpLevels; } return myLevels; } catch (VisADException vae) { throw new ucar.unidata.util.WrapperException(vae); } catch (HugeSizeException hse) { return null; } } /** * We can do geo selection in the properties gui * * @return can do geo selection */ public boolean canDoGeoSelection() { return true; } /** * Can this data source cache its * * @return can cache data to disk */ public boolean canCacheDataToDisk() { return true; } /** * Class description * * * @version Enter version here..., Wed, Nov 28, '12 * @author Enter your name here... */ public static class HugeSizeException extends Exception {} /** * Utility to create a new GeoGridAdapter for the given choice and data selection and * level indices * * @param dataChoice The data choice * @param givenDataSelection Data selection * @param requestProperties request properties * @param fromLevelIndex First level index. -1 if it is undefined * @param toLevelIndex Second level index. -1 if it is undefined * @param forMetaData true if we are using this to get metadata instead of * reading data. * * @return The GeoGridAdapter * * * @throws HugeSizeException _more_ * @throws VisADException On badness */ private GeoGridAdapter makeGeoGridAdapter(DataChoice dataChoice, DataSelection givenDataSelection, Hashtable requestProperties, int fromLevelIndex, int toLevelIndex, boolean forMetaData) throws VisADException, HugeSizeException { boolean readingFullGrid = !forMetaData; int numLevels = -1; if ((fromLevelIndex >= 0) && (toLevelIndex >= 0)) { numLevels = Math.abs(toLevelIndex - fromLevelIndex) + 1; } GridDataset myDataset = getDataset(); if (myDataset == null) { return null; } Object extraCacheKey = null; GeoGrid geoGrid = findGridForDataChoice(myDataset, dataChoice); String paramName = dataChoice.getStringId(); if (geoGrid == null) { return null; } ucar.nc2.Dimension ensDim = geoGrid.getEnsembleDimension(); GeoSelection geoSelection = ((givenDataSelection != null) ? givenDataSelection.getGeoSelection() : null); boolean needVolume = ((geoGrid.getCoordinateSystem().getVerticalTransform() != null) && ((requestProperties != null) && (requestProperties.get( DerivedDataChoice.PROP_FROMDERIVED) != null))); // System.out.println("need volume = " + needVolume + " " + geoGrid.getCoordinateSystem().getVerticalTransform()); StringBuffer filename = new StringBuffer("grid_" + paramName); String regionOption = null; regionOption = givenDataSelection.getProperty(DataSelection.PROP_REGIONOPTION, DataSelection.PROP_USEDEFAULTAREA); boolean isProgressiveResolution = givenDataSelection.getProperty( DataSelection.PROP_PROGRESSIVERESOLUTION, false); if(!isProgressiveResolution && dataChoice.getDataSelection() != null){ isProgressiveResolution = dataChoice.getDataSelection().getProperty(DataSelection.PROP_PROGRESSIVERESOLUTION, false); } try { Range ensRange = makeRange(ensDim, null, 1); Range timeRange = null; Range levelRange = null; Range xRange = null; Range yRange = null; if ((fromLevelIndex >= 0) && (toLevelIndex >= 0) && !needVolume) { levelRange = new Range(fromLevelIndex, toLevelIndex); filename.append("_r_" + fromLevelIndex + "_" + toLevelIndex); } /* if(geoSelection != null){ LatLonPoint[] llp0 = geoSelection.getRubberBandBoxPoints(); if(llp0 != null){ if(isReload || (this.haveBeenUnPersisted)) { GeoLocationInfo gInfo = new GeoLocationInfo(llp0[0].getLatitude(), llp0[0].getLongitude(), llp0[1].getLatitude(), llp0[1].getLongitude()); geoSelection.setBoundingBox(gInfo); } } } */ if (isProgressiveResolution) { int xLenght = geoGrid.getXDimension().getLength(); int yLength = geoGrid.getYDimension().getLength(); if (geoSelection.getLatLonRect() != null) { // spatial subset or usedisplayarea LatLonRect bbox = geoSelection.getLatLonRect(); List yx_ranges = geoGrid.getCoordinateSystem().getRangesFromLatLonRect( bbox); yRange = makeRange(geoGrid.getYDimension(), (Range) yx_ranges.get(0), 1); xRange = makeRange(geoGrid.getXDimension(), (Range) yx_ranges.get(1), 1); yLength = yRange.length(); xLenght = xRange.length(); } Rectangle2D rect = geoSelection.getScreenBound(); int xstride = calculateStrideFactor(xLenght, (int) rect.getWidth()); int ystride = calculateStrideFactor(yLength, (int) rect.getHeight()); if (xstride == 1) { xstride = 0; } if (ystride == 1) { ystride = 0; } geoSelection.setXStride(xstride); geoSelection.setYStride(ystride); } System.out.println("new x y strides: " + geoSelection.getXStride() + " " + geoSelection.getYStride()); if ((geoSelection != null) && (geoSelection.hasSpatialSubset() || geoSelection.getHasNonOneStride())) { //TODO: We should determine the size of the subset grid and use that. //readingFullGrid = false; //System.err.println("subsetting using:" + geoSelection.getLatLonRect()); extraCacheKey = geoSelection; if (levelRange != null) { extraCacheKey = Misc.newList(extraCacheKey, levelRange); } filename.append("_x_" + geoSelection.getXStrideToUse()); filename.append("_y_" + geoSelection.getYStrideToUse()); filename.append("_z_" + geoSelection.getZStrideToUse()); if (geoSelection.getLatLonRect() != null) { LatLonRect bbox = geoSelection.getLatLonRect(); filename.append("_rect_" + cleanBBoxName(bbox)); List yx_ranges = geoGrid.getCoordinateSystem().getRangesFromLatLonRect( bbox); yRange = makeRange(geoGrid.getYDimension(), (Range) yx_ranges.get(0), geoSelection.getYStrideToUse()); xRange = makeRange(geoGrid.getXDimension(), (Range) yx_ranges.get(1), * geoSelection.getYStrideToUse()); } else if (geoSelection.getHasNonOneStride()) { yRange = makeRange(geoGrid.getYDimension(), yRange, geoSelection.getYStrideToUse()); xRange = makeRange(geoGrid.getXDimension(), xRange, geoSelection.getYStrideToUse()); } // Z stride is ignored if if ((levelRange != null) && geoSelection.hasZStride() && (geoSelection.getZStrideToUse() > 1)) { levelRange = new Range(fromLevelIndex, toLevelIndex, geoSelection.getZStrideToUse()); } // System.out.println("level range(1): " + levelRange); geoGrid = (GeoGrid) geoGrid.makeSubset(null, ensRange, null, levelRange, yRange, xRange); /* geoGrid = geoGrid.subset(null, levelRange, geoSelection.getLatLonRect(), geoSelection.getZStrideToUse(), geoSelection.getYStrideToUse(), geoSelection.getXStrideToUse()); */ } else if (levelRange != null) { extraCacheKey = levelRange; // System.out.println("level range(2): " + levelRange); //geoGrid = geoGrid.subset(null, levelRange, null, null); geoGrid = (GeoGrid) geoGrid.makeSubset(null, ensRange, null, levelRange, yRange, xRange); } } catch (InvalidRangeException ire) { throw new IllegalArgumentException("Invalid range:" + ire); } // check to see if user wants to be warned about download size boolean warn = getIdv().getStore().get(PREF_LARGE_REMOTE_DATA_WARN, false); boolean fromBundle = this.haveBeenUnPersisted; // just prior to loading data if ((readingFullGrid) && ( !fromBundle) && (warn)) { // check if interactive, if restoring from a bundle, and if file being loaded is remote if (getIdv().getInteractiveMode() && ( !isLocalFile())) { long total = 1; // get dimensions (note that the time dimension returned does not take into // account subsetting! List dims = geoGrid.getDimensions(); // grab spatial dimension indices. List geoDims = Arrays.asList(geoGrid.getXDimensionIndex(), geoGrid.getYDimensionIndex(), geoGrid.getZDimensionIndex()); for (int d = 0; d < geoDims.size(); d++) { if (geoDims.get(d) != -1) { ucar.nc2.Dimension dim = (ucar.nc2.Dimension) dims.get(geoDims.get(d)); total *= dim.getLength(); } } // check if there is a time dimension, and if so, take into account for number of points if (geoGrid.getTimeDimension() != null) { try { total *= givenDataSelection.getTimes().size(); } catch (NullPointerException npe) { // if use default is selected in field selector for time, then // the getTimes() on the given data source throws and NPE and we // need to go to geoGrid to get the number of times. Note that // that getTimes() on geoGrid does not reflect any temporal subsetting // which is why we check the givenDataSelection first... total *= geoGrid.getTimes().size(); } } // compute size in megabytes of request (minus overhead of network protocol) double mb = (total * geoGrid.getDataType().getSize()); mb = (mb / 1048576.); if (mb > SIZE_THRESHOLD) { JCheckBox askCbx = new JCheckBox("Don't show this again", !warn); JComponent msgContents = GuiUtils .vbox(GuiUtils .inset(new JLabel("You are about to load " + ((int) mb) + " MB of data.
Are you sure you want to do this?


" + "
Consider subsetting for better performance!

"), 5), GuiUtils .inset(askCbx, new Insets(5, 0, 0, 0))); /** * JComponent msgContents = * new JLabel( * "You are about to load " + ((int) mb) * + " MB of data.
Are you sure you want to do this?


" * + "
Consider subsetting for better performance!

"); */ if (askCbx.isSelected()) { getIdv().getStore().put(PREF_LARGE_REMOTE_DATA_WARN, false); } if ( !GuiUtils.askOkCancel( "Large Remote Data Request Warning", msgContents)) { throw new HugeSizeException(); } } } } GeoGridAdapter adapter = new GeoGridAdapter(this, geoGrid, dataChoice.getName(), dataset.getNetcdfDataset(), extraCacheKey); adapter.cacheFile = filename.toString(); return adapter; } /** * _more_ * * @param dataPoints _more_ * @param displayPoints _more_ * * @return _more_ */ public int calculateStrideFactor(int dataPoints, int displayPoints) { if (dataPoints <= displayPoints) { return 0; } else { int factor = (int) Math.floor((1.0 * dataPoints) / (1.0 * displayPoints) + 0.8); return factor; } } /** * Make a range for the given parameters * * @param dim The dimension to subset * @param range an existing subset * @param stride the stride * * @return the corresponding Range * * @throws InvalidRangeException not a valid range */ private Range makeRange(ucar.nc2.Dimension dim, Range range, int stride) throws InvalidRangeException { if (dim == null) { return null; } if (range == null) { range = new Range(0, dim.getLength() - 1, stride); } else { range = new Range(range.first(), range.last(), stride); } return range; } /** * Clean up the bounding box name so it can be used in a file name. * change : and + and any other strange chars to _ * * @param bbox bounding box * * @return cleaned up name */ private String cleanBBoxName(LatLonRect bbox) { String name = Util.cleanName(bbox.toString()); name = name.replaceAll(":", "_"); name = name.replaceAll("\\+", "_"); return name; } /** * Find the index of the given object in the list of levels. If its * a Real then check values * * @param o Object * @param levels levels * * @return index * * @throws VisADException On badness */ private int indexOf(Object o, List levels) throws VisADException { if (o instanceof TwoFacedObject) { o = ((TwoFacedObject) o).getId(); } if (o instanceof Integer) { return ((Integer) o).intValue(); } if (o instanceof String) { try { o = ucar.visad.Util.toReal(o.toString()); } catch (Exception ignoreThis) {} } if (o instanceof String) { String s = (String) o; if (s.startsWith("#")) { int index = new Integer(s.substring(1).trim()).intValue(); return index; } o = new Real(new Double(s).doubleValue()); } if ((o instanceof Real) && (levels.size() > 0) && (levels.get(0) instanceof Real)) { Real r = (Real) o; for (int i = 0; i < levels.size(); i++) { //TODO: Check if the units are convertible Real lr = (Real) levels.get(i); if (r.getValue(lr.getUnit()) == lr.getValue()) { return i; } } return -1; } return levels.indexOf(o); } /** * Make the FieldImpl corresponding to the DataChoice and * specified DataSelection (times). * * @param dataChoice DataChoice. * @param givenDataSelection specified selection criteria. * @param requestProperties request properties * * @return the grid of data corresponding to the choice * * @throws VisADException couldn't create Data object * @throws RemoteException couldn't create remote Data object */ private FieldImpl makeFieldImpl(DataChoice dataChoice, DataSelection givenDataSelection, Hashtable requestProperties) throws VisADException, RemoteException { long millis = System.currentTimeMillis(); List allLevels = getAllLevels(dataChoice, new DataSelection(GeoSelection.STRIDE_BASE)); Trace.call1("GeoGridDataSource.makeField"); Object fromLevel = givenDataSelection.getFromLevel(); Object toLevel = givenDataSelection.getToLevel(); int fromLevelIndex = -1; int toLevelIndex = -1; if ((fromLevel != null) && (toLevel != null)) { fromLevelIndex = indexOf(fromLevel, allLevels); // System.err.println ("fromLevel index:" + fromLevelIndex); toLevelIndex = indexOf(toLevel, allLevels); if ((toLevelIndex < 0) || (fromLevelIndex < 0)) { System.err.println("Did not find level indices: fromLevel:" + fromLevel + " index:" + fromLevelIndex + " toLevel:" + toLevel + " index:" + toLevelIndex + "\nLevels:" + allLevels); if ((allLevels != null) && !allLevels.isEmpty()) { System.err.println("fromLevel is a " + fromLevel.getClass().getName() + ", toLevel is a " + toLevel.getClass().getName()); System.err.println( "levels are " + allLevels.get(0).getClass().getName()); } } } long starttime = System.currentTimeMillis(); FieldImpl fieldImpl = null; //GridDataset myDataset = getDataset(); //if (myDataset == null) { // return null; //} //GeoGrid geoGrid = findGridForDataChoice(myDataset, dataChoice); //String paramName = dataChoice.getStringId(); Trace.call1("GeoGridDataSource.make GeoGridAdapter"); // System.err.println("levels:" + fromLevelIndex +" " + toLevelIndex); GeoGridAdapter adapter = null; try { adapter = makeGeoGridAdapter(dataChoice, givenDataSelection, requestProperties, fromLevelIndex, toLevelIndex, false); } catch (HugeSizeException hse) { return null; } if (adapter == null) { throw new BadDataException("Could not find field:" + dataChoice.getStringId()); } Trace.call2("GeoGridDataSource.make GeoGridAdapter"); GeoGrid geoGrid = adapter.getGeoGrid(); String paramName = dataChoice.getStringId(); Trace.call1("GeoGridDataSource.make times"); List times = getTimesFromDataSelection(givenDataSelection, dataChoice); // Datasource overrides data selection List members = getEnsembleSelection(); if (members == null) { members = (List) givenDataSelection.getProperty(PROP_ENSEMBLEMEMBERS); } int[] membersIndices = null; if (members != null) { int msize = members.size(); membersIndices = new int[msize]; for (int i = 0; i < msize; i++) { membersIndices[i] = ((Integer) members.get(i)).intValue(); } } int[] timeIndices = null; List allTimes = null; if (times != null) { timeIndices = new int[times.size()]; allTimes = getGeoGridTimes((CoordinateAxis1DTime) geoGrid .getCoordinateSystem().getTimeAxis1D()); int numTimes = allTimes.size(); if (holdsIndices(times)) { for (int i = 0; i < times.size(); i++) { int index = ((Integer) times.get(i)).intValue(); if (getReverseTimes()) { index = numTimes - index - 1; } timeIndices[i] = index; } } else { for (int i = 0; i < times.size(); i++) { int index = allTimes.indexOf(times.get(i)); if (getReverseTimes()) { index = numTimes - index - 1; } timeIndices[i] = index; } } } Trace.call2("GeoGridDataSource.make times"); /* System.err.print("times:"); for(int i=0;i 0)) { fieldImpl = adapter.getSequence(timeIndices, membersIndices, loadId); } else { fieldImpl = adapter.getSequence(timeIndices, loadId); } } if (fieldImpl == null) { // System.err.println ("data selection:" + givenDataSelection); // System.err.println ("data selection times:" + times); // System.err.println ("allTimes:" + allTimes); // Misc.printArray("timeIndices", timeIndices); } boolean useDriverTime = false; if (givenDataSelection != null) { useDriverTime = givenDataSelection.getProperty( DataSelection.PROP_USESTIMEDRIVER, false); } if ((givenDataSelection != null) && !times.isEmpty()) { CalendarDateTime t0 = new CalendarDateTime((DateTime) times.get(0)); CalendarDate dt0 = t0.getCalendarDate(); CalendarDateTime t1 = new CalendarDateTime((DateTime) times.get(times.size() - 1)); CalendarDate dt1 = t1.getCalendarDate(); dateRange = CalendarDateRange.of(dt0, dt1); } else { dateRange = null; } Trace.call2("GeoGridDataSource.getSequence"); // if made a non-null FlatField, this is displayable as 3D data; // if not, quit. if (fieldImpl == null) { if ( !JobManager.getManager().canContinue(loadId)) { return null; } LogUtil.userMessage(log_, "Unable to load field: " + paramName + " from:" + getFilePath(), true); return null; } JobManager.getManager().stopLoad(loadId); Trace.call2("GeoGridDataSource.makeField"); LogUtil.message(""); log_.debug("Read grid in " + (System.currentTimeMillis() - millis)); return fieldImpl; } // end makeField /** * Find the grid in the dataset from the DataChoice * * @param ds the grid dataset * @param dc the data choice * @return the GeoGrid or null dataset doesn't exist or if variable not found */ public GeoGrid findGridForDataChoice(GridDataset ds, DataChoice dc) { if (ds == null) { return null; } String name = dc.getStringId(); /** * * Look for new name for parameter * If name already exists in dataset, then the old name is returned * */ /** handles general variable renaming */ List newName = gribRenamer.matchNcepNames(ds, name); List userRemappedNames = DataManager.getNewVariableName(name); // check IDV variable renaming first if ((userRemappedNames != null) && !userRemappedNames.isEmpty()) { for (String remappedName : userRemappedNames) { GeoGrid tmpGeoGrid = ds.findGridByName(remappedName); if (tmpGeoGrid != null) { name = remappedName; dc.setId(name); dc.setName(name); return tmpGeoGrid; } } } // ok, match not found in the user remapping...let's try netCDF-Java's map if (newName.size() == 1) { // a unique name has been returned from netCDF-Java - use it! name = newName.get(0); } else if (newName.size() > 0) { // netCDF-Java returned more than one match (no match was found in the // IDV tables)...ask user which one is correct LogUtil.printMessage("Multiple Matches Found for " + name); LogUtil.printMessage("Possible new variable names are:"); List newDescription = new ArrayList(); for (String possibleNewName : newName) { LogUtil.printMessage(" " + possibleNewName); newDescription.add( ds.getDataVariable(possibleNewName).getDescription()); } LogUtil.printMessage("Please update your bundle."); if (getIdv().getInteractiveMode()) { //Misc.printStack("findgrid", 10); String msg1 = "The variable name has changed. Please select a new match.

"; String msg2 = "Possible new names for the variable " + dc.getDescription() + " are:

"; String msg3 = StringUtil.join("
", newDescription); String label = "" + msg1 + msg2 + "" + msg3 + ""; List cats = new ArrayList(); for (String possibleNewName : newName) { cats.add(new DataCategory("param:" + possibleNewName, false)); } DataOperand operand = new DataOperand(name, label, cats, false); DataTreeDialog dataDialog = new DataTreeDialog(getIdv(), null, Misc.newList(operand), Misc.newList(this), Misc.newList(dc), false); List choices = dataDialog.getSelected(); if ((choices != null) && (choices.size() > 0)) { DataChoice dc_new = (DataChoice) ((List) choices.get(0)).get(0); name = dc_new.getStringId(); dc.setId(name); dc.setName(name); GeoGrid geoGrid = ds.findGridByName(name); return geoGrid; } } } else { // ok, no matches found anywhere...return null return null; } dc.setId(name); dc.setName(name); GeoGrid geoGrid = ds.findGridByName(name); return geoGrid; } /** * Utility to check if we should ignore the given z axis * * @param zaxis given z axis * * @return Is ok */ protected boolean isZAxisOk(CoordinateAxis1D zaxis) { return GeoGridAdapter.isZAxisOk(zaxis); } /** * Override the base class method to return the times for the data choice * * @param dataChoice DataChoice in question * @return List of all times for that choice */ public List getAllDateTimes(DataChoice dataChoice) { return (List) timeMap.get(dataChoice.getId()); } /** * Create a DataChoice corresponding to the GeoGrid. This is the * workhorse of the initialization. * * @param cfield GeoGrid * @param allTimes set of times to use * @param timeToIndex a mapping of time to index * * @return corresponding DataChoice */ private DataChoice makeDataChoiceFromGeoGrid(GeoGrid cfield, List allTimes, Hashtable timeToIndex) { GridCoordSystem gcs = cfield.getCoordinateSystem(); LatLonRect llr = gcs.getLatLonBoundingBox(); LatLonPointImpl lleft = llr.getLowerLeftPoint(); LatLonPointImpl uright = llr.getUpperRightPoint(); double centerLat = lleft.getLatitude() + (uright.getLatitude() - lleft.getLatitude()) / 2.0; EarthLocation elt = null; Hashtable ht = new Hashtable(); try { elt = new EarthLocationTuple(centerLat, llr.getCenterLon(), 0); ht.put(IdvConstants.INITIAL_PROBE_EARTHLOCATION, elt); } catch (Exception e) {} //int zIndex = gcs.getZdim(); //int yIndex = gcs.getYdim(); //int xIndex = gcs.getXdim(); CoordinateAxis xaxis = gcs.getXHorizAxis(); CoordinateAxis yaxis = gcs.getYHorizAxis(); CoordinateAxis1D zaxis = gcs.getVerticalAxis(); // get dimensions of coordinate axes. long sizeZ = 0; if (zaxis != null) { sizeZ = (int) zaxis.getSize(); } Hashtable threeDProps = Misc.newHashtable(DataChoice.PROP_ICON, "/auxdata/ui/icons/3D.gif"); Hashtable twoDProps = Misc.newHashtable(DataChoice.PROP_ICON, "/auxdata/ui/icons/2D.gif"); if (ht != null) { threeDProps.putAll(ht); twoDProps.putAll(ht); } DirectDataChoice choice = null; if (sizeZ < 0) { log_.error(" weird Geogrid -- it claims size Z<0; parm " + cfield.getName()); return null; } else if ( !isZAxisOk(zaxis)) { // do not use grid with "Hybrid", potential temp or // boundary layer vertical axis coordinate } else { // might know how to handle this. String parmName = cfield.getName(); String pseudoName = parmName; String description = cfield.getDescription(); if ((description == null) || description.equals("")) { description = parmName; } CoordinateAxis1DTime tAxis = gcs.getTimeAxis1D(); List geoTimes = getGeoGridTimes(tAxis); timeMap.put(parmName, geoTimes); // List indexList = Misc.getIndexList(geoTimes, allTimes); List indexList = new ArrayList(); if ((geoTimes != null) && (allTimes != null)) { for (int i = 0; i < geoTimes.size(); i++) { Integer timeIndex = (Integer) timeToIndex.get(geoTimes.get(i)); indexList.add(timeIndex); } } // none or only one level, we'll call it a 2D grid DataSelection dataSelection = DataSelection.NULL; if (false && !indexList.isEmpty()) { dataSelection = new DataSelection(indexList, DataSelection.TIMESMODE_USETHIS); } List categories = null; Hashtable props = null; if ((sizeZ == 0) || (sizeZ == 1)) { //if (sizeZ == 0) { int xLength = cfield.getXDimension().getLength(); int yLength = cfield.getYDimension().getLength(); ucar.nc2.Dimension ensDim = cfield.getEnsembleDimension(); if (twoDDimensionsLabel == null) { twoDDimensionsLabel = "Total grid size: x: " + xLength + " y: " + yLength + " #points: " + (xLength * yLength); } props = new Hashtable(twoDProps); props.put(PROP_GRIDSIZE, new ThreeDSize(xLength, yLength)); if (geoTimes != null) { props.put(PROP_TIMESIZE, new Integer(geoTimes.size())); } if ((ensDim != null) && (ensDim.getLength() > 1)) { List ensMembers = null; CoordinateAxis1D eAxis = gcs.getEnsembleAxis(); int numEns = ensDim.getLength(); if ((ensMembers == null) && (eAxis != null)) { ensMembers = eAxis.getNames(); } int[] ids = new int[numEns]; String[] enames = new String[numEns]; for (int i = 0; i < numEns; i++) { ids[i] = i; NamedAnything na = (NamedAnything) ensMembers.get(i); if (isNumeric(na.toString())) { enames[i] = "Member " + na.toString(); } else { enames[i] = na.toString(); } } List ensSet = TwoFacedObject.createList(ids, enames); props.put(PROP_ENSEMBLEMEMBERS, ensSet); } if ((ensDim != null) && (ensDim.getLength() > 1)) { categories = (tAxis == null) ? getTwoDCategories() : getTwoDEnsTimeSeriesCategories(); } else { categories = (tAxis == null) ? getTwoDCategories() : getTwoDTimeSeriesCategories(); } /* choice = new DirectDataChoice(this, parmName, pseudoName, description, (taxis == null) ? getTwoDCategories() : getTwoDTimeSeriesCategories(), dataSelection, props); */ } else { // if (sizeZ > 1) // Have 3D field (we expect); usually sizeZ > 1: int xLength = cfield.getXDimension().getLength(); int yLength = cfield.getYDimension().getLength(); int zLength = cfield.getZDimension().getLength(); ucar.nc2.Dimension ensDim = cfield.getEnsembleDimension(); if (xLength * yLength * zLength > max3D) { max3D = xLength * yLength * zLength; max3DX = xLength; max3DY = yLength; max3DZ = zLength; } ThreeDSize size = new ThreeDSize(xLength, yLength, zLength); props = new Hashtable(threeDProps); props.put(PROP_GRIDSIZE, size); if (geoTimes != null) { props.put(PROP_TIMESIZE, new Integer(geoTimes.size())); } if ((ensDim != null) && (ensDim.getLength() > 1)) { List ensMembers = null; CoordinateAxis1D eAxis = gcs.getEnsembleAxis(); int numEns = ensDim.getLength(); if ((ensMembers == null) && (eAxis != null)) { ensMembers = eAxis.getNames(); } int[] ids = new int[numEns]; String[] enames = new String[numEns]; for (int i = 0; i < numEns; i++) { ids[i] = i; NamedAnything na = (NamedAnything) ensMembers.get(i); enames[i] = na.toString(); } List ensSet = TwoFacedObject.createList(ids, enames); props.put(PROP_ENSEMBLEMEMBERS, ensSet); } /* choice = new DirectDataChoice(this, parmName, pseudoName, description, (tAxis == null) ? getThreeDCategories() : getThreeDTimeSeriesCategories(), dataSelection, props); */ if ((ensDim != null) && (ensDim.getLength() > 1)) { categories = (tAxis == null) ? getThreeDCategories() : getThreeDEnsTimeSeriesCategories(); } else { categories = (tAxis == null) ? getThreeDCategories() : getThreeDTimeSeriesCategories(); } } // see if we have any categorization Attribute attr = null; for (int i = 0; (i < categoryAttributes.length) && (attr == null); i++) { attr = cfield.findAttributeIgnoreCase(categoryAttributes[i]); } if (attr != null) { String append = attr.getStringValue(); if (append != null) { append = append.replaceAll(DataCategory.DIVIDER, "_"); } DataCategory cat = (DataCategory) categories.get(0); cat = cat.copyAndAppend(append); List newCategories = new ArrayList(); newCategories.add(cat); for (int i = 1; i < categories.size(); i++) { newCategories.add(categories.get(i)); } categories = newCategories; } // see if we have any categorization Group group = null; VariableEnhanced variable = cfield.getVariable(); if (variable != null) { group = variable.getParentGroup(); if ((group != null) && !group.equals("")) { String append = group.getName(); if (append != null) { append = append.replaceAll("/", ""); append = append.replaceAll(DataCategory.DIVIDER, "_"); } DataCategory cat = (DataCategory) categories.get(0); cat = cat.copyAndAppend(append); List newCategories = new ArrayList(); newCategories.add(cat); for (int i = 1; i < categories.size(); i++) { newCategories.add(categories.get(i)); } categories = newCategories; } } choice = new DirectDataChoice(this, parmName, pseudoName, description, categories, dataSelection, props); } return choice; } /** * check if a input is only numeric number * * @param str is this numeric * * @return true if numeric */ public static boolean isNumeric(String str) { try { double d = Double.parseDouble(str); } catch (NumberFormatException nfe) { return false; } return true; } /** * make a list of DateTime-s from a GeoGrid timeAxis * * @param timeAxis - GeoGrid time CoordinateAxis * @return corresponding List of DateTime-s. */ private List getGeoGridTimes(CoordinateAxis1DTime timeAxis) { if ((timeAxis == null) || (timeAxis.getSize() == 0)) { return new ArrayList(0); } List times = (List) gcsVsTime.get(timeAxis); if (times != null) { return times; } try { times = DataUtil.makeDateTimes(timeAxis); gcsVsTime.put(timeAxis, times); } catch (Exception e) { System.out.println("getGeoGridTimes() " + e); } return times; } /** for test */ private static boolean forceSubset = false; /** for test */ public static boolean testMode = false; /* public void putCache(Object key, Object value) { if(testMode) return; super.putCache(key,value); }*/ /** * Test this class by running * "java ucar.unidata.data.grid.GeoGridDataSource " * * @param args filename * * @throws Exception some error occurred */ public static void main(String[] args) throws Exception { /* if (true) { int j=0; // int []bufferSizes = {100,250,500,750,1000,5000,8092}; int []bufferSizes = {500,500,500,500}; for(int i=0;i<100;i++) { // for(j=0;j<2;j++) { ucar.grib.grib2.Grib2BitMapSection.SKIPIT = (j==0); for(String arg: args) { // ucar.unidata.io.RandomAccessFile.BUFFERSIZE = bufferSizes[i]; ucar.unidata.io.RandomAccessFile.BUFFERSIZE = 500; GridDataset gds = GridDataset.open(arg); gds.close(); File gbxFile = new File(arg+".gbx"); gbxFile.delete(); } // } } return; } */ // dead dataset... String leadUrl = "dods://lead.unidata.ucar.edu:8080/thredds/dodsC/model/NCEP/NAM/CONUS_80km/NAM_CONUS_80km_20071002_1200.grib1"; // dead dataset... String mlodeUrl = "dods://motherlode.ucar.edu:8080/thredds/dodsC/model/NCEP/NAM/CONUS_80km/NAM_CONUS_80km_20071002_1200.grib1"; String url = ((args.length == 0) ? leadUrl : mlodeUrl); String[] urls = { url }; testMode = true; for (int i = 0; i < 10000; i++) { for (int urlIdx = 0; urlIdx < urls.length; urlIdx++) { System.err.println("Reading data:" + i + " " + urls[urlIdx]); GeoGridDataSource ggds = new GeoGridDataSource(null, urls[urlIdx], null); ggds.doMakeDataChoices(); DataChoice dataChoice = ggds.findDataChoice("Temperature"); if (dataChoice == null) { dataChoice = ggds.findDataChoice("T"); } // System.err.println ("" + dataChoice.getProperties()); ggds.makeFieldImpl(dataChoice, ggds.getDataSelection(), null); } } /* GridDataset dataset = GridDataset.open("elev.nc"); GeoGrid geoGrid = findGridForDataChoice(dataset, "foo"); GeoGrid geoGrid50 = geoGrid.subset(null, null, null, 0, 50, 50); GeoGrid geoGrid100 = geoGrid.subset(null, null, null, 0, 100, 100); System.exit(0); */ /** * * testMode = true; * forceSubset = false; * LogUtil.setTestMode(true); * LogUtil.startOutputBuffer(); * CacheManager.setDoCache(false); * * if (args.length == 0) { * System.out.println("Must supply a file name"); * System.exit(1); * } * boolean verbose = false; * StringBuffer titleBuffer = new StringBuffer(); * StringBuffer errors = new StringBuffer(); * StringBuffer buffer = new StringBuffer(); * boolean doAll = false; * boolean nextOneTitle = false; * List nots = new ArrayList(); * for (int i = 0; i < args.length; i++) { * if (nextOneTitle) { * titleBuffer.append(args[i] + "
"); * nextOneTitle = false; * continue; * } * * if (args[i].startsWith("-not:")) { * nots.add(args[i].substring(5)); * } * * if (args[i].equals("-verbose")) { * verbose = true; * continue; * } * if (args[i].equals("-title")) { * nextOneTitle = true; * continue; * } * if (args[i].equals("-doall")) { * doAll = true; * continue; * } * if (args[i].equals("-subset")) { * forceSubset = true; * buffer.append("Doing subsetting

\n"); * continue; * } * if (args[i].equals("-nosubset")) { * forceSubset = false; * buffer.append("Not doing subsetting

\n"); * continue; * } * boolean shouldProcess = true; * for (int notIdx = 0; notIdx < nots.size(); notIdx++) { * if (args[i].indexOf(nots.get(notIdx).toString()) >= 0) { * shouldProcess = false; * } * } * if ( !shouldProcess) { * continue; * } * * String name = "file" + i; * boolean fileOk = true; * StringBuffer fileBuffer = new StringBuffer(); * fileBuffer.append("


\nFile: " * + args[i] * + "
\n"); * DataChoice dataChoice = null; * GeoGridDataSource ggds = null; * try { * ggds = new GeoGridDataSource(null, args[i], null); * } catch (Throwable exc) { * errors.append("" + args[i] * + "
"); * fileBuffer.append("Failed to open file:"); * fileBuffer.append("
");
         *       fileBuffer.append(LogUtil.getStackTrace(exc));
         *       fileBuffer.append("
"); * fileBuffer.append("
\n"); * buffer.append(fileBuffer.toString()); * continue; * } * List dataChoices = ggds.getDataChoices(); * if (dataChoices.size() == 0) { * errors.append("" + args[i] * + "
"); * fileBuffer.append("No data choices\n"); * fileBuffer.append("\n"); * buffer.append(fileBuffer.toString()); * continue; * } * StringBuffer okBuffer = new StringBuffer(); * StringBuffer notokBuffer = new StringBuffer(); * boolean fieldOk = true; * LogUtil.println(args[i]); * for (int dcIdx = 0; fieldOk && (dcIdx < dataChoices.size()); * dcIdx++) { * if ( !doAll && (dcIdx > 0)) { * break; * } * // LogUtil.println("\tLoop:" + dcIdx); * * dataChoice = (DataChoice) dataChoices.get(dcIdx); * DataSelection dataSelection = ggds.getDataSelection(); * Data testData = null; * try { * testData = ggds.makeFieldImpl(dataChoice, dataSelection); * } catch (Throwable exc) { * fileOk = false; * fieldOk = false; * notokBuffer.append("Exception reading field:" * + dataChoice + ""); * notokBuffer.append(preText(LogUtil.getStackTrace(exc))); * } * String s = LogUtil.getOutputBuffer(true).trim(); * if (s.length() > 0) { * boolean showError = true; * if ( !verbose) { * List lines = StringUtil.split(s, "\n", true, true); * if (lines.size() == 1) { * if (s.indexOf("Unable to load field") >= 0) { * showError = false; * } * if (s.indexOf("Unknown unit") >= 0) { * showError = false; * } * } * } * * if (showError) { * fileOk = false; * notokBuffer.append("Reading: " * + dataChoice.getId() + "" * + preText(s)); * } * } else { * okBuffer.append(dataChoice.getId() + " "); * } * if (notokBuffer.indexOf("Exception") >= 0) { * notokBuffer.append("*** Stopping here ***"); * break; * } * * } * if ( !fileOk) { * errors.append("" + args[i] * + "
"); * } * if (okBuffer.toString().length() > 0) { * // fileBuffer.append("OK: " + okBuffer.toString() + "

"); * } * if (notokBuffer.toString().length() > 0) { * fileBuffer.append(notokBuffer.toString()); * } * fileBuffer.append("\n"); * if ((notokBuffer.toString().length() > 0) || !fileOk) { * buffer.append(fileBuffer.toString()); * } * } * LogUtil.stopOutputBuffer(); * System.out.println("

Geogrid test

\n"); * System.out.println(titleBuffer.toString()); * if (errors.toString().length() > 0) { * System.out.println("Errors:
"); * System.out.println(errors.toString()); * } * * System.out.println(buffer.toString()); * * System.exit(0); */ } /** * for test * * @param s string to format * @return formatted string */ private static String preText(String s) { s = StringUtil.replace(s, "<", "<"); s = StringUtil.replace(s, ">", ">"); s = StringUtil.replace(s, "\n", "
\n"); s = StringUtil.replace(s, "\t", " "); s = StringUtil.replace(s, " ", " "); return "
" + s + "
"; } /** * Set the FileNameOrUrl property. * * @param value The new value for FileNameOrUrl */ public void setFileNameOrUrl(String value) { oldSourceFromBundles = value; } /** * Apply the properties * * @return everything ok */ public boolean applyProperties() { if ( !super.applyProperties()) { return false; } if (reverseTimesCheckbox != null) { reverseTimes = reverseTimesCheckbox.isSelected(); } return true; } /** * Add the reverse times checkbox * * @return extra comp */ protected JComponent getExtraTimesComponent() { reverseTimesCheckbox = new JCheckBox("Reverse Times", reverseTimes); reverseTimesCheckbox.setToolTipText( "If you have selected the first time then really use the last time"); return GuiUtils.right(reverseTimesCheckbox); } /** * Set the ReverseTimes property. * * @param value The new value for ReverseTimes */ public void setReverseTimes(boolean value) { reverseTimes = value; } /** * Get the ReverseTimes property. * * @return The ReverseTimes */ public boolean getReverseTimes() { return reverseTimes; } }
File
GeoGridDataSource.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
        super.initializeViewMenu(viewMenu);
        if (isDisplay3D()) {
     *
<<<<<<< HEAD
/*
 * Copyright 1997-2013 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 * 
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package ucar.unidata.idv;


import ucar.unidata.collab.Sharable;
import ucar.unidata.data.GeoLocationInfo;
import ucar.unidata.data.grid.GridUtil;
import ucar.unidata.geoloc.ProjectionImpl;
import ucar.unidata.geoloc.ProjectionRect;

import ucar.unidata.gis.maps.MapData;
import ucar.unidata.gis.maps.MapInfo;
import ucar.unidata.idv.control.MapDisplayControl;
import ucar.unidata.idv.control.ZSlider;
import ucar.unidata.idv.flythrough.Flythrough;
import ucar.unidata.idv.flythrough.FlythroughPoint;
import ucar.unidata.idv.ui.ContourInfoDialog;
import ucar.unidata.idv.ui.EarthNavPanel;
import ucar.unidata.idv.ui.IdvUIManager;
import ucar.unidata.idv.ui.PipPanel;
import ucar.unidata.ui.Command;
import ucar.unidata.ui.FontSelector;
import ucar.unidata.util.BooleanProperty;
import ucar.unidata.util.ContourInfo;
import ucar.unidata.util.FileManager;
import ucar.unidata.util.GuiUtils;
import ucar.unidata.util.LogUtil;
import ucar.unidata.util.Misc;
import ucar.unidata.util.StringUtil;
import ucar.unidata.util.Trace;
import ucar.unidata.util.TwoFacedObject;
import ucar.unidata.view.geoloc.AxisScaleInfo;
import ucar.unidata.view.geoloc.GlobeDisplay;
import ucar.unidata.view.geoloc.LatLonAxisScaleInfo;
import ucar.unidata.view.geoloc.LatLonScalePanel;
import ucar.unidata.view.geoloc.MapProjectionDisplay;
import ucar.unidata.view.geoloc.NavigatedDisplay;
import ucar.unidata.view.geoloc.ViewpointInfo;
import ucar.unidata.xml.PreferenceManager;
import ucar.unidata.xml.XmlObjectStore;
import ucar.unidata.xml.XmlResourceCollection;
import ucar.unidata.xml.XmlUtil;

import ucar.visad.GeoUtils;
import ucar.visad.ProjectionCoordinateSystem;
import ucar.visad.display.DisplayMaster;
import ucar.visad.display.LineDrawing;

import visad.ConstantMap;
import visad.ContourControl;
import visad.Data;
import visad.DisplayEvent;
import visad.DisplayRealType;
import visad.FieldImpl;
import visad.FlatField;
import visad.MouseBehavior;
import visad.Real;
import visad.RealTupleType;
import visad.RealType;
import visad.VisADException;

import visad.georef.EarthLocation;
import visad.georef.EarthLocationTuple;
import visad.georef.LatLonPoint;
import visad.georef.MapProjection;
import visad.georef.TrivialMapProjection;


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.Rectangle2D;

import java.rmi.RemoteException;

import java.text.Collator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
import java.util.TreeSet;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;





/**
 * A wrapper around a MapProjectDisplay display master.
 * Provides an interface for managing user interactions, gui creation, etc.
 *
 * @author IDV development team
 */

public class MapViewManager extends NavigatedViewManager {

    /** front globe clipping distance */
    public static final String PROP_CLIPDISTANCE_GLOBE_FRONT =
        "idv.clipdistance.globe.front";

    /** back globe clipping distance */
        }
    public static final String PROP_CLIPDISTANCE_GLOBE_BACK =
        "idv.clipdistance.globe.back";

    /** fron map clipping distance */
    public static final String PROP_CLIPDISTANCE_MAP_FRONT =
        "idv.clipdistance.map.front";

    /** back map clipping distance */
    public static final String PROP_CLIPDISTANCE_MAP_BACK =
        "idv.clipdistance.map.back";


    /** flythrough command */
    public static final String CMD_FLY_LEFT = "cmd.fly.left";

    /** flythrough command */
    public static final String CMD_FLY_RIGHT = "cmd.fly.right";

    /** flythrough command */
    public static final String CMD_FLY_FORWARD = "cmd.fly.forward";

    /** flythrough command */
    public static final String CMD_FLY_BACK = "cmd.fly.back";

    /** preference id for the list of addresses in the geocode dialog */
    public static final String PREF_ADDRESS_LIST = "view.address.list";

    /** do we reproject when we go to an address */
    public static final String PREF_ADDRESS_REPROJECT =
        "view.address.reproject";

    /** Preference for autorotate in globe mode */
    public static final String PREF_AUTOROTATE = "View.AutoRotate";

    /** Preference for  showing display in perspective view_ */
    public static final String PREF_PERSPECTIVEVIEW = "View.PerspectiveView";

    /** Preference for  default projection */
    public static final String PREF_PROJ_DFLT = "View.ProjectionDflt";

    /** Preference for  setting projection automatically from data_ */
    public static final String PREF_PROJ_USEFROMDATA = "View.UseFromData";

    /** Preference for  showing the pip */
    public static final String PREF_SHOWPIP = "View.ShowPip";

    /** Preference for progressive resolution */
    public static final String PREF_USE_PROGRESSIVE_RESOLUTION = 
    	"View.UseProgressiveResolution";

    /** label for progressive resolution/disclosure/whatever we call it */
    private static final String PR_LABEL = "Use Progressive Disclosure";
    
    /** Preference for showing the globe background */
    public static final String PREF_SHOWGLOBEBACKGROUND =
        "View.ShowGlobeBackground";

    /** Preference for the globe background  color */
    public static final String PREF_GLOBEBACKGROUND = "View.GlobeBackground";

    /** Preference for  showing the earth nav panel */
    public static final String PREF_SHOWEARTHNAVPANEL =
        "View.ShowEarthNavPanel";


    /** Defines the projection when sharing state */
    public static final String SHARE_PROJECTION =
        "MapViewManager.SHARE_PROJECTION";

    /**
     * This got set from the ViewManager properties. It is a comma
     * delimited list of map resources
     */
    private String initialMapResources;


    /** The display projection we are currently using */
    private MapProjection mainProjection;

    /** The name of the display projection we are currently using */
    private String mainProjectionName = null;

    /** The name of the default projection */
    private String defaultProjectionName = null;


    /** Keep track of the projections we have used */
    private ArrayList projectionHistory = new ArrayList();

    /** Main projections menu */
    private JMenu projectionsMenu;

    /** Big blob of xml map state from the map widget */
    private String mapState;

    /** Are we using the globe display */
    private boolean useGlobeDisplay = false;

    /** Are we 2d or 3d */
    private boolean use3D = true;

    /** The earth nav panel */
    EarthNavPanel earthNavPanel;

    /** Where the earth nav panel goes */
    JPanel earthNavPanelWrapper;

    /** The map panel in the GUI */
    private PipPanel pipPanel;

    /** mutex for dealing with the pip map */
    private Object PIP_MUTEX = new Object();

    /** Holds the pip map */
    private JComponent pipPanelWrapper;


    /** Do we reproject when we goto address */
    private static JCheckBox addressReprojectCbx;

    /** For checking if kmz capture is ok */
    private JCheckBox fixViewpointCbx;

    /** For checking if kmz capture is ok */
    private JCheckBox fixProjectionCbx;

    /** rotate button */
    JToggleButton rotateBtn;

    /** contour info dialog for preferences */
    ContourInfoDialog cid;

    /** background color for filled globe */
    private Color globeBackgroundColor = null;


    /** z level (really radius) for where to put the globe fill layer */
    private double globeBackgroundLevel = -0.01;

    /** globe fill background stuff */
    private LineDrawing globeBackgroundDisplayable;

    /** globe fill background stuff */
    private JComponent globeBackgroundColorComp;

    /** globe fill background stuff */
    private ZSlider globeBackgroundLevelSlider;


    /** The flythrough */
    private Flythrough flythrough;

    /** show maps flag */
    private boolean showMaps = true;

    /** init maps flag */
    private String initMapPaths;

    /** initial map width */
    private float initMapWidth = -1;

    /** initial map color */
    private Color initMapColor = null;

    /** initial lat/lon visibility */
    private boolean initLatLonVisible = false;

    /** initial lat/lon width */
    private int initLatLonWidth = 1;

    /** initial lat/lon spacing */
    private float initLatLonSpacing = 15;

    /** initial lat/lon color */
    private Color initLatLonColor = Color.white;

    /** initial lat/lon bounds */
    private Rectangle2D.Float initLatLonBounds;

    /** use default globe background flag */
    private boolean defaultGlobeBackground = false;

    /** initial display projection zoom */
    private double displayProjectionZoom = 0;

    /** do not set projection flag */
    private boolean doNotSetProjection = false;

    /** lat/lon scale widget */
    private LatLonScalePanel latLonScaleWidget;

    /** Lat axis scale info for unpersistence */
    private LatLonAxisScaleInfo latAxisScaleInfo;

    /** Lon axis scale info for unpersistence */
    private LatLonAxisScaleInfo lonAxisScaleInfo;

    /**
     *  Default constructor
     */
    public MapViewManager() {}


    /**
     * Construct a MapViewManager from an IDV
     *
     * @param viewContext Really the IDV
     */
    public MapViewManager(ViewContext viewContext) {
        super(viewContext);
    }

    /**
     * Construct a MapViewManager with the specified params
     * @param viewContext   context in which this MVM exists
     * @param desc   ViewDescriptor
     * @param properties   semicolon separated list of properties (can be null)
     *
     * @throws RemoteException Java RMI problem
     * @throws VisADException  Couldn't create the VisAD object
     */
    public MapViewManager(ViewContext viewContext, ViewDescriptor desc,
                          String properties)
            throws VisADException, RemoteException {
        super(viewContext, desc, properties);
    }


    /**
     * Get the default projection to use
     *
     * @return The default projection
     */
    public ProjectionImpl getDefaultProjection() {
        return getIdv().getIdvProjectionManager().getDefaultProjection();
    }




    /**
     * Make the DisplayMaster for this ViewManager
     *
     * @return the DisplayMaster
     *
     * @throws RemoteException Java RMI problem
     * @throws VisADException  Couldn't create the VisAD object
     */
    protected DisplayMaster doMakeDisplayMaster()
            throws VisADException, RemoteException {

        StateManager         stateManager = getStateManager();
        IntegratedDataViewer idv          = getIdv();
        if ((idv == null) || (stateManager == null)) {
            return null;
        }
        boolean mode3d = stateManager.getProperty(IdvConstants.PROP_3DMODE,
                             use3D);
        mode3d = getStore().get(PREF_DIMENSION, mode3d);
        // let property override the preference
        use3D = mode3d && use3D;
        int mode = (use3D
                    ? NavigatedDisplay.MODE_3D
                    : NavigatedDisplay.MODE_2Din3D);

        if ( !visad.util.Util.canDoJava3D()) {
            mode = NavigatedDisplay.MODE_2D;
        }

        boolean useGlobe = getUseGlobeDisplay()
                           && (mode != NavigatedDisplay.MODE_2D);

        Dimension dimension = stateManager.getViewSize();
        if (dimension == null) {
            if ((getFullScreenWidth() > 0) && (getFullScreenHeight() > 0)) {
                dimension = new Dimension(getFullScreenWidth(),
                                          getFullScreenHeight());
            } else if (displayBounds != null) {
                dimension = new Dimension(displayBounds.width,
                                          displayBounds.height);
            }
        }

        if ((dimension == null) || (dimension.width == 0)
                || (dimension.height == 0)) {
            dimension = null;
        }

        NavigatedDisplay navDisplay = null;


        if (useGlobe) {
            //TODO: Set the dimension
            GlobeDisplay globeDisplay =
                new GlobeDisplay(idv.getArgsManager().getIsOffScreen(),
                                 dimension, null);
            globeDisplay.setClipDistanceFront(
                getStateManager().getProperty(
                    PROP_CLIPDISTANCE_GLOBE_FRONT,
                    NavigatedDisplay.CLIP_FRONT_DEFAULT));
            globeDisplay.setClipDistanceBack(
                getStateManager().getProperty(
                    PROP_CLIPDISTANCE_GLOBE_BACK,
                    NavigatedDisplay.CLIP_BACK_DEFAULT));
            navDisplay = globeDisplay;
            setGlobeBackground(globeDisplay);
            navDisplay.setPolygonOffset(
                getStateManager().getProperty("idv.globe.polygonoffset", 1));
            navDisplay.setPolygonOffsetFactor(
                getStateManager().getProperty(
                    "idv.globe.polygonoffsetfactor", 1));
        } else {
            Trace.call1("MapViewManager.doMakeDisplayMaster projection");
            if (mainProjection == null) {
                if (initLatLonBounds != null) {
                    doNotSetProjection = true;
                    mainProjection =
                        ucar.visad.Util
                            .makeMapProjection(initLatLonBounds.getY()
                                - initLatLonBounds
                                    .getHeight(), initLatLonBounds.getX(),
                                        initLatLonBounds.getY(),
                                        initLatLonBounds.getX()
                                        + initLatLonBounds.getWidth(), false);


                } else {
                    ProjectionImpl dfltProjection = null;
                    if (defaultProjectionName != null) {
                        dfltProjection =
                            idv.getIdvProjectionManager()
                                .findProjectionByName(defaultProjectionName);
                        if (dfltProjection != null) {
                            doNotSetProjection = true;
                        }
                    }

                    if (dfltProjection == null) {
                        dfltProjection = getDefaultProjection();
                    }
                    mainProjection =
                        new ProjectionCoordinateSystem(dfltProjection);
                }
            }
            if ( !idv.getArgsManager().isScriptingMode()) {
                addProjectionToHistory(mainProjection, "Default");
            }

            Trace.call1("MapViewManager.new MPD");
            MapProjectionDisplay mapDisplay =
                MapProjectionDisplay.getInstance(mainProjection, mode,
                    idv.getArgsManager().getIsOffScreen(), dimension);
            Trace.call2("MapViewManager.new MPD");

            mapDisplay.setClipDistanceFront(
                getStateManager().getProperty(
                    PROP_CLIPDISTANCE_MAP_FRONT,
                    NavigatedDisplay.CLIP_FRONT_DEFAULT));
            mapDisplay.setClipDistanceBack(
                getStateManager().getProperty(
                    PROP_CLIPDISTANCE_MAP_BACK,
                    NavigatedDisplay.CLIP_BACK_DEFAULT));


            if (initLatLonBounds == null) {
                double[] aspect = getAspectRatio();
                if (aspect == null) {
                    aspect = new double[] { 1.0, 1.0, 0.4 };
                }
                mapDisplay.setDisplayAspect((mode == NavigatedDisplay.MODE_2D)
                                            ? new double[] { aspect[0],
                        aspect[1] }
                                            : aspect);
            } else {
                //If we have a a latlonbounds then we want to display exactly that area
                double[] aspect = new double[] { 1.0,
                        initLatLonBounds.getHeight()
                        / initLatLonBounds.getWidth(),
                        1.0 };
                double[] scaleMatrix =
                    mapDisplay.getMouseBehavior().make_matrix(0.0, 0.0, 0.0,
                        aspect[0], aspect[1], 1, 0.0, 0.0, 0.0);

                //                mapDisplay.setProjectionMatrix(scaleMatrix);
            }


            navDisplay = mapDisplay;
            navDisplay.setPerspectiveView(getPerspectiveView());

            if ((defaultProjectionName != null)
                    && (displayProjectionZoom != 0)) {
                navDisplay.zoom(displayProjectionZoom);
            }
            defaultProjectionName = null;
            initLatLonBounds      = null;

            Trace.call2("MapViewManager.doMakeDisplayMaster projection");
            navDisplay.setPolygonOffset(
                getStateManager().getProperty("idv.map.polygonoffset", 1));
            navDisplay.setPolygonOffsetFactor(
                getStateManager().getProperty(
                    "idv.map.polygonoffsetfactor", 1));
        }





        return navDisplay;
    }



    /**
     * Get the earth location of the screen center
     *
     * @return screen center
     *
     * @throws RemoteException On badness
     * @throws VisADException On badness
     */
    public EarthLocation getScreenCenter()
            throws VisADException, RemoteException {
        return getNavigatedDisplay().getEarthLocation(
            getNavigatedDisplay().getScreenCenter());
    }





    /**
     * Get a list of named locations of the different points of the view rectangle. e.g., center, upper left, etc.
     *
     * @return list of locations
     *
     * @throws RemoteException On badness
     * @throws VisADException On badness
     */
    public List getScreenCoordinates()
            throws VisADException, RemoteException {
        List l = getNavigatedDisplay().getScreenCoordinates();
        List result = new ArrayList();
        for (TwoFacedObject tfo : l) {
            result.add(
                new TwoFacedObject(
                    tfo.toString(),
                    getNavigatedDisplay().getEarthLocation(
                        (double[]) tfo.getId())));
        }
        return result;
    }


    /**
     * Initialize this object.
     *
     * @throws RemoteException
     * @throws VisADException
     */
    protected void init() throws VisADException, RemoteException {
        if (getHaveInitialized()) {
            return;
        }
        super.init();
        Trace.call1("MapViewManager.init checkDefaultMap",
                    " showMap:" + showMaps);

        checkDefaultMap();
        Trace.call2("MapViewManager.init checkDefaultMap");

        if (useGlobeDisplay) {
            //            if(!hasBooleanProperty(PREF_SHOWGLOBEBACKGROUND)) {
            initializeBooleanProperty(
                new BooleanProperty(
                    PREF_SHOWGLOBEBACKGROUND, "Show Globe Background",
                    "Show Globe Background", defaultGlobeBackground));
            //            }
        }





    }


    /**
     * Initialize the UI
     */
    protected void initUI() {
        super.initUI();
        //Initialize the flythrough here
        if (flythrough != null) {
            flythrough.init(this);
            if (flythrough.getShown()) {
                flythrough.show();
            }
        }
    }


    /**
     * Fill the legends
     */
    protected void fillLegends() {
        super.fillLegends();
        if (flythrough != null) {
            flythrough.displayControlChanged();
        }
    }

    /**
     * Handle a perspective view change
     *
     * @param v the value
     */
    protected void perspectiveViewChanged(boolean v) {
        setPerspectiveView(v);
        super.perspectiveViewChanged(v);
        notifyDisplayControls(PREF_PERSPECTIVEVIEW);
    }

    /**
     * Handle a vertical scale change
     * @deprecated see {@link #verticalRangeChanged()}
     */
    protected void verticalScaleChanged() {
        verticalRangeChanged();
    }

    /**
     * Handle a vertical range change
     */
    protected void verticalRangeChanged() {
        super.verticalRangeChanged();
        notifyDisplayControls(SHARE_PROJECTION);
    }

    /**
     * Should we animate view changes
     *
     * @return true if not running ISL
     */
    public boolean shouldAnimateViewChanges() {
        return !getStateManager().getRunningIsl();
    }

    /**
     * Handle the event
     *
     * @param event The event
     *
     * @throws RemoteException On badness
     * @throws VisADException On badness
     */
    public void displayChanged(DisplayEvent event)
            throws VisADException, RemoteException {
        if (getIsDestroyed()) {
            return;
        }

        checkPipPanel();

        if (flythrough != null) {
            flythrough.displayChanged(event);
        }


        NavigatedDisplay navDisplay = getMapDisplay();
        if ( !navDisplay.getAutoRotate()
                && getViewpointControl().getAutoRotate()) {
            getViewpointControl().setAutoRotate(false);
        }

        int        id         = event.getId();
        InputEvent inputEvent = event.getInputEvent();
        if ((id == DisplayEvent.KEY_PRESSED)
                && (inputEvent instanceof KeyEvent)) {
            KeyEvent keyEvent = (KeyEvent) inputEvent;
            if (GuiUtils.isControlKey(keyEvent, KeyEvent.VK_N)) {
                EarthLocation center = getScreenCenter();
                getMapDisplay().centerAndZoom(center, null, 1.0,
                        shouldAnimateViewChanges(), true);
                return;
            }

            if (GuiUtils.isControlKey(keyEvent, KeyEvent.VK_S)) {
                EarthLocation center = getScreenCenter();
                getMapDisplay().centerAndZoom(center, null, 1.0,
                        shouldAnimateViewChanges(), false);
                return;
            }


            if (GuiUtils.isControlKey(keyEvent)
                    && ((keyEvent.getKeyCode() == KeyEvent.VK_H)
                        || (keyEvent.getKeyCode() == KeyEvent.VK_J)
                        || (keyEvent.getKeyCode() == KeyEvent.VK_K)
                        || (keyEvent.getKeyCode() == KeyEvent.VK_L))) {
                double[] matrix = getProjectionControl().getMatrix();
                double[] rot   = new double[3];
                double[] scale = new double[3];
                double[] trans = new double[3];
                MouseBehavior mouseBehavior =
                    getNavigatedDisplay().getMouseBehavior();
                mouseBehavior.instance_unmake_matrix(rot, scale, trans,
                        matrix);
                double[] t = null;
                if (keyEvent.getKeyCode() == KeyEvent.VK_H) {
                    t = mouseBehavior.make_matrix(-5, 0.0, 0, 1.0, 0.0, 0.0,
                            0.0);
                } else if (keyEvent.getKeyCode() == KeyEvent.VK_J) {
                    t = mouseBehavior.make_matrix(5, 0.0, 0, 1.0, 0.0, 0.0,
                            0.0);
                } else if (keyEvent.getKeyCode() == KeyEvent.VK_K) {
                    t = mouseBehavior.make_matrix(0, -5.0, 0, 1.0, 0.0, 0.0,
                            0.0);
                } else if (keyEvent.getKeyCode() == KeyEvent.VK_L) {
                    t = mouseBehavior.make_matrix(0, 5.0, 0, 1.0, 0.0, 0.0,
                            0.0);
                }
                matrix = mouseBehavior.multiply_matrix(t, matrix);
                getMaster().setProjectionMatrix(matrix);

                return;
            }
        }


        super.displayChanged(event);
    }



    /**
     * Handle the mouse flicked
     *
     * @param startPoint  start point of flick
     * @param endPoint  end point of flick
     * @param startMatrix the start matrix
     * @param endMatrix the end matrix
     * @param speed  the speed of flicking
     */
    protected void mouseFlicked(Point startPoint, Point endPoint,
                                double[] startMatrix, double[] endMatrix,
                                double speed) {
        if ( !getUseGlobeDisplay()) {
            return;
        }

        double[] trans = { 0.0, 0.0, 0.0 };
        double[] rot1  = { 0.0, 0.0, 0.0 };
        double[] rot2  = { 0.0, 0.0, 0.0 };
        double[] scale = { 0.0, 0.0, 0.0 };
        getNavigatedDisplay().getMouseBehavior().instance_unmake_matrix(rot1,
                scale, trans, startMatrix);
        getNavigatedDisplay().getMouseBehavior().instance_unmake_matrix(rot2,
                scale, trans, endMatrix);

        //If there was no rotation then return
        if ((rot1[0] == rot2[0]) && (rot1[1] == rot2[1])) {
            return;
        }

        double distance = GuiUtils.distance(startPoint.x, startPoint.y,
                                            endPoint.x, endPoint.y);
        if (distance == 0) {
            return;
        }
        double percentX = (endPoint.x - startPoint.x) / distance;
        double percentY = (endPoint.y - startPoint.y) / distance;
        speed *= 2;
        getNavigatedDisplay().setRotationMultiplierMatrix(speed * -percentY,
                speed * -percentX, 0.0);
        getViewpointControl().setAutoRotate(true);
    }


    /**
     * Check if its ok to capture a kmz file
     *
     * @return ok to capture kmz
     */
    protected boolean checkForKmlImageCapture() {

        //Assume when we are running isl everything is ok
        if (getIdv().getArgsManager().getIsOffScreen()) {
            return true;
        }

        NavigatedDisplay navDisplay = getMapDisplay();
        );
        double[]         rotMatrix  = navDisplay.getRotation();
        if ((rotMatrix[0] != 0) || (rotMatrix[1] != 0)
                || (rotMatrix[2] != 0)) {
            if (fixViewpointCbx == null) {
                fixViewpointCbx = new JCheckBox("Fix it", true);
            }

            JComponent question =
                GuiUtils
                    .vbox(new JLabel(
                        "The viewpoint is not overhead. This will result in an incorrect image capture."), GuiUtils

                            .left(fixViewpointCbx));
            if ( !GuiUtils.askOkCancel("KML Capture", question)) {
                return false;
            }
            if (fixViewpointCbx.isSelected()) {
                try {
                    navDisplay.resetProjection();
                } catch (Exception exc) {
                    throw new RuntimeException(exc);
                }
            } else {
                return true;
            }
        }


        int cnt = 0;
        while (true) {
            cnt++;
            Rectangle sb = navDisplay.getDisplayComponent().getBounds();
            LatLonPoint ul =
                getMapDisplay().getEarthLocation(
                    getMapDisplay().getSpatialCoordinatesFromScreen(
                        0, 0)).getLatLonPoint();
            LatLonPoint ur =
                getMapDisplay().getEarthLocation(
                    getMapDisplay().getSpatialCoordinatesFromScreen(
                        sb.width, 0)).getLatLonPoint();
            LatLonPoint lr =
                getMapDisplay().getEarthLocation(
                    getMapDisplay().getSpatialCoordinatesFromScreen(
                        sb.width, sb.height)).getLatLonPoint();
            LatLonPoint ll =
                getMapDisplay().getEarthLocation(
                    getMapDisplay().getSpatialCoordinatesFromScreen(
                        0, sb.height)).getLatLonPoint();

            double width = Math.abs(ul.getLongitude().getValue()
                                    - ur.getLongitude().getValue());

            double height = Math.abs(ul.getLatitude().getValue()
                                     - ll.getLatitude().getValue());


            boolean projOk = true;

            if ( !isClose(width, ul.getLongitude().getValue(),
                          ll.getLongitude().getValue())) {
                projOk = false;
            }
            if ( !isClose(width, ur.getLongitude().getValue(),
                          lr.getLongitude().getValue())) {
                projOk = false;
            }
            if ( !isClose(height, ul.getLatitude().getValue(),
                          ur.getLatitude().getValue())) {
                projOk = false;
            }
            if ( !isClose(height, ll.getLatitude().getValue(),
                          lr.getLatitude().getValue())) {
                projOk = false;
            }

            if (projOk) {
                return true;
            }

            if (fixProjectionCbx == null) {
                fixProjectionCbx = new JCheckBox("Fix it", true);
            }
            String msg = ((cnt == 1)
                          ? "The projection is not lat/lon. This will result in an incorrect image capture."
                          : "For some reason the projection is still not lat/lon.");
            JComponent question = GuiUtils.vbox(new JLabel(msg),
                                      GuiUtils.left(fixProjectionCbx));
            if ( !GuiUtils.askOkCancel("KML Capture", question)) {
                return false;
            }

            if ( !fixProjectionCbx.isSelected()) {
                return true;
            }
            if (fixProjectionCbx.isSelected()) {
                try {
                    setCurrentAsProjection();
                } catch (Exception exc) {
                    throw new RuntimeException(exc);
                }
                Misc.sleep(1000);
            }
        }

    }



    /**
     * are the 2 values close
     *
     * @param span the range
     * @param value1 value 1
     * @param value2 value 2
     *
     * @return are the 2 values close
     */
    private boolean isClose(double span, double value1, double value2) {
        //Check that the difference of the two values is < 1% of the given span value
        if (Math.abs((value1 - value2) / span) > 0.01) {
            return false;
        }
        return true;
    }


    /**
     * Check for the default map
     */
    private void checkDefaultMap() {
        if ( !showMaps) {
            return;
        }
        MapDisplayControl defaultMap = findDefaultMap();
        if (defaultMap == null) {
            try {
                ControlDescriptor mapCD =
                    getIdv().getControlDescriptor(
                        ControlDescriptor.DISPLAYID_MAP);
                if (mapCD == null) {
                    return;
                }

                MapInfo mapInfo;
                if (mapState != null) {
                    Trace.call1("checkDefaultMap-1");
                    mapInfo = new MapInfo(XmlUtil.getRoot(mapState));
                    mapInfo.setJustLoadedLocalMaps(true);
                    Trace.call2("checkDefaultMap-1");
                    //SKIP the initial map resources for now
                } else if (false && (initialMapResources != null)) {
                    //This got set from the ViewManager properties. It is a comma
                    //delimited list of map resources 
                    Trace.call1("checkDefaultMap-2");
                    List resources = getResourceManager().getResourcePaths(
                                         StringUtil.split(
                                             initialMapResources, ",", true,
                                             true));
                    XmlResourceCollection customMapResources =
                        new XmlResourceCollection("custom maps");
                    customMapResources.addResources(resources);
                    mapInfo = new MapInfo(customMapResources, true, true);
                    Trace.call2("checkDefaultMap-2");
                } else {
                    XmlResourceCollection xrc =
                        getResourceManager().getMapResources(
                            getUseGlobeDisplay());
                    //                    XmlResourceCollection xrc = getResourceManager().getMapResources(false);
                    mapInfo = new MapInfo(xrc, false, true);
                }

                if (initLatLonVisible) {
                    mapInfo.getLatData().setVisible(true);
                    mapInfo.getLatData().setLineWidth(initLatLonWidth);
                    mapInfo.getLatData().setSpacing(initLatLonSpacing);
                    mapInfo.getLatData().setColor(initLatLonColor);

                    mapInfo.getLonData().setVisible(true);
                    mapInfo.getLonData().setLineWidth(initLatLonWidth);
                    mapInfo.getLonData().setSpacing(initLatLonSpacing);
                    mapInfo.getLonData().setColor(initLatLonColor);
                }

                if (initMapPaths != null) {
                    for (MapData mapData : mapInfo.getMapDataList()) {
                        mapData.setVisible(false);
                        if (initMapWidth > 0) {
                            mapData.setLineWidth(initMapWidth);
                        }
                        if (initMapColor != null) {
                            mapData.setColor(initMapColor);
                        }
                    }
                    for (String mapPath :
                            StringUtil.split(initMapPaths, ",", true, true)) {
                        for (MapData mapData : mapInfo.getMapDataList()) {
                            if (Misc.equals(mapData.getSource(), mapPath)) {

                                mapData.setVisible(true);
                            }
                        }
                    }
                }

                Trace.call1("checkDefaultMap-making map");
                defaultMap = new MapDisplayControl(this, mapInfo);
                defaultMap.setIsDefaultMap(true);
                Hashtable newProperties =
                    new Hashtable(mapCD.getProperties());
                newProperties.put("displayName", "Default Background Maps");
                mapCD.initControl(defaultMap, new ArrayList(), getIdv(),
                                  newProperties, null);
                Trace.call2("checkDefaultMap-making map");
            } catch (Exception exc) {
                logException("Initializing maps", exc);
            }
        }
    }




    /**
     * Can this view manager be used in exchange for the given view manager
     *
     * @param that The other view manager to check
     * @return Can this be used in place of that
     */
    public boolean canBe(ViewManager that) {
        if ( !super.canBe(that)) {
            return false;
        }
        MapViewManager mvm = (MapViewManager) that;
        if (this.getUseGlobeDisplay() != mvm.getUseGlobeDisplay()) {
            return false;
        }
        if (this.getUse3D() != mvm.getUse3D()) {
            return false;
        }
        return true;
    }




    /**
     * Initialize with another view
     *
     * @param viewState  the view state
     *
     * @throws Exception  problems
     */
    public void initWith(ViewState viewState) throws Exception {

        MapProjection thatProjection =
            (MapProjection) viewState.get(ViewState.PROP_PROJECTION);
        if (thatProjection != null) {
            setMapProjection(thatProjection, false, "Projection");
        }
        double[] aspect =
            (double[]) viewState.get(ViewState.PROP_ASPECTRATIO);
        if (aspect != null) {
            this.setAspectRatio(aspect);
        }

        super.initWith(viewState);
    }

    /**
     * Handle the animation time changed
     */
    protected void animationTimeChanged() {
        super.animationTimeChanged();
        if (flythrough != null) {
            flythrough.animationTimeChanged();
        }
    }


    /**
     * Initialize this object's state with the state from that.
     *
     * @param that The other obejct to get state from
     * @param ignoreWindow If true then don't set the window size and location
     *
     * @throws RemoteException Java RMI problem
     * @throws VisADException  Couldn't create the VisAD object
     */
    protected void initWithInner(ViewManager that, boolean ignoreWindow)

            throws VisADException, RemoteException {

        if (getInitViewStateName() != null) {
            List vms = getIdv().getVMManager().getVMState();
            for (int i = 0; i < vms.size(); i++) {
                ViewState viewState = (ViewState) vms.get(i);
                if (viewState.getName().equals(getInitViewStateName())) {
                    if (isCompatibleWith(viewState)) {
                        try {
                            initWith(viewState);
    /**
                            break;
                        } catch (Exception exc) {
                            throw new RuntimeException(exc);
                        }
                    } else {
                        setInitViewStateName(null);
                        break;
                    }
                }
            }

        }



        if ( !(that instanceof MapViewManager)) {
            return;
        }


        if (displayProjectionZoom != 0) {
            getMapDisplay().zoom(displayProjectionZoom);
        }

        if (doNotSetProjection) {
            return;
        }




        MapViewManager mvm            = (MapViewManager) that;
        MapProjection  thatProjection = mvm.getMainProjection();
        if (getInitViewStateName() == null) {
            this.setAspectRatio(that.getAspectRatio());
        }

        if ((mvm.flythrough != null) && (mvm.flythrough != this.flythrough)) {
            if (this.flythrough != null) {
                this.flythrough.destroy();
                //                this.flythrough.initWith(mvm.flythrough);
            }
            this.flythrough = mvm.flythrough;
            this.flythrough.setViewManager(this);
            if (this.flythrough.getShown()) {
                this.flythrough.show();
            }
        }

        boolean setProjection = false;
        if (thatProjection != null) {
            setProjection = setMapProjection(thatProjection, false,
                                             mvm.mainProjectionName);
        }

        if (getInitViewStateName() == null) {
            if ( !setProjection) {
                if (getAspectRatio() != null) {
                    getMapDisplay().setDisplayAspect(getAspectRatio());
                }
            }
        }

        //Only save the projection if we're not a globe
        //        getMapDisplay().saveProjection();
        if ( !getUseGlobeDisplay()) {
            getMapDisplay().saveProjection();
        }

        this.globeBackgroundColor = mvm.globeBackgroundColor;
        this.globeBackgroundLevel = mvm.globeBackgroundLevel;
        if (globeBackgroundDisplayable != null) {
            setGlobeBackground((GlobeDisplay) getMapDisplay());
        }



        super.initWithInner(that, ignoreWindow);
        try {
            //If we have an old bundle then the other map view has a non-null
            //map state. If so we load it in.
            if (mvm.mapState != null) {
                MapDisplayControl defaultMap = findDefaultMap();
                if (defaultMap != null) {
                    MapInfo mapInfo =
                        new MapInfo(XmlUtil.getRoot(mvm.mapState));
                    MapDisplayControl newMap = new MapDisplayControl(this,
                                                   mapInfo);
                    newMap.init((ucar.unidata.data.DataChoice) null);
                    defaultMap.loadNewMap(newMap);
                }
            }

        } catch (Exception exc) {
            logException("Initializing with MapViewManager", exc);
        }

        LatLonAxisScaleInfo latAxisScaleInfo = mvm.getLatAxisScaleInfo();

        if (latAxisScaleInfo != null) {
            setLatAxisScaleInfo(latAxisScaleInfo);
        LatLonAxisScaleInfo lonAxisScaleInfo = mvm.getLonAxisScaleInfo();

        if (lonAxisScaleInfo != null) {
            setLonAxisScaleInfo(lonAxisScaleInfo);
        }


    }


    /**
     * Initialize the ViewState
     *
     * @param viewState the ViewState
     */
    public void initViewState(ViewState viewState) {
        super.initViewState(viewState);
        viewState.put(ViewState.PROP_GLOBE,
                      new Boolean(getUseGlobeDisplay()));
        if ( !getUseGlobeDisplay()) {
            viewState.put(ViewState.PROP_PROJECTION, getMainProjection());
        }
    }


    /**
     * Get the JComponent for the VisAD display
     *
     * @return VisAD display's Component
     */
    public JComponent getInnerContents() {
        return (JComponent) getMapDisplay().getComponent();
    }





    /**
     * Leave this here for old bundles
     *
     * @param ms The map specification
     */
    public void setMapState(String ms) {
        mapState = ms;
    }




    /**
     * Add in the different preference panels.
     *
     * @param preferenceManager The preference manager to add things into
     */
    public void initPreferences(
            final IdvPreferenceManager preferenceManager) {

        super.initPreferences(preferenceManager);

        final JComponent[] bgComps =
            GuiUtils.makeColorSwatchWidget(getStore().get(PREF_BGCOLOR,
                getBackground()), "Set Background Color");

        final JComponent[] fgComps =
            GuiUtils.makeColorSwatchWidget(getStore().get(PREF_FGCOLOR,
                getForeground()), "Set Foreground Color");

        final JComponent[] border =
            GuiUtils
                .makeColorSwatchWidget(getStore()
                    .get(PREF_BORDERCOLOR, ViewManager
                        .borderHighlightColor), "Set Selected Panel Border Color");

        final JComponent[] globeComps =
            GuiUtils.makeColorSwatchWidget(getGlobeBackgroundColorToUse(),
                                           "Globe Background Color");
        StateManager stateManager = getStateManager();

        GuiUtils.tmpInsets = new Insets(5, 5, 5, 5);
        JPanel colorPanel = GuiUtils.left(GuiUtils.doLayout(new Component[] {
            GuiUtils.rLabel("  Background:"), bgComps[0], bgComps[1],
            GuiUtils.rLabel("  Foreground:"), fgComps[0], fgComps[1],
            GuiUtils.rLabel("  Selected Panel:"), border[0], border[1],
            GuiUtils.rLabel("  Globe Background:"), globeComps[0],
            globeComps[1],
        }, 3, GuiUtils.WT_N, GuiUtils.WT_N));
        colorPanel = GuiUtils.vbox(new JLabel("Color Scheme:"), colorPanel);

        cid        = new ContourInfoDialog("Preferences", false, null, false);
        ContourInfo ci =
            new ContourInfo(
                null, 0, 0, 10, true, false, false, 1, 0,
                ContourControl.LABEL_FREQ_LO,
                (int) stateManager
                    .getPreferenceOrProperty(
                        PREF_CONTOUR_LABELSIZE,
                        ContourInfo.DEFAULT_LABEL_SIZE), ContourInfoDialog
                            .getContourFont(
                                stateManager
                                    .getPreferenceOrProperty(
                                        PREF_CONTOUR_LABELFONT)), stateManager
                                            .getPreferenceOrProperty(
                                                PREF_CONTOUR_LABELALIGN,
                                                true));
        cid.setState(ci);

        JPanel contourPanel =
            GuiUtils.vbox(new JLabel("Contour Labels:"),
                          GuiUtils.inset(cid.getLabelPanel(),
                                         new Insets(5, 20, 0, 0)));
        contourPanel = GuiUtils.topCenter(contourPanel, GuiUtils.filler());
        colorPanel   = GuiUtils.hbox(colorPanel, contourPanel);

        final FontSelector fontSelector =
            new FontSelector(FontSelector.COMBOBOX_UI, false, false);
        Font f = getStore().get(PREF_DISPLAYLISTFONT, getDisplayListFont());
        fontSelector.setFont(f);
        final GuiUtils.ColorSwatch dlColorWidget =
            new GuiUtils.ColorSwatch(getStore().get(PREF_DISPLAYLISTCOLOR,
                getDisplayListColor()), "Set Display List Color");
        //GuiUtils.tmpInsets = new Insets(5, 5, 5, 5);
        JPanel fontPanel = GuiUtils.vbox(GuiUtils.lLabel("Display List:"),
                                         GuiUtils.doLayout(new Component[] {
                                             GuiUtils.rLabel("Font:"),
                                             GuiUtils.left(fontSelector.getComponent()),
                                             GuiUtils.rLabel("Color:"),
                                             GuiUtils.left(GuiUtils.hbox(dlColorWidget,
                                                 dlColorWidget.getSetButton(),
                                                 dlColorWidget.getClearButton(),
                                                 5)) }, 2, GuiUtils.WT_N,
                                                     GuiUtils.WT_N));


        List            projections = getProjectionList();
        final JComboBox projBox     = new JComboBox();
        final Hashtable projMap = new Hashtable();

        Collection projNames =
            new TreeSet(Collator.getInstance());

        for (int p = 0; p < projections.size(); p++) {
            String projName = ((ProjectionImpl) projections.get(p)).getName();
            projMap.put(projName, projections.get(p));
            projNames.add(projName);
        }

        GuiUtils.setListData(projBox, projNames.toArray());
        Object defaultProj = getDefaultProjection();
        if (defaultProj != null) {
            if (defaultProj instanceof ProjectionImpl) {
                projBox.setSelectedItem(
                    ((ProjectionImpl) defaultProj).getName());
            } else {
                projBox.setSelectedItem(defaultProj);
            }
        }

        final JCheckBox logoVizBox = new JCheckBox(
                                         "Show Logo in View",
                                         stateManager.getPreferenceOrProperty(
                                             PREF_LOGO_VISIBILITY, false));
        final JTextField logoField =
            new JTextField(stateManager.getPreferenceOrProperty(PREF_LOGO,
                ""));
        logoField.setToolTipText("Enter a file or URL");
        // top panel
        JButton browseButton = new JButton("Browse..");
        browseButton.setToolTipText("Choose a logo from disk");
        browseButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                String filename =
                    FileManager.getReadFile(FileManager.FILTER_IMAGE);
                if (filename == null) {
                    return;
                }
                logoField.setText(filename);
            }
        });

        String[] logos = parseLogoPosition(
                             stateManager.getPreferenceOrProperty(
                                 PREF_LOGO_POSITION_OFFSET, ""));
        final JComboBox logoPosBox = new JComboBox(logoPoses);
        logoPosBox.setToolTipText("Set the logo position on the screen");
        logoPosBox.setSelectedItem(findLoc(logos[0]));

        final JTextField logoOffsetField = new JTextField(logos[1]);
        logoOffsetField.setToolTipText(
            "Set an offset from the position (x,y)");

        float logoScaleFactor =
            (float) stateManager.getPreferenceOrProperty(PREF_LOGO_SCALE,
                1.0);
        final JLabel logoSizeLab = new JLabel("" + logoScaleFactor);
        JComponent[] sliderComps = GuiUtils.makeSliderPopup(0, 20,
                                       (int) (logoScaleFactor * 10), null);
        final JSlider  logoScaleSlider = (JSlider) sliderComps[1];
        ChangeListener listener        = new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                logoSizeLab.setText("" + logoScaleSlider.getValue() / 10.f);
            }
        };
        logoScaleSlider.addChangeListener(listener);
        sliderComps[0].setToolTipText("Change Logo Scale Value");

        JPanel logoPanel =
            GuiUtils.vbox(
                GuiUtils.left(logoVizBox),
                GuiUtils.centerRight(logoField, browseButton),
                GuiUtils.hbox(
                    GuiUtils.leftCenter(
                        GuiUtils.rLabel("Screen Position: "),
                        logoPosBox), GuiUtils.leftCenter(
                            GuiUtils.rLabel("Offset: "),
                            logoOffsetField), GuiUtils.leftCenter(
                                GuiUtils.rLabel("Scale: "),
                                GuiUtils.leftRight(
                                    logoSizeLab, sliderComps[0]))));
        logoPanel = GuiUtils.vbox(GuiUtils.lLabel("Logo: "),
                                  GuiUtils.left(GuiUtils.inset(logoPanel,
                                      new Insets(5, 5, 0, 0))));


        PreferenceManager miscManager = new PreferenceManager() {
            public void applyPreference(XmlObjectStore theStore,
                                        Object data) {
                IdvPreferenceManager.applyWidgets((Hashtable) data, theStore);
                if (projBox.getSelectedItem() instanceof String) {
                    theStore.put(PREF_PROJ_DFLT,
                                 projMap.get(projBox.getSelectedItem()));
                } else {
                    theStore.put(PREF_PROJ_DFLT, projBox.getSelectedItem());
                }
                theStore.put(PREF_BGCOLOR, bgComps[0].getBackground());
                theStore.put(PREF_GLOBEBACKGROUND,
                             globeComps[0].getBackground());
                theStore.put(PREF_FGCOLOR, fgComps[0].getBackground());
                theStore.put(PREF_BORDERCOLOR, border[0].getBackground());
                theStore.put(PREF_DISPLAYLISTFONT, fontSelector.getFont());
                theStore.put(PREF_DISPLAYLISTCOLOR,
                             dlColorWidget.getSwatchColor());
                checkToolBarVisibility();
                ViewManager.setHighlightBorder(border[0].getBackground());
                cid.doApply();
                ContourInfo ci = cid.getInfo();
                theStore.put(PREF_CONTOUR_LABELSIZE, ci.getLabelSize());
                theStore.put(PREF_CONTOUR_LABELFONT, ci.getFont());
                theStore.put(PREF_CONTOUR_LABELALIGN, ci.getAlignLabels());
                theStore.put(PREF_LOGO, logoField.getText());
                String lpos =
                    ((TwoFacedObject) logoPosBox.getSelectedItem()).getId()
                        .toString();
                String loff = logoOffsetField.getText().trim();

                theStore.put(PREF_LOGO_POSITION_OFFSET,
                             makeLogoPosition(lpos, loff));
                theStore.put(PREF_LOGO_VISIBILITY, logoVizBox.isSelected());
                theStore.put(PREF_LOGO_SCALE,
                             logoScaleSlider.getValue() / 10f);

            }
        };


        Hashtable  widgets     = new Hashtable();
        ArrayList  miscList    = new ArrayList();

        Object[][] miscObjects = {
            { "View:", null, null },
            { "Show Wireframe Box", PREF_WIREFRAME,
              new Boolean(getWireframe()) },
            { "Show Cursor Readout", PREF_SHOWCURSOR,
              new Boolean(getShowCursor()) },
            { "Clip View At Box", PREF_3DCLIP, new Boolean(getClipping()) },
            { "Show Display List", PREF_SHOWDISPLAYLIST,
              new Boolean(getShowDisplayList()) },
            { "Show Times In View", PREF_ANIREADOUT,
              new Boolean(getAniReadout()) },
            { "Show Map Display Scales", PREF_SHOWSCALES,
              new Boolean(getLabelsVisible()) },
            { "Show Transect Display Scales", PREF_SHOWTRANSECTSCALES,
              new Boolean(getTransectLabelsVisible()) },
            { "Show \"Please Wait\" Message", PREF_WAITMSG,
              new Boolean(getWaitMessageVisible()) },
            { "Reset Projection With New Data", PREF_PROJ_USEFROMDATA },
            { PR_LABEL, PREF_USE_PROGRESSIVE_RESOLUTION,
            	new Boolean(getUseProgressiveResolution())},
            { "Use 3D View", PREF_DIMENSION },
            { "Show Globe Background", PREF_SHOWGLOBEBACKGROUND,
              new Boolean(getStore().get(PREF_SHOWGLOBEBACKGROUND,
                                         defaultGlobeBackground)) }
        };


        Object[][] legendObjects = {
            { "Legends:", null, null },
            { "Show Side Legend", PREF_SHOWSIDELEGEND,
              new Boolean(getShowSideLegend()) },
            { "Show Bottom Legend", PREF_SHOWBOTTOMLEGEND,
              new Boolean(getShowBottomLegend()) },
            { "Show Animation Boxes", PREF_SHOWANIMATIONBOXES,
              new Boolean(getShowAnimationBoxes()) },
            { "Show Clock On Dashboard", IdvConstants.PROP_SHOWCLOCK_DASH,
              new Boolean(
                  getStateManager().getPreferenceOrProperty(
                      IdvConstants.PROP_SHOWCLOCK_DASH, "true")) },
            { "Show Clock On View Windows", IdvConstants.PROP_SHOWCLOCK_VIEW,
              new Boolean(
                  getStateManager().getPreferenceOrProperty(
                      IdvConstants.PROP_SHOWCLOCK_VIEW, "true")) },
            { "Show Overview Map", PREF_SHOWPIP,
              new Boolean(getStore().get(PREF_SHOWPIP, false)) }
        };

        Object[][] toolbarObjects = {
            { "Toolbars:", null, null },
            { "Show Earth Navigation Panel", PREF_SHOWEARTHNAVPANEL,
              new Boolean(getShowEarthNavPanel()) },
            { "Show Viewpoint Toolbar", PREF_SHOWTOOLBAR + "perspective" },
            { "Show Zoom/Pan Toolbar", PREF_SHOWTOOLBAR + "zoompan" },
            { "Show Undo/Redo Toolbar", PREF_SHOWTOOLBAR + "undoredo" }
        };

        JPanel miscPanel = IdvPreferenceManager.makePrefPanel(miscObjects,
                               widgets, getStore());
        JPanel legendPanel =
            IdvPreferenceManager.makePrefPanel(legendObjects, widgets,
                getStore());
        JPanel toolbarPanel =
            IdvPreferenceManager.makePrefPanel(toolbarObjects, widgets,
                getStore());
        JPanel projPanel =
            GuiUtils.vbox(GuiUtils.lLabel("Default Projection: "),
                          GuiUtils.left(GuiUtils.inset(projBox,
                              new Insets(5, 20, 0, 0))));

        JPanel colorFontPanel = GuiUtils.vbox(GuiUtils.top(colorPanel),
                                    GuiUtils.top(fontPanel)  //,
        //GuiUtils.top(projPanel)


        GuiUtils.tmpInsets = new Insets(5, 5, 5, 5);
        JPanel miscContents = GuiUtils.doLayout(Misc.newList(new Component[] {
            GuiUtils.top(legendPanel), GuiUtils.top(toolbarPanel),
            GuiUtils.top(miscPanel), GuiUtils.top(colorFontPanel),
            GuiUtils.top(projPanel), GuiUtils.top(logoPanel)
        }), 2, GuiUtils.WT_N, GuiUtils.WT_N);


        miscContents = GuiUtils.inset(GuiUtils.left(miscContents), 5);
        preferenceManager.add("View", "View Preferences", miscManager,
                              miscContents, widgets);


    }


    /**
     * Go the a street address
     */
    public void goToAddress() {
        Misc.run(new Runnable() {
            public void run() {
                goToAddressInner();
            }
        });
    }


    /**
     * Popup the address location dialog and translate to the lat/lon.
     */
    private void goToAddressInner() {
        try {
            if (addressReprojectCbx == null) {

                addressReprojectCbx = new JCheckBox("Reproject",
                        getStore().get(PREF_ADDRESS_REPROJECT, true));
                addressReprojectCbx.setToolTipText(
                    "When checked make a simple map projection over the location");

                List savedAddresses =
                    (List) getStore().get(PREF_ADDRESS_LIST);
                if (savedAddresses != null) {
                    GeoUtils.setSavedAddresses(savedAddresses);
                }
            }
            getIdvUIManager().showWaitCursor();
            LatLonPoint llp = GeoUtils.getLocationOfAddress(
                                  GuiUtils.left(getUseGlobeDisplay()
                    ? GuiUtils.filler()
                    : (JComponent) addressReprojectCbx));
            //            System.out.println ("{" + GeoUtils.lastAddress+"}  -ll {" + llp.getLatitude()+","+llp.getLongitude()+"}");
            getIdvUIManager().showNormalCursor();
            if (llp == null) {
                return;
            }

            getStore().put(PREF_ADDRESS_LIST, GeoUtils.getSavedAddresses());
            getStore().put(PREF_ADDRESS_REPROJECT,
                           addressReprojectCbx.isSelected());


            float x      = (float) llp.getLongitude().getValue();
            float y      = (float) llp.getLatitude().getValue();
            float offset = (float) (1.0 / 60.0f);
            Rectangle2D.Float rect = new Rectangle2D.Float(x - offset,
                                         y - offset, offset * 2, offset * 2);
            if ( !getUseGlobeDisplay() && addressReprojectCbx.isSelected()) {
                TrivialMapProjection mp =
                    new TrivialMapProjection(
                        RealTupleType.SpatialEarth2DTuple, rect);

                setMapProjection(mp, true);
            } else {
                getMapDisplay().center(GeoUtils.toEarthLocation(llp),
                                       shouldAnimateViewChanges());
                //                getMapDisplay().center(GeoUtils.toEarthLocation(llp), false);
            }
        } catch (Exception e) {
            getIdvUIManager().showNormalCursor();
            logException("Error going to address", e);
        }
    }



    /**
     * Get the map display.
     *
     * @return The map display. This is the main display for thie view manager.
     */
    public NavigatedDisplay getMapDisplay() {
        return (NavigatedDisplay) getMaster();
    }

    /**
     * Are we in 3d mode
     *
     * @return Is display in 3d?
     */
    public boolean isDisplay3D() {
        return (getMapDisplay().getDisplayMode() == NavigatedDisplay.MODE_3D);
    }

    /**
     * Handle the receipt of shared data
     *
     * @param from Who is it from
     * @param dataId What is it
     * @param data Here it is
     */
                }
    public void receiveShareData(Sharable from, Object dataId,
                                 Object[] data) {
        if ( !getInitDone()) {
            return;
        }
        if (dataId.equals(SHARE_PROJECTION)) {
            setMapProjection(((MapProjection) data[0]), false);
            return;
        }
        super.receiveShareData(from, dataId, data);
    }




    /**
     * Add the PIP panel if needed
     *
     * @param sideLegend The side legend
     *
     * @return The side legend or the sidelegend coupled with the pip panel
     */
    protected JComponent getSideComponent(JComponent sideLegend) {
        if (false && getUseGlobeDisplay()) {
            return sideLegend;
        }
        pipPanel = new PipPanel(this);
        pipPanel.setPreferredSize(new Dimension(100, 100));
        JButton closeBtn =
            GuiUtils.makeImageButton("/auxdata/ui/icons/Cancel16.gif", this,
                                     "hidePip");
        pipPanelWrapper = GuiUtils.topCenter(GuiUtils.right(closeBtn),
                                             pipPanel);
        if ( !getShowPip()) {
            pipPanelWrapper.setVisible(false);
        }
        return GuiUtils.centerBottom(sideLegend, pipPanelWrapper);
    }


    /**
     * Make the GUI contents.
     *
     * @return The GUI contents
     */
    protected Container doMakeContents() {
        NavigatedDisplay navDisplay   = getMapDisplay();
        JComponent       navComponent = getComponent();
        navComponent.setPreferredSize(getMySize());
        earthNavPanelWrapper = new JPanel(new BorderLayout());
        JPanel contents = GuiUtils.centerBottom(navComponent,
                              earthNavPanelWrapper);

        earthNavPanel = new EarthNavPanel(this);
        if (getShowEarthNavPanel()) {
            earthNavPanelWrapper.add(BorderLayout.CENTER, earthNavPanel);
        }
        return contents;
    }


    /**
     * Initialize the toolbars for the GUI
     */
    protected void initToolBars() {
        if (isDisplay3D()) {
            addToolBar(doMakeViewPointToolBar(JToolBar.VERTICAL),
                       "perspective", "Viewpoint toolbar");
        }
        super.initToolBars();
    }



    /**
     * Set the viewpoint info
     *
     * @param viewpointInfo the viewpoint info
     */
    public void setViewpointInfo(ViewpointInfo viewpointInfo) {
        getViewpointControl().setViewpointInfo(viewpointInfo);
    }






    /**
     * Dynamically initialize the view menu
     *
     * @param viewMenu the view menu
     */
    public void initializeViewMenu(JMenu viewMenu) {
            viewMenu.add(getViewpointControl().getMenu());
        }
        viewMenu.add(makeColorMenu());
        viewMenu.addSeparator();

        if (isFullScreen()) {
            viewMenu.add(
                GuiUtils.setIcon(
                    GuiUtils.makeMenuItem(
                        "Reset Full Screen", this,
                        "resetFullScreen"), "/auxdata/ui/icons/arrow_in.png"));
        } else {
            viewMenu.add(
                GuiUtils.setIcon(
                    GuiUtils.makeMenuItem(
                        "Full Screen", this,
                        "setFullScreen"), "/auxdata/ui/icons/arrow_out.png"));
        }
        viewMenu.addSeparator();
        viewMenu.add(
            GuiUtils.setIcon(
                GuiUtils.makeMenuItem(
                    "Animation Timeline", this,
                    "showTimeline"), "/auxdata/ui/icons/timeline_marker.png"));

        viewMenu.add(GuiUtils.setIcon(GuiUtils.makeMenuItem("Flythrough",
                this, "showFlythrough"), "/auxdata/ui/icons/plane.png"));

        viewMenu.add(GuiUtils.setIcon(GuiUtils.makeMenuItem("Properties",
                this,
                "showPropertiesDialog"), "/auxdata/ui/icons/information.png"));
    }

    /**
     * Create and return the list of menus for the menu bar.
     * Just the map and view menu.
     *
     * @return List of menus.
     */
    public ArrayList doMakeMenuList() {
        ArrayList menus = super.doMakeMenuList();
        menus.add(makeViewMenu());
        menus.add(makeProjectionMenu());
        return menus;
    }


    /**
     * Create and return the show menu.
     *
     * @return The Show menu
     */
    protected JMenu makeShowMenu() {
        JMenu showMenu = super.makeShowMenu();
        if (globeBackgroundDisplayable != null) {
            createCBMI(showMenu, PREF_SHOWGLOBEBACKGROUND);
        }

        createCBMI(showMenu, PREF_SHOWSCALES);
        createCBMI(showMenu, PREF_ANIREADOUT);
        createCBMI(showMenu, PREF_SHOWPIP);
        createCBMI(showMenu, PREF_SHOWEARTHNAVPANEL);
        createCBMI(showMenu, PREF_LOGO_VISIBILITY);
        return showMenu;
    }



    /**
     * Center the display (animated) to the center of the given mapprojection
     *
     * @param mp  map projection
     *
     * @throws RemoteException  Java RMI problem
     * @throws VisADException   VisAD problem
     */
    public void center(MapProjection mp)
            throws RemoteException, VisADException {
        LatLonPoint center = mp.getCenterLatLon();
        getNavigatedDisplay().center(
            new EarthLocationTuple(
                center.getLatitude(), center.getLongitude(),
                new Real(RealType.Altitude, 0)), true);

    }


    /**
     * Set the projection to the first  projection  found in the displays
     */
    public void setProjectionFromFirstDisplay() {
        List controls = getControls();
        for (int i = 0; i < controls.size(); i++) {
            DisplayControl display = (DisplayControl) controls.get(i);
            MapProjection  mp      = display.getDataProjection();
            if (displayProjectionOk(mp)) {
                setMapProjection(
                    mp, true,
                    getDisplayConventions().getMapProjectionLabel(
                        mp, display));
                break;
            }
        }
    }


    /**
     * If we are using a ProjectionImpl then see if it hsa
     *
     * @throws RemoteException On badness
     * @throws VisADException On badness
     */
    protected void updateProjection() throws RemoteException, VisADException {
        if ((mainProjection == null)
    }
                || !(mainProjection instanceof ProjectionCoordinateSystem)) {
            return;
        }
        ProjectionImpl myProjection =
            (ProjectionImpl) ((ProjectionCoordinateSystem) mainProjection)
                .getProjection();
        ProjectionImpl newProjection =
            getIdv().getIdvProjectionManager().findProjectionByName(
                myProjection.getName());
        if ((newProjection != null) && !myProjection.equals(newProjection)) {
            double[] matrix = getDisplayMatrix();
            setProjection(newProjection);
            setDisplayMatrix(matrix);
        }
    }



    /**
     * Find and set the projection by name
     *
     * @param projName projection name
     */
    public void setProjectionByName(String projName) {
        List projections = getProjectionList();
        for (int i = 0; i < projections.size(); i++) {
            ProjectionImpl p = (ProjectionImpl) projections.get(i);
            if (p.getName().equals(projName)) {
                setProjection(p);
                return;
            }
        }

        for (int i = 0; i < projections.size(); i++) {
            ProjectionImpl p = (ProjectionImpl) projections.get(i);
            if (StringUtil.stringMatch(p.getName(), projName)) {
                setProjection(p);
                return;
            }
        }
        //        System.err.println("Could not find projection:" + projName);
    }



    /**
     * Set the current projection
     *
     * @param p The new projection.
     */
    public void setProjection(ProjectionImpl p) {
        p = (ProjectionImpl) p.constructCopy();
        try {
            setMapProjection(new ProjectionCoordinateSystem(p), true);
            if (pipPanel != null) {
                pipPanel.setProjectionImpl(p);
            }
        } catch (Exception excp) {
            logException("setMapProjection ()", excp);
        }
    }

    /**
     * Class ProjectionCommand manages changes to the projection
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.382 $
     */
    public static class ProjectionCommand extends Command {

        /** THe view manager I am in */
        MapViewManager viewManager;

        /** old state */
        String oldName;

        /** old state */
        String oldProjection;

        /** new state */
        String newName;

        /** new state */
        String newProjection;


        /**
         * ctor
         *
         * @param viewManager The vm
         * @param oldName old state
         * @param oldProjection old state
         * @param newName new state
         * @param newProjection new state

     */
         */
        public ProjectionCommand(MapViewManager viewManager, String oldName,
                                 MapProjection oldProjection, String newName,
                                 MapProjection newProjection) {
            this.viewManager   = viewManager;
            this.oldName       = oldName;
            this.oldProjection = encode(oldProjection);
            this.newName       = newName;
    /**
            this.newProjection = encode(newProjection);
        }

        /**
         * Encode the map projection
         *
         * @param projection the map projection
         *
         * @return the encoded XML
         */
        private String encode(MapProjection projection) {
            try {
                return viewManager.getIdv().encodeObject(projection, false);
            } catch (Exception exc) {
                throw new RuntimeException(exc);
            }
        }

        /**
         * Decode a MapProjection XML spec
         *
         * @param xml a MapProjection XML spec
         *
         * @return  the decoded MapProjection
         */
        private MapProjection decode(String xml) {
            try {
                return (MapProjection) viewManager.getIdv().decodeObject(xml);
            } catch (Exception exc) {
                throw new RuntimeException(exc);
            }
        }


        /**
         * Redo
         */
        public void redoCommand() {
            viewManager.setMapProjection(decode(newProjection), true,
                                         newName, false, false);
        }

        /**
         * Undo
         */
        public void undoCommand() {
            viewManager.setMapProjection(decode(oldProjection), true,
                                         oldName, false, false);
        }
    }





    /**
     *  A wrapper aroung the setMapProjection call that takes a projection name.
     *  This passes in null.
     *
     * @param projection The projection
     * @param fromWidget Is it from the projection selection widget
     */
    public void setMapProjection(MapProjection projection,
                                 boolean fromWidget) {
        setMapProjection(projection, fromWidget, null);
    }


    /**
     * Get the current projection.Used for xml encoding/decoding.
     *
     * @return The current projection
     */
    public MapProjection getMainProjection() {
        return mainProjection;
    }

    /**
     * Set the current projection. Used for xml encoding/decoding.
     *
     * @param projection The new projection
     */
    public void setMainProjection(MapProjection projection) {
        mainProjection = projection;
    }


    /**
     * The main projection name.Used for xml encoding/decoding.
     *
     * @return Projection name
     */
    public String getMainProjectionName() {
        return mainProjectionName;
    }

    /**
     * The main projection name.Used for xml encoding/decoding.
     *
     * @param projectionName Projection name
     * @return
    public void setMainProjectionName(String projectionName) {
        mainProjectionName = projectionName;
    }

    /**
     * Set the DfltProjectionName property.
     *
     * @param value The new value for DfltProjectionName
     */
    public void setDefaultProjectionName(String value) {
        this.defaultProjectionName = value;
    }

    /**
     * Get the DfltProjectionName property.
     *
     * @return The DfltProjectionName
     */
    public String getDefaultProjectionName() {
        return this.defaultProjectionName;
    }





    /**
     * Add the projection and the given name into the history list. Add a menu item
     * into the history menu.
     *
     * @param projection The projection
     * @param name Its name
     */
    private void addProjectionToHistory(MapProjection projection,
                                        String name) {
        String encodedProjection = getIdv().encodeObject(projection, false);
        TwoFacedObject tfo = TwoFacedObject.findId(encodedProjection,
                                 projectionHistory);
        if (tfo != null) {
            projectionHistory.remove(tfo);
            projectionHistory.add(0, tfo);
            return;

        }
        tfo = new TwoFacedObject(name, encodedProjection);
        projectionHistory.add(0, tfo);


        String label = ((name != null)
                        ? name
                        : projection.toString());
    }


    /**
     * Set map projection in the main display.
     *
     * @param projection a Projection
     * @param fromWidget  true if this was from a widget (ie. widget or
     *                    menu item)
     * @param name        name to put in the history list (may be null)
     *
     * @return  true if successful
     */
    public boolean setMapProjection(MapProjection projection,
                                    boolean fromWidget, String name) {
        return setMapProjection(projection, fromWidget, name, false);
    }

    /**
     * Set map projection in the main display.
     *
     * @param projection a Projection
     * @param fromWidget  true if this was from a widget (ie. widget or
     *                    menu item)
     * @param name        name to put in the history list (may be null)
     * @param checkDefault  if true, check to see if we
     *                    should call getUseProjectionFromData()
     *
     * @return  true if successful
     */
    public boolean setMapProjection(MapProjection projection,
                                    boolean fromWidget, String name,
                                    boolean checkDefault) {
        return setMapProjection(projection, fromWidget, name, checkDefault,
                                true);
    }



    /**
     * Set map projection in the main display.
     *
     * @param projection a Projection
     * @param fromWidget  true if this was from a widget (ie. widget or
     *                    menu item)
     * @param name        name to put in the history list (may be null)
     * @param checkDefault  if true, check to see if we
     *                    should call getUseProjectionFromData()
     * @param addToCommandHistory Add this projection to the command history
     *
     * @return  true if successful
     */
    public boolean setMapProjection(MapProjection projection,
                                    boolean fromWidget, String name,
                                    boolean checkDefault,
                                    boolean addToCommandHistory) {
        return setMapProjection(projection, fromWidget, name, checkDefault,
                                addToCommandHistory, false);
    }


     * Set map projection in the main display.
     *
     * @param projection a Projection
     * @param fromWidget  true if this was from a widget (ie. widget or
     *                    menu item)
     * @param name        name to put in the history list (may be null)
     * @param checkDefault  if true, check to see if we
     *                    should call getUseProjectionFromData()
     * @param addToCommandHistory Add this projection to the command history
     * @param maintainViewpoint  maintain the viewpoint
     *
     * @return  true if successful
     */
    public boolean setMapProjection(MapProjection projection,
                                    boolean fromWidget, String name,
                                    boolean checkDefault,
                                    boolean addToCommandHistory,
                                    boolean maintainViewpoint) {


        IdvUIManager.startTime = System.currentTimeMillis();

        if (checkDefault) {
            if ( !getUseProjectionFromData()) {
                return false;
            }
            if (doNotSetProjection) {
                return false;
            }
        }

        if (projection == null) {
            return false;
        }




        if (getUseGlobeDisplay() && !getViewpointControl().getAutoRotate()) {
            try {
                LatLonPoint center = projection.getCenterLatLon();
                getNavigatedDisplay().center(
                    new EarthLocationTuple(
                        center.getLatitude(), center.getLongitude(),
                        new Real(RealType.Altitude, 0)), true);
                return true;
            } catch (Exception e) {
                logException("setProjection", e);
                return false;
            }
        }




        boolean  actuallyChangedProjection = false;
        double[] matrix                    = getDisplayMatrix();
        try {

            setMasterInactive();
            if (addToCommandHistory && (mainProjection != null)) {
                addCommand(new ProjectionCommand(this, mainProjectionName,
                        mainProjection, name, projection));
            }

            if ( !Misc.equals(mainProjection, projection)) {
                if (name == null) {
                    name = getDisplayConventions().getMapProjectionName(
                        projection);
                }

                //If this is the first time we've put one in then save the current (default) proj.
                if ((projectionHistory.size() == 0)
                        && (mainProjection != null)) {
                    addProjectionToHistory(mainProjection, "Default");
                }
                mainProjectionName = name;
                addProjectionToHistory(projection, name);

                mainProjection = projection;
                if (fromWidget) {
                    doShare(SHARE_PROJECTION, projection);
                }
                try {
                    actuallyChangedProjection = true;
                    getMapDisplay().setMapProjection(mainProjection);
                    setAspectRatio(getMapDisplay().getDisplayAspect());


                    // override the aspect ratio
                    //if (getAspectRatio() != null) {
                    //getMapDisplay().setDisplayAspect(getAspectRatio());
                    //}
                    setDisplayMatrix(matrix);
                } catch (Exception e) {
                    logException("setProjection", e);
                notifyDisplayControls(SHARE_PROJECTION);
                checkPipPanel();
            }
            // if the projections are the same, reset to main view in case
            // they are zoomed/panned
            try {
                if ( !maintainViewpoint
                        && projection.equals(mainProjection)) {
                    getMaster().resetProjection();
                }
            } catch (Exception e) {
                logException("setProjection", e);
            }
        } finally {
            updateDisplayList();
            setMasterActive();
        }
        return actuallyChangedProjection;
    }

    /**
     * Check the pip panel. If non-null have it reset its box
     */
    public void checkPipPanel() {
        try {
            synchronized (PIP_MUTEX) {
                if (pipPanel == null) {
                    return;
                }
                pipPanel.resetDrawBounds();
            }
        } catch (Exception exc) {
            pipPanel = null;
            logException("Error setting pip panel", exc);
        }
    }



    /**
     * can this viewmanager import the given display control
     *
     * @param control the control
     *
     * @return ok to import
     */
    public boolean okToImportDisplay(DisplayControl control) {
        //Base class method checks for non-null and class equality
        if ( !super.okToImportDisplay(control)) {
            return false;
        }
        MapViewManager vm = (MapViewManager) control.getViewManager();
        return this.getUseGlobeDisplay() == vm.getUseGlobeDisplay();
    }


    /**
     * Received the first frame done event  from the display
     */
    protected void doneFirstFrame() {
        super.doneFirstFrame();
        checkPipPanel();
    }

    /**
     * Search through the list of display controls
     * looking for a {@link ucar.unidata.idv.control.MapDisplayControl}
     * that has been set to be the "default map"
     *
     * @return The default map display control
     */
    private MapDisplayControl findDefaultMap() {
        List controls = getControls();
        for (int i = controls.size() - 1; i >= 0; i--) {
            DisplayControl control = (DisplayControl) controls.get(i);
            if ( !(control instanceof MapDisplayControl)) {
                continue;
            }
            if (((MapDisplayControl) control).getIsDefaultMap()) {
                return (MapDisplayControl) control;
            }
        }
        return null;
    }


    /**
     * Apply the properties
     *
     * @return true if successful
     */
    public boolean applyProperties() {
        if ( !super.applyProperties()) {
            return false;
     */
     * Apply axis visibility choices.
        }
        if (useGlobeDisplay && (globeBackgroundDisplayable != null)) {
            globeBackgroundColor = globeBackgroundColorComp.getBackground();
            globeBackgroundLevel = globeBackgroundLevelSlider.getValue();
            setGlobeBackground((GlobeDisplay) getMapDisplay());
            return true;
        }
        return applyAxisVisibility();
    private boolean applyAxisVisibility() {

        if (latLonScaleWidget == null) {
            return false;
        }

        boolean b = !useGlobeDisplay
                    && (getBp(PREF_SHOWSCALES)
                        || latLonScaleWidget.isLatVisible()
                        || latLonScaleWidget.isLonVisible());

        setBp(PREF_SHOWSCALES, b);

        return latLonScaleWidget.doApply();
    }



    /**
     * Add the properties components for this ViewManager
     *
     * @param tabbedPane  the tabbed pane to add to
     */
    protected void addPropertiesComponents(JTabbedPane tabbedPane) {
        super.addPropertiesComponents(tabbedPane);
        if ( !useGlobeDisplay) {
            MapProjectionDisplay mpDisplay =
                (MapProjectionDisplay) getNavigatedDisplay();
            mpDisplay.getLatScaleInfo().setVisible(getBp(PREF_SHOWSCALES));
            mpDisplay.getLonScaleInfo().setVisible(getBp(PREF_SHOWSCALES));
            latLonScaleWidget = new LatLonScalePanel(mpDisplay);
            tabbedPane.add("Horizontal Scale",
                           GuiUtils.topLeft(latLonScaleWidget));
        }
        if (globeBackgroundDisplayable == null) {
            return;
        }


        globeBackgroundLevelSlider = new ZSlider(globeBackgroundLevel);
        JComponent levelComp = globeBackgroundLevelSlider.getContents();
        JComponent[] bgComps =
            GuiUtils.makeColorSwatchWidget(getGlobeBackgroundColorToUse(),
                                           "Globe Background Color");


        globeBackgroundColorComp = bgComps[0];
        JComponent comp = GuiUtils.formLayout(new Component[] {
                              GuiUtils.rLabel("Color:"),
                              GuiUtils.left(bgComps[0]),
                              GuiUtils.rLabel("Level:"),
                              levelComp });
        tabbedPane.add("Globe Background", GuiUtils.top(comp));
    }


    /**
     * Set the globe background
     *
     * @param globe  the globe display
     */
    private void setGlobeBackground(GlobeDisplay globe) {
        try {
            if (globeBackgroundDisplayable == null) {
                FlatField ff = ucar.visad.Util.makeField(-180, 180, 180, 90,
                                   -90, 180, 1, "celsius");
                Data d = ff;
                globeBackgroundDisplayable = new LineDrawing("background");
                globeBackgroundDisplayable.setData(d);
                globe.addDisplayable(globeBackgroundDisplayable);
            }

            //            Color c = new Color(globeBackgroundColor.getRed(),
            //                                globeBackgroundColor.getGreen(),
            //                                globeBackgroundColor.getBlue(),
            //                                (int)(255*0.5));

            globeBackgroundDisplayable.setColor(
                getGlobeBackgroundColorToUse());
            globeBackgroundDisplayable.setVisible(getGlobeBackgroundShow());

    }
            DisplayRealType drt          = globe.getDisplayAltitudeType();
            double[]        range        = new double[2];
            double          realPosition = 1;
            if (drt.getRange(range)) {
                double pcnt = (globeBackgroundLevel - (-1)) / 2;
                realPosition = Math.min((range[0]
                                         + (range[1] - range[0])
                                           * pcnt), range[1]);
            }

            ConstantMap constantMap = new ConstantMap(realPosition, drt);
            flythrough.driveRight();
            globeBackgroundDisplayable.addConstantMap(constantMap);
        } catch (Exception exc) {
            throw new RuntimeException(exc);

        }
    }


    /**
     * Destroy this ViewManager
     */
    public void destroy() {
        if (flythrough != null) {
            try {
                flythrough.destroy();
            } catch (Exception ignore) {}
            flythrough = null;
        }
        super.destroy();
    }



    /**
     * Show the fly through
     */
    public void showFlythrough() {
        if (flythrough == null) {
            flythrough = new Flythrough(this);
        }
        flythrough.show();
    }


    /**
     * Do a flythrough
     *
     * @param pts  the flythrough points
     */
    public void flythrough(final float[][] pts) {
        if (flythrough == null) {
            flythrough = new Flythrough(this);
        }
        flythrough.flythrough(pts);
    }


    /**
     * Flythrough the points
     *
     * @param pts  the List of points
     */
    public void flythrough(List pts) {
        if (flythrough == null) {
            flythrough = new Flythrough(this);
        }
        flythrough.flythrough(pts);
    }

    /**
     * Initialize after unpersistence
     *
     * @param idv  the IDV
     *
     * @throws RemoteException Java RMI exception
     * @throws VisADException  VisAD problem
     */
    public final void initAfterUnPersistence(IntegratedDataViewer idv)
            throws VisADException, RemoteException {
        super.initAfterUnPersistence(idv);
    }


    /**
     * Handle a change to the data in a DisplayControl
     *
     * @param display  the DisplayControl
     */
    public void displayDataChanged(DisplayControl display) {
        displayDataChanged(display, false);
    }


    /**
     * Handle a change to the data in a DisplayControl
     *
     * @param display  the DisplayControl
     * @param fromInitialLoad  if this is from the initial load
     */
    public void displayDataChanged(DisplayControl display,
                                   boolean fromInitialLoad) {
        try {
            if (getUseProjectionFromData()
                    && !getStateManager().getProperty(
                        IdvConstants.PROP_LOADINGXML, false)) {
                MapProjection mp = display.getDataProjection();

                if (getUseGlobeDisplay()
                        && !getViewpointControl().getAutoRotate()) {
                    LatLonPoint center = mp.getCenterLatLon();
                    getNavigatedDisplay().center(
                        new EarthLocationTuple(
                            center.getLatitude(), center.getLongitude(),
                            new Real(RealType.Altitude, 0)), true);
                    return;
                }


                if (displayProjectionOk(mp)) {
                    if ((mainProjection == null)
                            || !mp.equals(mainProjection)) {
                        boolean maintainViewpoint = !fromInitialLoad;
                        setMapProjection(
                            mp, true,
                            getDisplayConventions().getMapProjectionLabel(
                                mp, display), true, true, maintainViewpoint);
                        if (displayProjectionZoom != 0) {
                            getMapDisplay().zoom(displayProjectionZoom);
                        }
                    }
                }
            }
        } catch (Exception exp) {
            // ignore, don't set anything.   Uncomment for debugging
            // LogUtil.logException ( "addDisplayInfo:setMapProjection()", exp);
        }
        super.displayDataChanged(display);
    }


    /**
     * Reset projection of display based control's getDataProjection().
     * called by DisplayInfo.addDisplayable (), usually from control's init.
     *
     * @param displayInfos The List of new display infos to add
     *
     * @throws RemoteException  Java RMI Exception
     * @throws VisADException   Problem creating VisAD object
     */
    public void addDisplayInfos(List displayInfos)
            throws RemoteException, VisADException {

        if (getIsDestroyed()) {
            return;
        }

        if (displayInfos.size() == 0) {
            return;
        }

        //Check if we are adding a default map.
        DisplayControl display = displayInfos.get(0).getDisplayControl();
        if ((display instanceof MapDisplayControl)
                && ((MapDisplayControl) display).getIsDefaultMap()) {
            MapDisplayControl defaultMap = findDefaultMap();
            if ((defaultMap != null) && (defaultMap != display)) {
                defaultMap.loadNewMap((MapDisplayControl) display);
                //This rebuilds the legends, etc.
                displayControlChanged(defaultMap);
                return;
            }
        }

        displayDataChanged(display, true);


        super.addDisplayInfos(displayInfos);

    }

    /**
     * Reset projection of display based on data.
     * @deprecated  no substitute.  Use setMapProjection()
     *
     * @param data The data form the display
     * @param display The display
     */
    public void checkProjection(FieldImpl data, DisplayControl display) {
        try {
            MapProjection mp = GridUtil.getNavigation(data);
            if (mp == null) {
                return;
            }
            setMapProjection(
                mp, false,
                getDisplayConventions().getMapProjectionLabel(mp, display));
        } catch (Exception exp) {}  // do nothing - no projection in data
    }





    /**
     * Required interface for ActionEvents, to implement ActionListener
     * for the UI objects such as JButton-s and MenuItem-s
     *
     * @param event an ActionEvent
     */
    public void actionPerformed(ActionEvent event) {
        String cmd = event.getActionCommand();
        if (cmd.equals(CMD_FLY_FORWARD) && (flythrough != null)) {
            flythrough.driveForward();
        } else if (cmd.equals(CMD_FLY_BACK) && (flythrough != null)) {
            flythrough.driveBack();
        } else if (cmd.equals(CMD_FLY_LEFT) && (flythrough != null)) {
            flythrough.driveLeft();
        } else if (cmd.equals(CMD_FLY_RIGHT) && (flythrough != null)) {
        } else if (cmd.equals(CMD_NAV_ZOOMIN)) {
            getMapDisplay().zoom(ZOOM_FACTOR);
        } else if (cmd.equals(CMD_NAV_ROTATELEFT)) {
            getMapDisplay().rotateZ(-5.0);
        } else if (cmd.equals(CMD_NAV_ROTATERIGHT)) {
            getMapDisplay().rotateZ(5.0);
        } else if (cmd.equals(CMD_NAV_ZOOMOUT)) {
            getMapDisplay().zoom(1.0 / (double) ZOOM_FACTOR);
        } else if (cmd.equals(CMD_NAV_HOME)) {
            try {
                getMapDisplay().resetProjection();
            } catch (Exception exc) {}
        } else if (cmd.equals(CMD_NAV_RIGHT)) {
            getMapDisplay().translate(-TRANSLATE_FACTOR, 0.0);
        } else if (cmd.equals(CMD_NAV_LEFT)) {
            getMapDisplay().translate(TRANSLATE_FACTOR, 0.0);
        } else if (cmd.equals(CMD_NAV_UP)) {
            getMapDisplay().translate(0.0, -TRANSLATE_FACTOR);
        } else if (cmd.equals(CMD_NAV_DOWN)) {
            getMapDisplay().translate(0.0, TRANSLATE_FACTOR);
        } else if (cmd.equals(CMD_NAV_SMALLZOOMIN)) {
            getMapDisplay().zoom(ZOOM_FACTOR);
        } else if (cmd.equals(CMD_NAV_SMALLZOOMOUT)) {
            getMapDisplay().zoom(1.0 / ZOOM_FACTOR);
        } else if (cmd.equals(CMD_NAV_SMALLROTATELEFT)) {
            getMapDisplay().rotateZ(-2.0);
        } else if (cmd.equals(CMD_NAV_SMALLROTATERIGHT)) {
            getMapDisplay().rotateZ(2.0);
        } else if (cmd.equals(CMD_NAV_SMALLTILTUP)) {
            getMapDisplay().rotateX(-2.0);
        } else if (cmd.equals(CMD_NAV_SMALLTILTDOWN)) {
            getMapDisplay().rotateX(2.0);
        } else if (cmd.equals(CMD_NAV_SMALLRIGHT)) {
            getMapDisplay().translate(-0.02, 0.0);
        } else if (cmd.equals(CMD_NAV_SMALLLEFT)) {
            getMapDisplay().translate(0.02, 0.0);
        } else if (cmd.equals(CMD_NAV_SMALLUP)) {
            getMapDisplay().translate(0.0, -0.02);
        } else if (cmd.equals(CMD_NAV_SMALLDOWN)) {
            getMapDisplay().translate(0.0, 0.02);
        }

    }


    /**
     * Get the bounds that are visible
     *
     * @return bounds
     */
    public GeoLocationInfo getVisibleGeoBounds() {
        NavigatedDisplay navDisplay = getMapDisplay();
        Rectangle        screenBounds;

        screenBounds = navDisplay.getDisplayComponent().getBounds();
        if ( !getIdv().getArgsManager().getIsOffScreen()) {
            //            screenBounds = navDisplay.getComponent().getBounds();
        } else {
            //            Dimension 
        }
        double[] ulXY = getMapDisplay().getSpatialCoordinatesFromScreen(0, 0);
        double[] lrXY = getMapDisplay().getSpatialCoordinatesFromScreen(
                            screenBounds.width, screenBounds.height);

        LatLonPoint ulLLP =
            getMapDisplay().getEarthLocation(ulXY).getLatLonPoint();
        LatLonPoint lrLLP =
            getMapDisplay().getEarthLocation(lrXY).getLatLonPoint();



        double minX = Math.min(ulLLP.getLongitude().getValue(),
                               lrLLP.getLongitude().getValue());
        double maxX = Math.max(ulLLP.getLongitude().getValue(),
                               lrLLP.getLongitude().getValue());
        double minY = Math.min(ulLLP.getLatitude().getValue(),
                               lrLLP.getLatitude().getValue());
        double maxY = Math.max(ulLLP.getLatitude().getValue(),
                               lrLLP.getLatitude().getValue());
        Rectangle2D.Float rect = new Rectangle2D.Float((float) minX,
                                     (float) minY, (float) (maxX - minX),
                                     (float) (maxY - minY));

        return new GeoLocationInfo(maxY, minX, minY, maxX);
    }

    /**
     * Set the current viewpoint as the projection
     */
    public void setCurrentAsProjection() {
        try {
            NavigatedDisplay display      = getMapDisplay();
            Rectangle        screenBounds =
                display.getComponent().getBounds();
            LatLonPoint      ulLLP        = null;
            LatLonPoint      lrLLP        = null;

            int              sw           = screenBounds.width;
            int              sh           = screenBounds.height;
            int              x            = 0;
            int              y            = 0;

            while ((x < sw) && (y < sh)) {
                double[] ulXY = display.getSpatialCoordinatesFromScreen(x, y);
                ulLLP =
                    getMapDisplay().getEarthLocation(ulXY).getLatLonPoint();
                if ( !ulLLP.getLatitude().isMissing()
                        && !ulLLP.getLongitude().isMissing()) {
                    break;
                }
                ulLLP = null;
                x++;
                y++;
            }

            while ((sw > 0) && (sh > 0)) {
                double[] lrXY = display.getSpatialCoordinatesFromScreen(sw,
                                    sh);
                lrLLP =
                    getMapDisplay().getEarthLocation(lrXY).getLatLonPoint();
                if ( !lrLLP.getLatitude().isMissing()
                        && !lrLLP.getLongitude().isMissing()) {
                    break;
                }
                lrLLP = null;
                sw--;
                sh--;
            }

            if ((ulLLP == null) || (lrLLP == null)) {
                LogUtil.userMessage("Could not create a valid projection");
                return;
            }


            double minX = Math.min(ulLLP.getLongitude().getValue(),
                                   lrLLP.getLongitude().getValue());
            double maxX = Math.max(ulLLP.getLongitude().getValue(),
                                   lrLLP.getLongitude().getValue());
            double minY = Math.min(ulLLP.getLatitude().getValue(),
                                   lrLLP.getLatitude().getValue());
            double maxY = Math.max(ulLLP.getLatitude().getValue(),
                                   lrLLP.getLatitude().getValue());
            Rectangle2D.Float rect = new Rectangle2D.Float((float) minX,
                                         (float) minY, (float) (maxX - minX),
                                         (float) (maxY - minY));

            MapProjection mp = ucar.visad.Util.makeMapProjection(minY, minX,
                                   maxY, maxX);
            setMapProjection(mp, true);
            //            getMapDisplay().zoom(ZOOM_FACTOR);
            getMapDisplay().saveProjection();
        } catch (Exception exp) {
            logException("Setting projection", exp);
        }
    }


    /**
     * Show the projection manager.
     */
    public void showProjectionManager() {
        getIdv().showIdvProjectionManager();
    }




    /**
     * Set or reset map area of view, using NavigatedDisplay method.
     *
     * @param mapArea ProjectionRect the map area of view
     */
    public void setMapArea(ProjectionRect mapArea) {
        try {
            getMapDisplay().setMapArea(mapArea);
        } catch (Exception e) {
            logException("setMapArea", e);
        }
    }

    /**
     * Check if the display projection is okay
     *
     * @param mp  map projection to check
     *
     * @return true if okay
     */
    private boolean displayProjectionOk(MapProjection mp) {
        if (mp == null) {
            return false;
        }
        if (doNotSetProjection) {
            return false;
        }

        Rectangle2D rect = mp.getDefaultMapArea();
        if ((rect.getWidth() == 0) || (rect.getHeight() == 0)) {
            return false;
        } else {
            if (rect.getWidth() / rect.getHeight() < 0.1) {
                return false;
            } else if (rect.getHeight() / rect.getWidth() < 0.1) {
                return false;
            }
        }
        return true;
    }



    /**
     * Init menu
     *
     * @param projectionsMenu menu
     */
    public void initializeProjectionMenu(JMenu projectionsMenu) {
        List projections = getProjectionList();
        List controls    = getControls();
        if ( !getUseGlobeDisplay()) {
            //            projectionsMenu.add(GuiUtils.makeMenuItem("Use Displayed Area",
            //                    this, "setCurrentAsProjection"));
        }
        ProjectionImpl currentProjection = null;
        if ((mainProjection != null)
                && (mainProjection instanceof ProjectionCoordinateSystem)) {
            currentProjection =
                ((ProjectionCoordinateSystem) mainProjection).getProjection();
        }


        makeProjectionsMenu(projectionsMenu, projections, this,
                            "setProjection", currentProjection);
    }



    /**
     * Init menu
     *
     * @param displaysMenu menu
     */
    public void initializeDisplaysProjectionMenu(JMenu displaysMenu) {
        List controls = getControls();
        int  cnt      = 0;
        for (int i = 0; i < controls.size(); i++) {
            final DisplayControl control = (DisplayControl) controls.get(i);
            final MapProjection  mp      = control.getDataProjection();
            if ( !displayProjectionOk(mp)) {
                continue;
            }

            final String label =
                getDisplayConventions().getMapProjectionLabel(mp, control);
            JMenuItem mi = new JMenuItem(label);
            displaysMenu.add(mi);
            mi.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent ae) {
                    setMapProjection(mp, false, label);
                }
            });
            cnt++;
        }
        if (cnt == 0) {
            displaysMenu.add(new JMenuItem("None Defined"));
        }
    }




    /**
     * Make the projections menu. Call the method on the given object.
     *
     * @param projectionsMenu Menu to add to
     * @param projections List of projections
     * @param object object to call
     * @param method method to call
     */
    public static void makeProjectionsMenu(JMenu projectionsMenu,
                                           List projections, Object object,
                                           String method) {
        makeProjectionsMenu(projectionsMenu, projections, object, method,
                            null);
    }


    /**
     * Make the projections menu
     *
     * @param projectionsMenu the menu to add to
     * @param projections list of projections
     * @param object object to call
     * @param method  method to call
     * @param currentProjection current projection
     */
    public static void makeProjectionsMenu(JMenu projectionsMenu,
                                           List projections, Object object,
                                           String method,
                                           ProjectionImpl currentProjection) {
        if (currentProjection != null) {
            List names = StringUtil.split(currentProjection.getName(), ">",
                                          true, true);
            if (names.size() > 0) {
                String name = "Current: "
                              + (String) names.get(names.size() - 1);
                JMenuItem mi = GuiUtils.makeMenuItem(name, object, method,
                                   currentProjection);

                projectionsMenu.add(mi);
                projectionsMenu.addSeparator();
            }
        }

        Hashtable catMenus = new Hashtable();
        for (int i = 0; i < projections.size(); i++) {
            ProjectionImpl p = (ProjectionImpl) projections.get(i);
            List names = StringUtil.split(p.getName(), ">", true,
                                     true);
            JMenu  theMenu  = projectionsMenu;
            String catSoFar = "";
            int    catIdx   = 0;
            for (catIdx = 0; catIdx < names.size() - 1; catIdx++) {
                String cat = (String) names.get(catIdx);
                catSoFar += "-" + cat;
                JMenu tmpMenu = (JMenu) catMenus.get(catSoFar);
                if (tmpMenu == null) {
                    tmpMenu = new JMenu(cat);
                    catMenus.put(catSoFar, tmpMenu);
                    theMenu.add(tmpMenu);
                }
                theMenu = tmpMenu;
            }
            String  name      = ((catIdx < names.size())
                                 ? names.get(catIdx)
                                 : "");
            boolean isCurrent = Misc.equals(p, currentProjection);
            if (isCurrent) {
                //              name = "> " + name;
            }
            JMenuItem mi = GuiUtils.makeMenuItem(name, object, method, p);
            theMenu.add(mi);
            if (isCurrent) {
                GuiUtils.italicizeFont(mi.getComponent());
            }
        }


    }




    /**
     * Init menu
     *
     * @param menu menu
     */
    public void initializeProjectionHistoryMenu(JMenu menu) {
        menu.removeAll();
        for (int i = 0; i < projectionHistory.size(); i++) {
            final TwoFacedObject tfo =
                (TwoFacedObject) projectionHistory.get(i);
            JMenuItem mi = new JMenuItem(tfo.toString());
            mi.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    try {
                        String encodedProjection = (String) tfo.getId();
                        MapProjection mp =
                            (MapProjection) getIdv().decodeObject(
                                encodedProjection);
                        setMapProjection(mp, true);
                    } catch (Exception exc) {
                        logException("Failed to instantiate the projection",
                                     exc);
                    }
                }
            });
            menu.add(mi);
        }
    }


    /**
     * Is this a compatible ViewManager
     *

     * @param vm  the other
     *
     * @return  true if compatible
     */
    public boolean isCompatibleWith(ViewManager vm) {
        if ( !super.isCompatibleWith(vm)) {
            return false;
        }
        MapViewManager that = (MapViewManager) vm;
        return this.getUseGlobeDisplay() == that.getUseGlobeDisplay();
    }


    /**
     * Is this compatible with the ViewState
     *
     * @param viewState  the view state
     *
     * @return  true if compatible
     */
    public boolean isCompatibleWith(ViewState viewState) {
        if ( !super.isCompatibleWith(viewState)) {
            return false;
        }
        Boolean b = (Boolean) viewState.get(ViewState.PROP_GLOBE);
        if (b != null) {
            return getUseGlobeDisplay() == b.booleanValue();
        }
        return true;
    }


    /**
     * Make the "Projections" menu to be added to the menu bar, which provides
     * controls for maps.
     * @return JMenu
     */
    private JMenu makeProjectionMenu() {
        JMenu projMenu = new JMenu("Projections");
        projMenu.setMnemonic(GuiUtils.charToKeyCode("P"));
        projectionsMenu = GuiUtils.makeDynamicMenu("Predefined", this,
                "initializeProjectionMenu");
        JMenu displaysMenu = GuiUtils.makeDynamicMenu("From Displays", this,
                                 "initializeDisplaysProjectionMenu");

        if ( !getUseGlobeDisplay()) {
            projMenu.add(projectionsMenu);
            projMenu.add(displaysMenu);
        }
        projMenu.add(makeSavedViewsMenu());
        JMenu projectionHistoryMenu = GuiUtils.makeDynamicMenu("History",
                                          this,
                                          "initializeProjectionHistoryMenu");

        if ( !getUseGlobeDisplay()) {
            projMenu.add(projectionHistoryMenu);
        }


        if ( !getUseGlobeDisplay()) {
            projMenu.addSeparator();
            projMenu.add(GuiUtils.setIcon(GuiUtils.makeMenuItem("New/Edit...",
                    this,
                    "showProjectionManager"), "/auxdata/ui/icons/world_edit.png"));
            projMenu.add(GuiUtils.setIcon(GuiUtils.makeMenuItem("Use Displayed Area",
                    this,
                    "setCurrentAsProjection"), "/auxdata/ui/icons/world_rect.png"));
        }
        projMenu.add(GuiUtils.setIcon(GuiUtils.makeMenuItem("Go to Address",
                this, "goToAddress"), "/auxdata/ui/icons/house_go.png"));


        projMenu.addSeparator();
        if ( !getUseGlobeDisplay()) {
            createCBMI(projMenu, PREF_PROJ_USEFROMDATA).setToolTipText(
                "Automatically change the projection to the native data projection of new displays");
        } else {
            createCBMI(projMenu, PREF_PROJ_USEFROMDATA).setToolTipText(
                "Automatically change viewpoint to the native data projection of new displays");
        }
        createCBMI(projMenu, PREF_USE_PROGRESSIVE_RESOLUTION).setToolTipText("" +
        		"Progressively disclose higher resolution data on zoom");
        createCBMI(projMenu, PREF_SHAREVIEWS);

        projMenu.add(GuiUtils.makeMenuItem("Set Share Group", this,
                                           "showSharableDialog"));
        return projMenu;
    }


    /**
     * Have this so we don't get warnings on unpersisting old bundles
     *
     * @param location The location
     */
    public void setMapConfigFile(String location) {}


    /**
     * Use globe display. Used for xml encoding/decoding.
     *
     * @param use The globe display value
     */
    public void setUseGlobeDisplay(boolean use) {
        useGlobeDisplay = use;
    }

    /**
     * Get the globe display flag. Used for xml encoding/decoding.
     *
     * @return The globe display value
     */
    public boolean getUseGlobeDisplay() {
        return useGlobeDisplay;
    }


    /**
     * Use a 3D display. Used for xml encoding/decoding.
     *
     * @param use The use 3D display value
     */
    public void setUse3D(boolean use) {
        use3D = use;
    }

    /**
     * Get the use 3D display flag. Used for xml encoding/decoding.
     *
     * @return The use 3D value
     */
    public boolean getUse3D() {
        return use3D;
    }



    /**
     * The BooleanProperty identified byt he given id has changed.
     * Apply the change to the display.
     *
     * @param id Id of the changed BooleanProperty
     * @param value Its new value
     *
     * @throws Exception problem handeling the change
     */
    protected void handleBooleanPropertyChange(String id, boolean value)
            throws Exception {
        super.handleBooleanPropertyChange(id, value);
        if (id.equals(PREF_AUTOROTATE)) {
            if (hasViewpointControl()) {
                getViewpointControl().setAutoRotate(value);
            }
        } else if (id.equals(PREF_SHOWSCALES)) {
            if (hasDisplayMaster()) {
                getNavigatedDisplay().setScalesVisible(value);
            }
        } else if (id.equals(PREF_SHOWEARTHNAVPANEL)) {
            if (earthNavPanelWrapper != null) {
                earthNavPanelWrapper.removeAll();
                if (value) {
                    earthNavPanelWrapper.add(BorderLayout.CENTER,
                                             earthNavPanel);
                }
            }
        } else if (id.equals(PREF_SHOWPIP)) {
            if (pipPanelWrapper != null) {
                pipPanelWrapper.setVisible(value);
            }
        } else if (id.equals(PREF_USE_PROGRESSIVE_RESOLUTION)) {
        	// TODO: what do we want to do?
        } else if (id.equals(PREF_SHOWGLOBEBACKGROUND)) {
            if (globeBackgroundDisplayable != null) {
                globeBackgroundDisplayable.setVisible(value);
            }
        } else if (id.equals(PREF_PERSPECTIVEVIEW)) {
            if (hasViewpointControl()) {
                getViewpointControl().setPerspectiveView(value);
            }
        }
    }

    /**
     * Apply preferences
     */
    public void applyPreferences() {
        super.applyPreferences();
        applyAxisVisibility();
    }

    /**
     * Create the set of {@link ucar.unidata.util.BooleanProperty}s.
     * These hold all of the different flag based display state.
     *
     * @param props the list of properties
     */
    protected void getInitialBooleanProperties(List props) {
        super.getInitialBooleanProperties(props);
        props.add(new BooleanProperty(PREF_SHOWSCALES, "Show Display Scales",
                                      "Show Display Scales", false));

        props.add(
            new BooleanProperty(
                PREF_PROJ_USEFROMDATA, "Auto-set Projection",
                "Use projection from newly loaded data", true));
        props.add(new BooleanProperty(PREF_PERSPECTIVEVIEW,
                                      "Perspective View",
                                      "Toggle perspective view", false));
        props.add(new BooleanProperty(PREF_AUTOROTATE, "Auto-rotate", "",
                                      false));
        props.add(new BooleanProperty(PREF_SHOWEARTHNAVPANEL,
                                      "Show Earth Navigation Panel",
                                      "Show Earth Navigation Panel", false));

        props.add(new BooleanProperty(PREF_SHOWPIP, "Show Overview Map",
                                      "Show Overview Map", false));

        props.add(new BooleanProperty(PREF_USE_PROGRESSIVE_RESOLUTION, 
        		                      PR_LABEL,
                                      PR_LABEL, true));

        if (useGlobeDisplay) {
            props.add(new BooleanProperty(PREF_SHOWGLOBEBACKGROUND,
                                          "Show Globe Background",
                                          "Show Globe Background",
                                          defaultGlobeBackground));
        }
    }



    /**
     * Set the autorotate property
     *
     * @param value The value
     */
    public void setAutoRotate(boolean value) {
        setBp(PREF_AUTOROTATE, value);
    }

    /**
     * Get  the autorotate flag
     * @return The flag value
     */
    public boolean getAutoRotate() {
        return getBp(PREF_AUTOROTATE);
    }

    /**
     * Set the  perspective view flag
     *
     * @param value The value
     */
    public void setPerspectiveView(boolean value) {
        setBp(PREF_PERSPECTIVEVIEW, value);
    }

    /**
     * Get  the perspective view  flag
     * @return The flag value
     */
    public boolean getPerspectiveView() {
        return getBp(PREF_PERSPECTIVEVIEW);
    }


    /**
     * Dummy for old bundles
     *
     * @param value The value
     */
    public void setShowMap(boolean value) {}


    /**
     * Dummy for old bundles
     *
     * @param value The value
     */
    public void setShowElevation(boolean value) {}

    /**
     * _more_
     *
     * @param value _more_
     */
    public void setProjectionFromData(boolean value) {
        setUseProjectionFromData(value);
    }

    /**
     * Set the  use projection from data flag
     *
     * @param value The value
     */
    public void setUseProjectionFromData(boolean value) {
        setBp(PREF_PROJ_USEFROMDATA, value);
    /**
     * Get  the use projection from data  flag
     * @return The flag value
     */
    public boolean getUseProjectionFromData() {
        //   if ( !isInteractive()) {
        //       return true;
        //   }
        return getBp(PREF_PROJ_USEFROMDATA);
    }


    /**
     * Set the background color property.
     * @deprecated  Keep this around for old bundles
     * @param bgColor The value
     */
    public void setBgColor(boolean bgColor) {
        if ( !bgColor) {
            setBackground(Color.white);
            setForeground(Color.black);
        } else {
            setBackground(Color.black);
            setForeground(Color.white);
        }
    }


    /**
     * Set the ShowEarthNavPanel property.
     *
     * @param value The new value for ShowEarthNavPanel
     */
    public void setShowEarthNavPanel(boolean value) {
        setBp(PREF_SHOWEARTHNAVPANEL, value);
    }

    /**
     * Get the ShowEarthNavPanel property.
     *
     * @return The ShowEarthNavPanel
     */
    public boolean getShowEarthNavPanel() {
        return getBp(PREF_SHOWEARTHNAVPANEL);
    }


    /**
     * Hide the pip panel
     */
    public void hidePip() {
        setShowPip(false);
    }

    /**
     * Set the ShowPipPanel property.
     *
     * @param value The new value for ShowPipPanel
     */
    public void setShowPip(boolean value) {
        setBp(PREF_SHOWPIP, value);
    }

    /**
     * Get the ShowPipPanel property.
     *
     * @return The ShowPipPanel
     */
    public boolean getShowPip() {
        return getBp(PREF_SHOWPIP, false);
    }

    /**
     * Set the Progressive Resolution property.
     *
     * @param value The new value for Progressive Resolution
     */
    public void setUseProgressiveResolution(boolean value) {
        setBp(PREF_USE_PROGRESSIVE_RESOLUTION, value);
    }

    /**
     * Get the ProgressiveResolution property.
     *
     * @return The ProgressiveResolution
     */
    public boolean getUseProgressiveResolution() {
        return getBp(PREF_USE_PROGRESSIVE_RESOLUTION, true);
    }


    /**
     * Set the InitialMapResources property. This gets set
     * by the viewmanager properties and is a comma separated
     * list of map resource paths.
     *
     * @param value The new value for InitialMapResources
     */
    public void setInitialMapResources(String value) {
        initialMapResources = value;
    }



    /**
     * What type of view is this
     *
     * @return The type of view
     */
    public String getTypeName() {
        if (getUseGlobeDisplay()) {
            return "Globe";
        }
        return "Map";
    }



    /**
     * Get the default map position from a property
     *
     * @return the value in the range of -1 to 1
     */
    public float getDefaultMapPosition() {
        if (getUseGlobeDisplay()) {
            return (float) getStateManager().getProperty(
                IdvConstants.PROP_MAP_GLOBE_LEVEL, 0.005f);
        } else {
            return (float) getStateManager().getProperty(
                IdvConstants.PROP_MAP_MAP_LEVEL, -0.99f);
        }
    }

    /**
     *  Set the GlobeBackgroundColor property.
     *
     *  @param value The new value for GlobeBackgroundColor
     */
    public void setGlobeBackgroundColor(Color value) {
        globeBackgroundColor = value;
    }


    /**
     *  Get the GlobeBackgroundColor property.
     *
     *  @return The GlobeBackgroundColor
     */
    public Color getGlobeBackgroundColor() {
        return globeBackgroundColor;
    }

    /**
     *  Get the GlobeBackgroundColor property to be used. If it has not been set then get the preference
     *
     *  @return The GlobeBackgroundColor to use
     */
    public Color getGlobeBackgroundColorToUse() {
        Color backgroundColor = globeBackgroundColor;
        if (backgroundColor == null) {
            backgroundColor = getStore().get(PREF_GLOBEBACKGROUND,
                                             Color.white);
        }
        return backgroundColor;
    }


    /**
     *  Set the GlobeBackgroundShow property.
     *
     *  @param value The new value for GlobeBackgroundShow
     */
    public void setGlobeBackgroundShow(boolean value) {
        defaultGlobeBackground = value;
        setBp(PREF_SHOWGLOBEBACKGROUND, value);
    }

    /**
     *  Get the GlobeBackgroundShow property.
     *
     *  @return The GlobeBackgroundShow
     */
    public boolean getGlobeBackgroundShow() {
        if (hasBooleanProperty(PREF_SHOWGLOBEBACKGROUND)) {
            return getBp(PREF_SHOWGLOBEBACKGROUND, false);
        }
        XmlObjectStore store = getStore();
        if (store != null) {
            return store.get(PREF_SHOWGLOBEBACKGROUND, false);
        }
        return false;
    }

    /**
     *  Set the GlobeBackgroundLevel property.
     *
     *  @param value The new value for GlobeBackgroundLevel
     */
    public void setGlobeBackgroundLevel(double value) {
        globeBackgroundLevel = value;
    }

    /**
     *  Get the GlobeBackgroundLevel property.
     *
     *  @return The GlobeBackgroundLevel
     */
    public double getGlobeBackgroundLevel() {
        return globeBackgroundLevel;
    }

    /**
     * Set the Flythrough property.
     *
     * @param value The new value for Flythrough
     */
    public void setFlythrough(Flythrough value) {
        this.flythrough = value;
    }

    /**
     * Get the Flythrough property.
     *
     * @return The Flythrough
     */
    public Flythrough getFlythrough() {
        return this.flythrough;
    }


    /**
     *  Set the ShowMaps property.
     *
     *  @param value The new value for ShowMaps
     */
    public void setShowMaps(boolean value) {
        this.showMaps = value;
    }

    /**
     *  Get the ShowMaps property.
     *
     *  @return The ShowMaps
     */
    public boolean getShowMaps() {
        return this.showMaps;
    }





    /**
     *  Set the DisplayProjectionZoom property.
     *
     *  @param value The new value for DisplayProjectionZoom
     */
    public void setDisplayProjectionZoom(double value) {
        this.displayProjectionZoom = value;
    }

    /**
     *  Get the DisplayProjectionZoom property.
     *
     *  @return The DisplayProjectionZoom
     */
    public double getDisplayProjectionZoom() {
        return this.displayProjectionZoom;
    }

    /**
     *  Set the InitMapPaths property.
     *
     *  @param value The new value for InitMapPaths
     */
    public void setInitMapPaths(String value) {
        this.initMapPaths = value;
    }


    /**
     *  Get the InitMapPaths property.
     *
     *  @return The InitMapPaths
     */
    public String getInitMapPaths() {
        return this.initMapPaths;
    }

    /**
     * Set the intial lat/lon visible
     *
     * @param v  true or false
     */
    public void setInitLatLonVisible(boolean v) {
        initLatLonVisible = v;
    }



    /**
     * Set the intial lat/lon color
     *
     * @param v  the color
     */
    public void setInitLatLonColor(Color v) {
        initLatLonColor = v;
    }


    /**
     * Set the initial lat/lon line width
     *
     * @param v  the width
     */
    public void setInitLatLonWidth(int v) {
        initLatLonWidth = v;
    }

    /**
     * Set the initial lat/lon spacing
     *
     * @param v  the spacing
     */
    public void setInitLatLonSpacing(float v) {
        initLatLonSpacing = v;
    }

    /**
     * Set the InitMapWidth property.
     *
     * @param value The new value for InitMapWidth
     */
    public void setInitMapWidth(float value) {
        this.initMapWidth = value;
    }

    /**
     * Get the InitMapWidth property.
     *
     * @return The InitMapWidth
     */
    public float getInitMapWidth() {
        return this.initMapWidth;
    }

    /**
     * Set the InitMapColor property.
     *
     * @param value The new value for InitMapColor
     */
    public void setInitMapColor(Color value) {
        this.initMapColor = value;
    }

    /**
     * Get the InitMapColor property.
     *
     * @return The InitMapColor
     */
    public Color getInitMapColor() {
        return this.initMapColor;
    }

    /**
     * Set the InitLatLonBounds property.
     *
     * @param value The new value for InitLatLonBounds
     */
    public void setInitLatLonBounds(Rectangle2D.Float value) {
        this.initLatLonBounds = value;
    }

    /**
     * Get the InitLatLonBounds property.
     *
     * @return The InitLatLonBounds
     */
    public Rectangle2D.Float getInitLatLonBounds() {
        return this.initLatLonBounds;
    }

    /**
     * Gets the lat axis scale info.
     *
     * @return the lat axis scale info
     */
    public LatLonAxisScaleInfo getLatAxisScaleInfo() {
        if ( !hasDisplayMaster()) {
            return latAxisScaleInfo;
        }

        if ( !useGlobeDisplay) {
            MapProjectionDisplay d =
                (MapProjectionDisplay) getNavigatedDisplay();
            return d.getLatScaleInfo();
        } else {
            return null;
        }
    }

    /**
     * Sets the lat axis scale info.
     *
     * @param axisScaleInfo
     *            the new lat axis scale info
     * @throws RemoteException
     *             the remote exception
     * @throws VisADException
     *             the VisAD exception
     * @deprecated
     * public void setLatAxisScaleInfo(AxisScaleInfo axisScaleInfo)
     *       throws RemoteException, VisADException {
     *
     *   setLatAxisScaleInfo((LatLonAxisScaleInfo) axisScaleInfo);
     * }
     */

    /**
     * Sets the lat axis scale info.
     *
     * @param axisScaleInfo
     *            the new lat axis scale info
     * @throws RemoteException
     *             the remote exception
     * @throws VisADException
     *             the VisAD exception
     */
    public void setLatAxisScaleInfo(LatLonAxisScaleInfo axisScaleInfo)
            throws RemoteException, VisADException {

        this.latAxisScaleInfo = axisScaleInfo;

        if ( !hasDisplayMaster()) {
            return;
        }

        if ( !useGlobeDisplay) {
            MapProjectionDisplay d =
                (MapProjectionDisplay) getNavigatedDisplay();
            d.setLatScaleInfo(axisScaleInfo);
        }
    }

    /**
     * Gets the lon axis scale info.
     *
     * @return the lon axis scale info
     */
    public LatLonAxisScaleInfo getLonAxisScaleInfo() {
        if ( !hasDisplayMaster()) {
            return lonAxisScaleInfo;
        }

        if ( !useGlobeDisplay) {
            MapProjectionDisplay d =
                (MapProjectionDisplay) getNavigatedDisplay();
            return d.getLonScaleInfo();
        } else {
            return null;
        }
    }

    /**
     * Sets the lon axis scale info.
     *
     * @param axisScaleInfo
     *            the new lon axis scale info
     * @throws RemoteException
     *             the remote exception
     * @throws VisADException
     *             the vis ad exception
     * @deprecated
     * public void setLonAxisScaleInfo(AxisScaleInfo axisScaleInfo)
     *       throws RemoteException, VisADException {
     *   setLonAxisScaleInfo((LatLonAxisScaleInfo) axisScaleInfo);
     * }
     */

    /**
     * Sets the lon axis scale info.
     *
     * @param axisScaleInfo
     *            the new lon axis scale info
     * @throws RemoteException
     *             the remote exception
     * @throws VisADException
     *             the vis ad exception
     */
    public void setLonAxisScaleInfo(LatLonAxisScaleInfo axisScaleInfo)
            throws RemoteException, VisADException {

        this.lonAxisScaleInfo = axisScaleInfo;

        if ( !hasDisplayMaster()) {
            return;
        }

        if ( !useGlobeDisplay) {
            MapProjectionDisplay d =
                (MapProjectionDisplay) getNavigatedDisplay();
            d.setLonScaleInfo(axisScaleInfo);
        }
    }




}
=======
/*
 * Copyright 1997-2013 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 * 
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package ucar.unidata.idv;


import ucar.unidata.collab.Sharable;
import ucar.unidata.data.GeoLocationInfo;
import ucar.unidata.data.grid.GridUtil;
import ucar.unidata.geoloc.ProjectionImpl;
import ucar.unidata.geoloc.ProjectionRect;
import ucar.unidata.gis.maps.MapData;
import ucar.unidata.gis.maps.MapInfo;
import ucar.unidata.idv.control.MapDisplayControl;
import ucar.unidata.idv.control.ZSlider;
import ucar.unidata.idv.flythrough.Flythrough;
import ucar.unidata.idv.flythrough.FlythroughPoint;
import ucar.unidata.idv.ui.ContourInfoDialog;
import ucar.unidata.idv.ui.EarthNavPanel;
import ucar.unidata.idv.ui.IdvUIManager;
import ucar.unidata.idv.ui.PipPanel;
import ucar.unidata.ui.Command;
import ucar.unidata.ui.FontSelector;
import ucar.unidata.util.BooleanProperty;
import ucar.unidata.util.ContourInfo;
import ucar.unidata.util.FileManager;
import ucar.unidata.util.GuiUtils;
import ucar.unidata.util.LogUtil;
import ucar.unidata.util.Misc;
import ucar.unidata.util.StringUtil;
import ucar.unidata.util.Trace;
import ucar.unidata.util.TwoFacedObject;
import ucar.unidata.view.geoloc.GlobeDisplay;
import ucar.unidata.view.geoloc.LatLonAxisScaleInfo;
import ucar.unidata.view.geoloc.LatLonScalePanel;
import ucar.unidata.view.geoloc.MapProjectionDisplay;

import ucar.unidata.view.geoloc.NavigatedDisplay;
import ucar.unidata.view.geoloc.ViewpointInfo;
import ucar.unidata.xml.PreferenceManager;
import ucar.unidata.xml.XmlObjectStore;
import ucar.unidata.xml.XmlResourceCollection;
import ucar.unidata.xml.XmlUtil;

import ucar.visad.GeoUtils;
import ucar.visad.ProjectionCoordinateSystem;
import ucar.visad.display.DisplayMaster;
import ucar.visad.display.LineDrawing;

import visad.ConstantMap;
import visad.ContourControl;
import visad.Data;
import visad.DisplayEvent;
import visad.DisplayRealType;
import visad.FieldImpl;
import visad.FlatField;
import visad.MouseBehavior;
import visad.Real;
import visad.RealTupleType;
import visad.RealType;
import visad.VisADException;

import visad.georef.EarthLocation;
import visad.georef.EarthLocationTuple;
import visad.georef.LatLonPoint;
import visad.georef.MapProjection;
import visad.georef.TrivialMapProjection;


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.Rectangle2D;

import java.rmi.RemoteException;

import java.text.Collator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
import java.util.TreeSet;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;





/**
 * A wrapper around a MapProjectDisplay display master.
 * Provides an interface for managing user interactions, gui creation, etc.
 *
 * @author IDV development team
 */

public class MapViewManager extends NavigatedViewManager {

    /** front globe clipping distance */
    public static final String PROP_CLIPDISTANCE_GLOBE_FRONT =
        "idv.clipdistance.globe.front";

    /** back globe clipping distance */
    public static final String PROP_CLIPDISTANCE_GLOBE_BACK =
        "idv.clipdistance.globe.back";

    /** fron map clipping distance */
    public static final String PROP_CLIPDISTANCE_MAP_FRONT =
        "idv.clipdistance.map.front";

    /** back map clipping distance */
    public static final String PROP_CLIPDISTANCE_MAP_BACK =
        "idv.clipdistance.map.back";


    /** flythrough command */
    public static final String CMD_FLY_LEFT = "cmd.fly.left";

    /** flythrough command */
    public static final String CMD_FLY_RIGHT = "cmd.fly.right";
    /** flythrough command */
    public static final String CMD_FLY_FORWARD = "cmd.fly.forward";

    /** flythrough command */
    public static final String CMD_FLY_BACK = "cmd.fly.back";

    /** preference id for the list of addresses in the geocode dialog */
    public static final String PREF_ADDRESS_LIST = "view.address.list";

    /** do we reproject when we go to an address */
    public static final String PREF_ADDRESS_REPROJECT =
        "view.address.reproject";

    /** Preference for autorotate in globe mode */
    public static final String PREF_AUTOROTATE = "View.AutoRotate";

    /** Preference for  showing display in perspective view_ */
    public static final String PREF_PERSPECTIVEVIEW = "View.PerspectiveView";

    /** Preference for  default projection */
    public static final String PREF_PROJ_DFLT = "View.ProjectionDflt";

    /** Preference for  setting projection automatically from data_ */
    public static final String PREF_PROJ_USEFROMDATA = "View.UseFromData";

    /** Preference for  showing the pip */
    public static final String PREF_SHOWPIP = "View.ShowPip";

    /** Preference for showing the globe background */
    public static final String PREF_SHOWGLOBEBACKGROUND =
        "View.ShowGlobeBackground";

    /** Preference for the globe background  color */
    public static final String PREF_GLOBEBACKGROUND = "View.GlobeBackground";

    /** Preference for  showing the earth nav panel */
    public static final String PREF_SHOWEARTHNAVPANEL =
        "View.ShowEarthNavPanel";


    /** Defines the projection when sharing state */
    public static final String SHARE_PROJECTION =
        "MapViewManager.SHARE_PROJECTION";

    /**
     * This got set from the ViewManager properties. It is a comma
     * delimited list of map resources
     */
    private String initialMapResources;


    /** The display projection we are currently using */
    private MapProjection mainProjection;

    /** The name of the display projection we are currently using */
    private String mainProjectionName = null;

    /** The name of the default projection */
    private String defaultProjectionName = null;


    /** Keep track of the projections we have used */
    private ArrayList projectionHistory = new ArrayList();

    /** Main projections menu */
    private JMenu projectionsMenu;

    /** Big blob of xml map state from the map widget */
    private String mapState;

    /** Are we using the globe display */
    private boolean useGlobeDisplay = false;

    /** Are we 2d or 3d */
    private boolean use3D = true;

    /** The earth nav panel */
    EarthNavPanel earthNavPanel;

    /** Where the earth nav panel goes */
    JPanel earthNavPanelWrapper;

    /** The map panel in the GUI */
    private PipPanel pipPanel;

    /** mutex for dealing with the pip map */
    private Object PIP_MUTEX = new Object();

    /** Holds the pip map */
    private JComponent pipPanelWrapper;


    /** Do we reproject when we goto address */
    private static JCheckBox addressReprojectCbx;

    /** For checking if kmz capture is ok */
    private JCheckBox fixViewpointCbx;

    /** For checking if kmz capture is ok */
    private JCheckBox fixProjectionCbx;

    /** rotate button */
    JToggleButton rotateBtn;

    /** contour info dialog for preferences */
    ContourInfoDialog cid;

    /** background color for filled globe */
    private Color globeBackgroundColor = null;


    /** z level (really radius) for where to put the globe fill layer */
    private double globeBackgroundLevel = -0.01;

    /** globe fill background stuff */
    private LineDrawing globeBackgroundDisplayable;

    /** globe fill background stuff */
    private JComponent globeBackgroundColorComp;

    /** globe fill background stuff */
    private ZSlider globeBackgroundLevelSlider;


    /** The flythrough */
    private Flythrough flythrough;

    /** show maps flag */
    private boolean showMaps = true;

    /** init maps flag */
    private String initMapPaths;

    /** initial map width */
    private float initMapWidth = -1;

    /** initial map color */
    private Color initMapColor = null;

    /** initial lat/lon visibility */
    private boolean initLatLonVisible = false;

    /** initial lat/lon width */
    private int initLatLonWidth = 1;

    /** initial lat/lon spacing */
    private float initLatLonSpacing = 15;

    /** initial lat/lon color */
    private Color initLatLonColor = Color.white;

    /** initial lat/lon bounds */
    private Rectangle2D.Float initLatLonBounds;

    /** use default globe background flag */
    private boolean defaultGlobeBackground = false;

    /** initial display projection zoom */
    private double displayProjectionZoom = 0;

    /** do not set projection flag */
    private boolean doNotSetProjection = false;

    /** lat/lon scale widget */
    private LatLonScalePanel latLonScaleWidget;

    /** Lat axis scale info for unpersistence */
    private LatLonAxisScaleInfo latAxisScaleInfo;

    /** Lon axis scale info for unpersistence */
    private LatLonAxisScaleInfo lonAxisScaleInfo;

    /**
     *  Default constructor
     */
    public MapViewManager() {}


    /**
     * Construct a MapViewManager from an IDV
     *
     * @param viewContext Really the IDV
     */
    public MapViewManager(ViewContext viewContext) {
        super(viewContext);
    }

    /**
     * Construct a MapViewManager with the specified params
     * @param viewContext   context in which this MVM exists
     * @param desc   ViewDescriptor
     * @param properties   semicolon separated list of properties (can be null)
     *
     * @throws RemoteException Java RMI problem
     * @throws VisADException  Couldn't create the VisAD object
     */
    public MapViewManager(ViewContext viewContext, ViewDescriptor desc,
                          String properties)
            throws VisADException, RemoteException {
        super(viewContext, desc, properties);
    }


    /**
     * Get the default projection to use
     *
     * @return The default projection
     */
    public ProjectionImpl getDefaultProjection() {
        return getIdv().getIdvProjectionManager().getDefaultProjection();
    }




    /**
     * Make the DisplayMaster for this ViewManager
     *
     * @return the DisplayMaster
     *
     * @throws RemoteException Java RMI problem
     * @throws VisADException  Couldn't create the VisAD object
     */
    protected DisplayMaster doMakeDisplayMaster()
            throws VisADException, RemoteException {

        StateManager         stateManager = getStateManager();
        IntegratedDataViewer idv          = getIdv();
        if ((idv == null) || (stateManager == null)) {
            return null;
        }
        boolean mode3d = stateManager.getProperty(IdvConstants.PROP_3DMODE,
                             use3D);
        mode3d = getStore().get(PREF_DIMENSION, mode3d);
        // let property override the preference
        use3D = mode3d && use3D;
        int mode = (use3D
                    ? NavigatedDisplay.MODE_3D
                    : NavigatedDisplay.MODE_2Din3D);

        if ( !visad.util.Util.canDoJava3D()) {
            mode = NavigatedDisplay.MODE_2D;
        }

        boolean useGlobe = getUseGlobeDisplay()
                           && (mode != NavigatedDisplay.MODE_2D);

        Dimension dimension = stateManager.getViewSize();
        if (dimension == null) {
            if ((getFullScreenWidth() > 0) && (getFullScreenHeight() > 0)) {
                dimension = new Dimension(getFullScreenWidth(),
                                          getFullScreenHeight());
            } else if (displayBounds != null) {
                dimension = new Dimension(displayBounds.width,
                                          displayBounds.height);
            }
        }

        if ((dimension == null) || (dimension.width == 0)
                || (dimension.height == 0)) {
            dimension = null;
        }

        NavigatedDisplay navDisplay = null;


        if (useGlobe) {
            //TODO: Set the dimension
            GlobeDisplay globeDisplay =
                new GlobeDisplay(idv.getArgsManager().getIsOffScreen(),
                                 dimension, null);
            globeDisplay.setClipDistanceFront(
                getStateManager().getProperty(
                    PROP_CLIPDISTANCE_GLOBE_FRONT,
                    NavigatedDisplay.CLIP_FRONT_DEFAULT));
            globeDisplay.setClipDistanceBack(
                getStateManager().getProperty(
                    PROP_CLIPDISTANCE_GLOBE_BACK,
                    NavigatedDisplay.CLIP_BACK_DEFAULT));
            navDisplay = globeDisplay;
            setGlobeBackground(globeDisplay);
            navDisplay.setPolygonOffset(
                getStateManager().getProperty("idv.globe.polygonoffset", 1));
            navDisplay.setPolygonOffsetFactor(
                getStateManager().getProperty(
                    "idv.globe.polygonoffsetfactor", 1));
        } else {
            Trace.call1("MapViewManager.doMakeDisplayMaster projection");
            if (mainProjection == null) {
                if (initLatLonBounds != null) {
                    doNotSetProjection = true;
                    mainProjection =
                        ucar.visad.Util
                            .makeMapProjection(initLatLonBounds.getY()
                                - initLatLonBounds
                                    .getHeight(), initLatLonBounds.getX(),
                                        initLatLonBounds.getY(),
                                        initLatLonBounds.getX()
                                        + initLatLonBounds.getWidth(), false);


                } else {
                }
                    ProjectionImpl dfltProjection = null;
                    if (defaultProjectionName != null) {
                        dfltProjection =
                            idv.getIdvProjectionManager()
                                .findProjectionByName(defaultProjectionName);
                        if (dfltProjection != null) {
                            doNotSetProjection = true;
                        }
                    }

                    if (dfltProjection == null) {
                        dfltProjection = getDefaultProjection();
                    }
                    mainProjection =
                        new ProjectionCoordinateSystem(dfltProjection);
                }
            }
            if ( !idv.getArgsManager().isScriptingMode()) {
                addProjectionToHistory(mainProjection, "Default");
            }

            Trace.call1("MapViewManager.new MPD");
            MapProjectionDisplay mapDisplay =
                MapProjectionDisplay.getInstance(mainProjection, mode,
                    idv.getArgsManager().getIsOffScreen(), dimension);
            Trace.call2("MapViewManager.new MPD");

            mapDisplay.setClipDistanceFront(
                getStateManager().getProperty(
                    PROP_CLIPDISTANCE_MAP_FRONT,
                    NavigatedDisplay.CLIP_FRONT_DEFAULT));
            mapDisplay.setClipDistanceBack(
                getStateManager().getProperty(
                    PROP_CLIPDISTANCE_MAP_BACK,
                    NavigatedDisplay.CLIP_BACK_DEFAULT));

            if (getLatAxisScaleInfo() != null) {
            	mapDisplay.setLatScaleInfo(getLatAxisScaleInfo());
            }
            if (getLonAxisScaleInfo() != null) {
            	mapDisplay.setLonScaleInfo(getLonAxisScaleInfo());
            }

            if (initLatLonBounds == null) {
                double[] aspect = getAspectRatio();
                if (aspect == null) {
                    aspect = new double[] { 1.0, 1.0, 0.4 };
                }
                mapDisplay.setDisplayAspect((mode == NavigatedDisplay.MODE_2D)
                                            ? new double[] { aspect[0],
                        aspect[1] }
                                            : aspect);
            } else {
                //If we have a a latlonbounds then we want to display exactly that area
                double[] aspect = new double[] { 1.0,
                        initLatLonBounds.getHeight()
                        / initLatLonBounds.getWidth(),
                        1.0 };
                double[] scaleMatrix =
                    mapDisplay.getMouseBehavior().make_matrix(0.0, 0.0, 0.0,
                        aspect[0], aspect[1], 1, 0.0, 0.0, 0.0);
                //                mapDisplay.setProjectionMatrix(scaleMatrix);
            }


            navDisplay = mapDisplay;
            navDisplay.setPerspectiveView(getPerspectiveView());

            if ((defaultProjectionName != null)
                    && (displayProjectionZoom != 0)) {
                navDisplay.zoom(displayProjectionZoom);
            }
            defaultProjectionName = null;
            initLatLonBounds      = null;

            Trace.call2("MapViewManager.doMakeDisplayMaster projection");
            navDisplay.setPolygonOffset(
                getStateManager().getProperty("idv.map.polygonoffset", 1));
            navDisplay.setPolygonOffsetFactor(
                getStateManager().getProperty(
                    "idv.map.polygonoffsetfactor", 1));
            
        }





        return navDisplay;
    }



    /**
     * Get the earth location of the screen center
     *
     * @return screen center
     *
     * @throws RemoteException On badness
     * @throws VisADException On badness
     */
    public EarthLocation getScreenCenter()
            throws VisADException, RemoteException {
        return getNavigatedDisplay().getEarthLocation(
            getNavigatedDisplay().getScreenCenter());
    }





    /**
     * Get a list of named locations of the different points of the view rectangle. e.g., center, upper left, etc.
     *
     * @return list of locations
     *
     * @throws RemoteException On badness
     * @throws VisADException On badness
     */
    public List getScreenCoordinates()
            throws VisADException, RemoteException {
        List l = getNavigatedDisplay().getScreenCoordinates();
        List result = new ArrayList();
        for (TwoFacedObject tfo : l) {
            result.add(
                new TwoFacedObject(
                    tfo.toString(),
                    getNavigatedDisplay().getEarthLocation(
                        (double[]) tfo.getId())));
        }
        return result;
    }


    /**
     * Initialize this object.
     *
     * @throws RemoteException
     * @throws VisADException
     */
    protected void init() throws VisADException, RemoteException {
        if (getHaveInitialized()) {
            return;
        }
        super.init();
        Trace.call1("MapViewManager.init checkDefaultMap",
                    " showMap:" + showMaps);

        checkDefaultMap();
        Trace.call2("MapViewManager.init checkDefaultMap");

        if (useGlobeDisplay) {
            //            if(!hasBooleanProperty(PREF_SHOWGLOBEBACKGROUND)) {
            initializeBooleanProperty(
                new BooleanProperty(
                    PREF_SHOWGLOBEBACKGROUND, "Show Globe Background",
                    "Show Globe Background", defaultGlobeBackground));
            //            }
        }





    }


    /**
     * Initialize the UI
     */
    protected void initUI() {
        super.initUI();
        //Initialize the flythrough here
        if (flythrough != null) {
            flythrough.init(this);
            if (flythrough.getShown()) {
                flythrough.show();
            }
        }
    }


    /**
     * Fill the legends
     */
    protected void fillLegends() {
        super.fillLegends();
        if (flythrough != null) {
            flythrough.displayControlChanged();
        }
    }

    /**
     * Handle a perspective view change
     *
     * @param v the value
     */
    protected void perspectiveViewChanged(boolean v) {
        setPerspectiveView(v);
        super.perspectiveViewChanged(v);
        notifyDisplayControls(PREF_PERSPECTIVEVIEW);
    }

    /**
     * Handle a vertical scale change
     * @deprecated see {@link #verticalRangeChanged()}
     */
    protected void verticalScaleChanged() {
        verticalRangeChanged();
    }

    /**
     * Handle a vertical range change
     */
    protected void verticalRangeChanged() {
        super.verticalRangeChanged();
        notifyDisplayControls(SHARE_PROJECTION);
    }

    /**
     * Should we animate view changes
     *
     * @return true if not running ISL
     */
    public boolean shouldAnimateViewChanges() {
        return !getStateManager().getRunningIsl();
    }

    /**
     * Handle the event
     *
     * @param event The event
     *
     * @throws RemoteException On badness
     * @throws VisADException On badness
     */
    public void displayChanged(DisplayEvent event)
            throws VisADException, RemoteException {
        if (getIsDestroyed()) {
            return;
        }

        checkPipPanel();

        if (flythrough != null) {
            flythrough.displayChanged(event);
        }


        NavigatedDisplay navDisplay = getMapDisplay();
        if ( !navDisplay.getAutoRotate()
                && getViewpointControl().getAutoRotate()) {
            getViewpointControl().setAutoRotate(false);
        }

        int        id         = event.getId();
        InputEvent inputEvent = event.getInputEvent();
        if ((id == DisplayEvent.KEY_PRESSED)
                && (inputEvent instanceof KeyEvent)) {
            KeyEvent keyEvent = (KeyEvent) inputEvent;
            if (GuiUtils.isControlKey(keyEvent, KeyEvent.VK_N)) {
                EarthLocation center = getScreenCenter();
                getMapDisplay().centerAndZoom(center, null, 1.0,
                        shouldAnimateViewChanges(), true);
                return;
            }

            if (GuiUtils.isControlKey(keyEvent, KeyEvent.VK_S)) {
                EarthLocation center = getScreenCenter();
                getMapDisplay().centerAndZoom(center, null, 1.0,
                        shouldAnimateViewChanges(), false);
                return;
            }


            if (GuiUtils.isControlKey(keyEvent)
                    && ((keyEvent.getKeyCode() == KeyEvent.VK_H)
                        || (keyEvent.getKeyCode() == KeyEvent.VK_J)
                        || (keyEvent.getKeyCode() == KeyEvent.VK_K)
                        || (keyEvent.getKeyCode() == KeyEvent.VK_L))) {
                double[] matrix = getProjectionControl().getMatrix();
                double[] rot   = new double[3];
                double[] scale = new double[3];
                double[] trans = new double[3];
                MouseBehavior mouseBehavior =
                    getNavigatedDisplay().getMouseBehavior();
                mouseBehavior.instance_unmake_matrix(rot, scale, trans,
                        matrix);

                double[] t = null;
                if (keyEvent.getKeyCode() == KeyEvent.VK_H) {
                    t = mouseBehavior.make_matrix(-5, 0.0, 0, 1.0, 0.0, 0.0,
                            0.0);
                } else if (keyEvent.getKeyCode() == KeyEvent.VK_J) {
                    t = mouseBehavior.make_matrix(5, 0.0, 0, 1.0, 0.0, 0.0,
                            0.0);
                } else if (keyEvent.getKeyCode() == KeyEvent.VK_K) {
                    t = mouseBehavior.make_matrix(0, -5.0, 0, 1.0, 0.0, 0.0,
                            0.0);
                } else if (keyEvent.getKeyCode() == KeyEvent.VK_L) {
                    t = mouseBehavior.make_matrix(0, 5.0, 0, 1.0, 0.0, 0.0,
                            0.0);
                matrix = mouseBehavior.multiply_matrix(t, matrix);
                getMaster().setProjectionMatrix(matrix);

                return;
            }
        }


        super.displayChanged(event);
    }



    /**
     * Handle the mouse flicked
     *
     * @param startPoint  start point of flick
     * @param endPoint  end point of flick
     * @param startMatrix the start matrix
     * @param endMatrix the end matrix
     * @param speed  the speed of flicking
     */
    protected void mouseFlicked(Point startPoint, Point endPoint,
                                double[] startMatrix, double[] endMatrix,
                                double speed) {
        if ( !getUseGlobeDisplay()) {
            return;
        }

        double[] trans = { 0.0, 0.0, 0.0 };
        double[] rot1  = { 0.0, 0.0, 0.0 };
        double[] rot2  = { 0.0, 0.0, 0.0 };
        double[] scale = { 0.0, 0.0, 0.0 };
        getNavigatedDisplay().getMouseBehavior().instance_unmake_matrix(rot1,
                scale, trans, startMatrix);
        getNavigatedDisplay().getMouseBehavior().instance_unmake_matrix(rot2,
                scale, trans, endMatrix);

        //If there was no rotation then return
        if ((rot1[0] == rot2[0]) && (rot1[1] == rot2[1])) {
            return;
        }

        double distance = GuiUtils.distance(startPoint.x, startPoint.y,
                                            endPoint.x, endPoint.y);
        if (distance == 0) {
            return;
        }
        double percentX = (endPoint.x - startPoint.x) / distance;
        double percentY = (endPoint.y - startPoint.y) / distance;
        speed *= 2;
        getNavigatedDisplay().setRotationMultiplierMatrix(speed * -percentY,
                speed * -percentX, 0.0);
        getViewpointControl().setAutoRotate(true);
    }


    /**
     * Check if its ok to capture a kmz file
     *
     * @return ok to capture kmz
     */
    protected boolean checkForKmlImageCapture() {

        //Assume when we are running isl everything is ok
        if (getIdv().getArgsManager().getIsOffScreen()) {
            return true;
        }

        NavigatedDisplay navDisplay = getMapDisplay();
        double[]         rotMatrix  = navDisplay.getRotation();
        if ((rotMatrix[0] != 0) || (rotMatrix[1] != 0)
                || (rotMatrix[2] != 0)) {
            if (fixViewpointCbx == null) {
                fixViewpointCbx = new JCheckBox("Fix it", true);
            }

            JComponent question =
                GuiUtils
                    .vbox(new JLabel(
                        "The viewpoint is not overhead. This will result in an incorrect image capture."), GuiUtils
                            .left(fixViewpointCbx));
            if ( !GuiUtils.askOkCancel("KML Capture", question)) {
                return false;
            }
            if (fixViewpointCbx.isSelected()) {
                try {
                    navDisplay.resetProjection();
                } catch (Exception exc) {
                    throw new RuntimeException(exc);
                }
            } else {
                return true;
            }
        }


        int cnt = 0;
        while (true) {
            cnt++;
            Rectangle sb = navDisplay.getDisplayComponent().getBounds();
            LatLonPoint ul =
                getMapDisplay().getEarthLocation(
                    getMapDisplay().getSpatialCoordinatesFromScreen(
                        0, 0)).getLatLonPoint();
            LatLonPoint ur =
                getMapDisplay().getEarthLocation(
                    getMapDisplay().getSpatialCoordinatesFromScreen(
                        sb.width, 0)).getLatLonPoint();
            LatLonPoint lr =
                getMapDisplay().getEarthLocation(
                    getMapDisplay().getSpatialCoordinatesFromScreen(
                        sb.width, sb.height)).getLatLonPoint();
            LatLonPoint ll =
                getMapDisplay().getEarthLocation(
                    getMapDisplay().getSpatialCoordinatesFromScreen(
                        0, sb.height)).getLatLonPoint();

            double width = Math.abs(ul.getLongitude().getValue()
                                    - ur.getLongitude().getValue());

            double height = Math.abs(ul.getLatitude().getValue()
                                     - ll.getLatitude().getValue());


            boolean projOk = true;

            if ( !isClose(width, ul.getLongitude().getValue(),
                          ll.getLongitude().getValue())) {
                projOk = false;
            }
            if ( !isClose(width, ur.getLongitude().getValue(),
                          lr.getLongitude().getValue())) {
                projOk = false;
            }
            if ( !isClose(height, ul.getLatitude().getValue(),
                          ur.getLatitude().getValue())) {
                projOk = false;
            }
            if ( !isClose(height, ll.getLatitude().getValue(),
                          lr.getLatitude().getValue())) {
                projOk = false;
            }

            if (projOk) {
                return true;
            }

            if (fixProjectionCbx == null) {
                fixProjectionCbx = new JCheckBox("Fix it", true);
            }
            String msg = ((cnt == 1)
                          ? "The projection is not lat/lon. This will result in an incorrect image capture."
                          : "For some reason the projection is still not lat/lon.");
            JComponent question = GuiUtils.vbox(new JLabel(msg),
                                      GuiUtils.left(fixProjectionCbx));
            if ( !GuiUtils.askOkCancel("KML Capture", question)) {
                return false;
            }

            if ( !fixProjectionCbx.isSelected()) {
                return true;
            }

            if (fixProjectionCbx.isSelected()) {
                try {
                    setCurrentAsProjection();
                } catch (Exception exc) {
                    throw new RuntimeException(exc);
                }
                Misc.sleep(1000);
            }
        }

    }



    /**
     * are the 2 values close
     *
     * @param span the range
     * @param value1 value 1
     * @param value2 value 2
     *
     * @return are the 2 values close
     */
    private boolean isClose(double span, double value1, double value2) {
        //Check that the difference of the two values is < 1% of the given span value
        if (Math.abs((value1 - value2) / span) > 0.01) {
            return false;
        }
        return true;
    }


    /**
     * Check for the default map
     */
    private void checkDefaultMap() {
        if ( !showMaps) {
            return;
        }
        MapDisplayControl defaultMap = findDefaultMap();
        if (defaultMap == null) {
            try {
                ControlDescriptor mapCD =
                    getIdv().getControlDescriptor(
                        ControlDescriptor.DISPLAYID_MAP);
                if (mapCD == null) {
                    return;
                }

                MapInfo mapInfo;
                if (mapState != null) {
                    Trace.call1("checkDefaultMap-1");
                    mapInfo = new MapInfo(XmlUtil.getRoot(mapState));
                    mapInfo.setJustLoadedLocalMaps(true);
                    Trace.call2("checkDefaultMap-1");
                    //SKIP the initial map resources for now
                } else if (false && (initialMapResources != null)) {
                    //This got set from the ViewManager properties. It is a comma
                    //delimited list of map resources 
                    Trace.call1("checkDefaultMap-2");
                    List resources = getResourceManager().getResourcePaths(
                                         StringUtil.split(
                                             initialMapResources, ",", true,
                                             true));
                    XmlResourceCollection customMapResources =
                        new XmlResourceCollection("custom maps");
                    customMapResources.addResources(resources);
                    mapInfo = new MapInfo(customMapResources, true, true);
                    Trace.call2("checkDefaultMap-2");
                } else {
                    XmlResourceCollection xrc =
                        getResourceManager().getMapResources(
                            getUseGlobeDisplay());
                    //                    XmlResourceCollection xrc = getResourceManager().getMapResources(false);
                    mapInfo = new MapInfo(xrc, false, true);
                }

                if (initLatLonVisible) {
                    mapInfo.getLatData().setVisible(true);
                    mapInfo.getLatData().setLineWidth(initLatLonWidth);
                    mapInfo.getLatData().setSpacing(initLatLonSpacing);
                    mapInfo.getLatData().setColor(initLatLonColor);

                    mapInfo.getLonData().setVisible(true);
                    mapInfo.getLonData().setLineWidth(initLatLonWidth);
                    mapInfo.getLonData().setSpacing(initLatLonSpacing);
                    mapInfo.getLonData().setColor(initLatLonColor);
                }

                if (initMapPaths != null) {
                    for (MapData mapData : mapInfo.getMapDataList()) {
                        mapData.setVisible(false);
                        if (initMapWidth > 0) {
                            mapData.setLineWidth(initMapWidth);
                        }
                        if (initMapColor != null) {
                            mapData.setColor(initMapColor);
                        }
                    }
                    for (String mapPath :
                            StringUtil.split(initMapPaths, ",", true, true)) {
                        for (MapData mapData : mapInfo.getMapDataList()) {
                            if (Misc.equals(mapData.getSource(), mapPath)) {
                                mapData.setVisible(true);
                            }
                        }
                    }
                }

                Trace.call1("checkDefaultMap-making map");
                defaultMap = new MapDisplayControl(this, mapInfo);
                defaultMap.setIsDefaultMap(true);
                Hashtable newProperties =
                    new Hashtable(mapCD.getProperties());
                newProperties.put("displayName", "Default Background Maps");
                mapCD.initControl(defaultMap, new ArrayList(), getIdv(),
                                  newProperties, null);
                Trace.call2("checkDefaultMap-making map");
            } catch (Exception exc) {
                logException("Initializing maps", exc);
            }
        }
    }




    /**
     * Can this view manager be used in exchange for the given view manager
     *
     * @param that The other view manager to check
     * @return Can this be used in place of that
     */
    public boolean canBe(ViewManager that) {
        if ( !super.canBe(that)) {
            return false;
        }
        MapViewManager mvm = (MapViewManager) that;
        if (this.getUseGlobeDisplay() != mvm.getUseGlobeDisplay()) {
            return false;
        }
        if (this.getUse3D() != mvm.getUse3D()) {
            return false;
        }
        return true;
    }




    /**
     * Initialize with another view
     *
     * @param viewState  the view state
     *
     * @throws Exception  problems
     */
    public void initWith(ViewState viewState) throws Exception {

        MapProjection thatProjection =
            (MapProjection) viewState.get(ViewState.PROP_PROJECTION);
        if (thatProjection != null) {
            setMapProjection(thatProjection, false, "Projection");
        }
        double[] aspect =
            (double[]) viewState.get(ViewState.PROP_ASPECTRATIO);
        if (aspect != null) {
            this.setAspectRatio(aspect);
        }

        super.initWith(viewState);
    }

    /**
     * Handle the animation time changed
     */
    protected void animationTimeChanged() {
        super.animationTimeChanged();
        if (flythrough != null) {
            flythrough.animationTimeChanged();
        }
    }


    /**
     * Initialize this object's state with the state from that.
     *
     * @param that The other obejct to get state from
     * @param ignoreWindow If true then don't set the window size and location
     *
     * @throws RemoteException Java RMI problem
     * @throws VisADException  Couldn't create the VisAD object
     */
    protected void initWithInner(ViewManager that, boolean ignoreWindow)
            throws VisADException, RemoteException {

        if (getInitViewStateName() != null) {
            List vms = getIdv().getVMManager().getVMState();
            for (int i = 0; i < vms.size(); i++) {
                ViewState viewState = (ViewState) vms.get(i);
                if (viewState.getName().equals(getInitViewStateName())) {
                    if (isCompatibleWith(viewState)) {
                        try {
                            initWith(viewState);
                            break;
                        } catch (Exception exc) {
                            throw new RuntimeException(exc);
                        }
                    } else {
                        setInitViewStateName(null);
                        break;
                    }
                }
            }

        }



        if ( !(that instanceof MapViewManager)) {
            return;
        }


        if (displayProjectionZoom != 0) {
            getMapDisplay().zoom(displayProjectionZoom);
        }

        if (doNotSetProjection) {
            return;
        }




        MapViewManager mvm            = (MapViewManager) that;
        MapProjection  thatProjection = mvm.getMainProjection();
        if (getInitViewStateName() == null) {
    }
            this.setAspectRatio(that.getAspectRatio());
        }

        if ((mvm.flythrough != null) && (mvm.flythrough != this.flythrough)) {
            if (this.flythrough != null) {
                this.flythrough.destroy();
                //                this.flythrough.initWith(mvm.flythrough);
            }
            this.flythrough = mvm.flythrough;
            this.flythrough.setViewManager(this);
            if (this.flythrough.getShown()) {
                this.flythrough.show();
            }
        }

        boolean setProjection = false;
        if (thatProjection != null) {
            setProjection = setMapProjection(thatProjection, false,
                                             mvm.mainProjectionName);
        }

        if (getInitViewStateName() == null) {
            if ( !setProjection) {
                if (getAspectRatio() != null) {
                    getMapDisplay().setDisplayAspect(getAspectRatio());
                }
            }
        }

        //Only save the projection if we're not a globe
        //        getMapDisplay().saveProjection();
        if ( !getUseGlobeDisplay()) {
            getMapDisplay().saveProjection();
        }

        this.globeBackgroundColor = mvm.globeBackgroundColor;
        this.globeBackgroundLevel = mvm.globeBackgroundLevel;
        if (globeBackgroundDisplayable != null) {
            setGlobeBackground((GlobeDisplay) getMapDisplay());
        }



        super.initWithInner(that, ignoreWindow);
        try {
            //If we have an old bundle then the other map view has a non-null
            //map state. If so we load it in.
            if (mvm.mapState != null) {
                MapDisplayControl defaultMap = findDefaultMap();
                if (defaultMap != null) {
                    MapInfo mapInfo =
                        new MapInfo(XmlUtil.getRoot(mvm.mapState));
                    MapDisplayControl newMap = new MapDisplayControl(this,
                                                   mapInfo);
                    newMap.init((ucar.unidata.data.DataChoice) null);
                    defaultMap.loadNewMap(newMap);
                }
            }

        } catch (Exception exc) {
            logException("Initializing with MapViewManager", exc);
        }

        LatLonAxisScaleInfo latAxisScaleInfo = mvm.getLatAxisScaleInfo();

        if (latAxisScaleInfo != null) {
            setLatAxisScaleInfo(latAxisScaleInfo);
        }

        LatLonAxisScaleInfo lonAxisScaleInfo = mvm.getLonAxisScaleInfo();

        if (lonAxisScaleInfo != null) {
            setLonAxisScaleInfo(lonAxisScaleInfo);
        }


    }


    /**
     * Initialize the ViewState
     *
     * @param viewState the ViewState
     */
    public void initViewState(ViewState viewState) {
        super.initViewState(viewState);
        viewState.put(ViewState.PROP_GLOBE,
                      new Boolean(getUseGlobeDisplay()));
        if ( !getUseGlobeDisplay()) {
            viewState.put(ViewState.PROP_PROJECTION, getMainProjection());
        }
    }


    /**
     * Get the JComponent for the VisAD display
     *
     * @return VisAD display's Component
     */
    public JComponent getInnerContents() {
        return (JComponent) getMapDisplay().getComponent();





    /**
     * Leave this here for old bundles
     *
     * @param ms The map specification
     */
    public void setMapState(String ms) {
        mapState = ms;
    }




    /**
     * Add in the different preference panels.
     *
     * @param preferenceManager The preference manager to add things into
     */
    public void initPreferences(
            final IdvPreferenceManager preferenceManager) {

        super.initPreferences(preferenceManager);

        final JComponent[] bgComps =
            GuiUtils.makeColorSwatchWidget(getStore().get(PREF_BGCOLOR,
                getBackground()), "Set Background Color");

        final JComponent[] fgComps =
            GuiUtils.makeColorSwatchWidget(getStore().get(PREF_FGCOLOR,
                getForeground()), "Set Foreground Color");

        final JComponent[] border =
            GuiUtils
                .makeColorSwatchWidget(getStore()
                    .get(PREF_BORDERCOLOR, ViewManager
                        .borderHighlightColor), "Set Selected Panel Border Color");

        final JComponent[] globeComps =
            GuiUtils.makeColorSwatchWidget(getGlobeBackgroundColorToUse(),
                                           "Globe Background Color");
        StateManager stateManager = getStateManager();

        GuiUtils.tmpInsets = new Insets(5, 5, 5, 5);
        JPanel colorPanel = GuiUtils.left(GuiUtils.doLayout(new Component[] {
            GuiUtils.rLabel("  Background:"), bgComps[0], bgComps[1],
            GuiUtils.rLabel("  Foreground:"), fgComps[0], fgComps[1],
            GuiUtils.rLabel("  Selected Panel:"), border[0], border[1],
            GuiUtils.rLabel("  Globe Background:"), globeComps[0],
            globeComps[1],
        }, 3, GuiUtils.WT_N, GuiUtils.WT_N));
        colorPanel = GuiUtils.vbox(new JLabel("Color Scheme:"), colorPanel);

        cid        = new ContourInfoDialog("Preferences", false, null, false);
        ContourInfo ci =
            new ContourInfo(
                null, 0, 0, 10, true, false, false, 1, 0,
                ContourControl.LABEL_FREQ_LO,
                (int) stateManager
                    .getPreferenceOrProperty(
                        PREF_CONTOUR_LABELSIZE,
                        ContourInfo.DEFAULT_LABEL_SIZE), ContourInfoDialog
                            .getContourFont(
                                stateManager
                                    .getPreferenceOrProperty(
                                        PREF_CONTOUR_LABELFONT)), stateManager
                                            .getPreferenceOrProperty(
                                                PREF_CONTOUR_LABELALIGN,
                                                true));
        cid.setState(ci);

        JPanel contourPanel =
            GuiUtils.vbox(new JLabel("Contour Labels:"),
                          GuiUtils.inset(cid.getLabelPanel(),
                                         new Insets(5, 20, 0, 0)));
        contourPanel = GuiUtils.topCenter(contourPanel, GuiUtils.filler());
        colorPanel   = GuiUtils.hbox(colorPanel, contourPanel);

        final FontSelector fontSelector =
            new FontSelector(FontSelector.COMBOBOX_UI, false, false);
        Font f = getStore().get(PREF_DISPLAYLISTFONT, getDisplayListFont());

        fontSelector.setFont(f);
        final GuiUtils.ColorSwatch dlColorWidget =
            new GuiUtils.ColorSwatch(getStore().get(PREF_DISPLAYLISTCOLOR,
                getDisplayListColor()), "Set Display List Color");
        //GuiUtils.tmpInsets = new Insets(5, 5, 5, 5);
        JPanel fontPanel = GuiUtils.vbox(GuiUtils.lLabel("Display List:"),
                                         GuiUtils.doLayout(new Component[] {
        };
                                             GuiUtils.rLabel("Font:"),
                                             GuiUtils.left(fontSelector.getComponent()),
                                             GuiUtils.rLabel("Color:"),
                                             GuiUtils.left(GuiUtils.hbox(dlColorWidget,
                                                 dlColorWidget.getSetButton(),
                                                 dlColorWidget.getClearButton(),
                                                 5)) }, 2, GuiUtils.WT_N,
                                                     GuiUtils.WT_N));


        List            projections = getProjectionList();
        final JComboBox projBox     = new JComboBox();
        final Hashtable projMap = new Hashtable();

        Collection projNames =
            new TreeSet(Collator.getInstance());

        for (int p = 0; p < projections.size(); p++) {
            String projName = ((ProjectionImpl) projections.get(p)).getName();
            projMap.put(projName, projections.get(p));
            projNames.add(projName);
        }

        GuiUtils.setListData(projBox, projNames.toArray());
        Object defaultProj = getDefaultProjection();
        if (defaultProj != null) {
            if (defaultProj instanceof ProjectionImpl) {
                projBox.setSelectedItem(
                    ((ProjectionImpl) defaultProj).getName());
            } else {
                projBox.setSelectedItem(defaultProj);
            }
        }

        final JCheckBox logoVizBox = new JCheckBox(
                                         "Show Logo in View",
                                         stateManager.getPreferenceOrProperty(
                                             PREF_LOGO_VISIBILITY, false));
        final JTextField logoField =
            new JTextField(stateManager.getPreferenceOrProperty(PREF_LOGO,
                ""));
        logoField.setToolTipText("Enter a file or URL");
        // top panel
        JButton browseButton = new JButton("Browse..");
        browseButton.setToolTipText("Choose a logo from disk");
        browseButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                String filename =
                    FileManager.getReadFile(FileManager.FILTER_IMAGE);
                if (filename == null) {
                    return;
                }
                logoField.setText(filename);
            }
        });

        String[] logos = parseLogoPosition(
                             stateManager.getPreferenceOrProperty(
                                 PREF_LOGO_POSITION_OFFSET, ""));
        final JComboBox logoPosBox = new JComboBox(logoPoses);
        logoPosBox.setToolTipText("Set the logo position on the screen");
        logoPosBox.setSelectedItem(findLoc(logos[0]));

        final JTextField logoOffsetField = new JTextField(logos[1]);
        logoOffsetField.setToolTipText(
            "Set an offset from the position (x,y)");

        float logoScaleFactor =
            (float) stateManager.getPreferenceOrProperty(PREF_LOGO_SCALE,
                1.0);
        final JLabel logoSizeLab = new JLabel("" + logoScaleFactor);
        JComponent[] sliderComps = GuiUtils.makeSliderPopup(0, 20,
                                       (int) (logoScaleFactor * 10), null);
        final JSlider  logoScaleSlider = (JSlider) sliderComps[1];
        ChangeListener listener        = new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                logoSizeLab.setText("" + logoScaleSlider.getValue() / 10.f);
            }
        };
        logoScaleSlider.addChangeListener(listener);
        sliderComps[0].setToolTipText("Change Logo Scale Value");

        JPanel logoPanel =
            GuiUtils.vbox(
                GuiUtils.left(logoVizBox),

                GuiUtils.centerRight(logoField, browseButton),
                GuiUtils.hbox(
                    GuiUtils.leftCenter(
                        GuiUtils.rLabel("Screen Position: "),
                        logoPosBox), GuiUtils.leftCenter(
                            GuiUtils.rLabel("Offset: "),
                            logoOffsetField), GuiUtils.leftCenter(
                                GuiUtils.rLabel("Scale: "),
                                GuiUtils.leftRight(
                                    logoSizeLab, sliderComps[0]))));
        logoPanel = GuiUtils.vbox(GuiUtils.lLabel("Logo: "),
                                  GuiUtils.left(GuiUtils.inset(logoPanel,
                                      new Insets(5, 5, 0, 0))));


        PreferenceManager miscManager = new PreferenceManager() {
            public void applyPreference(XmlObjectStore theStore,
                                        Object data) {
                IdvPreferenceManager.applyWidgets((Hashtable) data, theStore);
                if (projBox.getSelectedItem() instanceof String) {
                    theStore.put(PREF_PROJ_DFLT,
                                 projMap.get(projBox.getSelectedItem()));
                } else {
                    theStore.put(PREF_PROJ_DFLT, projBox.getSelectedItem());
                }
                theStore.put(PREF_BGCOLOR, bgComps[0].getBackground());
                theStore.put(PREF_GLOBEBACKGROUND,
                             globeComps[0].getBackground());
                theStore.put(PREF_FGCOLOR, fgComps[0].getBackground());
                theStore.put(PREF_BORDERCOLOR, border[0].getBackground());
                theStore.put(PREF_DISPLAYLISTFONT, fontSelector.getFont());
                theStore.put(PREF_DISPLAYLISTCOLOR,
                             dlColorWidget.getSwatchColor());
                checkToolBarVisibility();
                ViewManager.setHighlightBorder(border[0].getBackground());
                cid.doApply();
                ContourInfo ci = cid.getInfo();
                theStore.put(PREF_CONTOUR_LABELSIZE, ci.getLabelSize());
                theStore.put(PREF_CONTOUR_LABELFONT, ci.getFont());
                theStore.put(PREF_CONTOUR_LABELALIGN, ci.getAlignLabels());
                theStore.put(PREF_LOGO, logoField.getText());
                String lpos =
                    ((TwoFacedObject) logoPosBox.getSelectedItem()).getId()
                        .toString();
                String loff = logoOffsetField.getText().trim();
                theStore.put(PREF_LOGO_POSITION_OFFSET,
                             makeLogoPosition(lpos, loff));
                theStore.put(PREF_LOGO_VISIBILITY, logoVizBox.isSelected());
                theStore.put(PREF_LOGO_SCALE,
                             logoScaleSlider.getValue() / 10f);

            }
        };


        Hashtable  widgets     = new Hashtable();
        ArrayList  miscList    = new ArrayList();


        Object[][] miscObjects = {
            { "View:", null, null },
            { "Show Wireframe Box", PREF_WIREFRAME,
              new Boolean(getWireframe()) },
            { "Show Cursor Readout", PREF_SHOWCURSOR,
              new Boolean(getShowCursor()) },
            { "Clip View At Box", PREF_3DCLIP, new Boolean(getClipping()) },
            { "Show Display List", PREF_SHOWDISPLAYLIST,
              new Boolean(getShowDisplayList()) },
            { "Show Times In View", PREF_ANIREADOUT,
              new Boolean(getAniReadout()) },
            { "Show Map Display Scales", PREF_SHOWSCALES,
              new Boolean(getLabelsVisible()) },
            { "Show Transect Display Scales", PREF_SHOWTRANSECTSCALES,
              new Boolean(getTransectLabelsVisible()) },

            { "Show \"Please Wait\" Message", PREF_WAITMSG,
              new Boolean(getWaitMessageVisible()) },
            { "Reset Projection With New Data", PREF_PROJ_USEFROMDATA },
            { "Use 3D View", PREF_DIMENSION },
            { "Show Globe Background", PREF_SHOWGLOBEBACKGROUND,
              new Boolean(getStore().get(PREF_SHOWGLOBEBACKGROUND,
                                         defaultGlobeBackground)) }

        Object[][] legendObjects = {
            { "Legends:", null, null },
            { "Show Side Legend", PREF_SHOWSIDELEGEND,
              new Boolean(getShowSideLegend()) },
            { "Show Bottom Legend", PREF_SHOWBOTTOMLEGEND,
              new Boolean(getShowBottomLegend()) },
            { "Show Animation Boxes", PREF_SHOWANIMATIONBOXES,
              new Boolean(getShowAnimationBoxes()) },
            { "Show Clock On Dashboard", IdvConstants.PROP_SHOWCLOCK_DASH,
              new Boolean(
                  getStateManager().getPreferenceOrProperty(
                      IdvConstants.PROP_SHOWCLOCK_DASH, "true")) },
            { "Show Clock On View Windows", IdvConstants.PROP_SHOWCLOCK_VIEW,
              new Boolean(
                  getStateManager().getPreferenceOrProperty(
                      IdvConstants.PROP_SHOWCLOCK_VIEW, "true")) },
            { "Show Overview Map", PREF_SHOWPIP,
              new Boolean(getStore().get(PREF_SHOWPIP, false)) }
        };

        Object[][] toolbarObjects = {
            { "Toolbars:", null, null },
            { "Show Earth Navigation Panel", PREF_SHOWEARTHNAVPANEL,
              new Boolean(getShowEarthNavPanel()) },
            { "Show Viewpoint Toolbar", PREF_SHOWTOOLBAR + "perspective" },
            { "Show Zoom/Pan Toolbar", PREF_SHOWTOOLBAR + "zoompan" },
            { "Show Undo/Redo Toolbar", PREF_SHOWTOOLBAR + "undoredo" }
        };

        JPanel miscPanel = IdvPreferenceManager.makePrefPanel(miscObjects,
                               widgets, getStore());
        JPanel legendPanel =
            IdvPreferenceManager.makePrefPanel(legendObjects, widgets,
                getStore());
        JPanel toolbarPanel =
            IdvPreferenceManager.makePrefPanel(toolbarObjects, widgets,
                getStore());
        JPanel projPanel =
            GuiUtils.vbox(GuiUtils.lLabel("Default Projection: "),
                          GuiUtils.left(GuiUtils.inset(projBox,
                              new Insets(5, 20, 0, 0))));

        JPanel colorFontPanel = GuiUtils.vbox(GuiUtils.top(colorPanel),
                                    GuiUtils.top(fontPanel)  //,
        //GuiUtils.top(projPanel)
        );




        GuiUtils.tmpInsets = new Insets(5, 5, 5, 5);
        JPanel miscContents = GuiUtils.doLayout(Misc.newList(new Component[] {
            GuiUtils.top(legendPanel), GuiUtils.top(toolbarPanel),
            GuiUtils.top(miscPanel), GuiUtils.top(colorFontPanel),
            GuiUtils.top(projPanel), GuiUtils.top(logoPanel)
        }), 2, GuiUtils.WT_N, GuiUtils.WT_N);


        miscContents = GuiUtils.inset(GuiUtils.left(miscContents), 5);
        preferenceManager.add("View", "View Preferences", miscManager,
                              miscContents, widgets);


    }


    /**
     * Go the a street address
     */
    public void goToAddress() {
        Misc.run(new Runnable() {
            public void run() {
                goToAddressInner();
            }
        });
    }


    /**
     * Popup the address location dialog and translate to the lat/lon.
     */
    private void goToAddressInner() {
        try {
            if (addressReprojectCbx == null) {
                addressReprojectCbx = new JCheckBox("Reproject",
                        getStore().get(PREF_ADDRESS_REPROJECT, true));
                addressReprojectCbx.setToolTipText(
                    "When checked make a simple map projection over the location");

                List savedAddresses =
                    (List) getStore().get(PREF_ADDRESS_LIST);
                if (savedAddresses != null) {
                    GeoUtils.setSavedAddresses(savedAddresses);
                }
            }
            getIdvUIManager().showWaitCursor();
            LatLonPoint llp = GeoUtils.getLocationOfAddress(
                                  GuiUtils.left(getUseGlobeDisplay()
                    ? GuiUtils.filler()
                    : (JComponent) addressReprojectCbx));
            //            System.out.println ("{" + GeoUtils.lastAddress+"}  -ll {" + llp.getLatitude()+","+llp.getLongitude()+"}");
            getIdvUIManager().showNormalCursor();
            if (llp == null) {
                return;
            }

            getStore().put(PREF_ADDRESS_LIST, GeoUtils.getSavedAddresses());
            getStore().put(PREF_ADDRESS_REPROJECT,
                           addressReprojectCbx.isSelected());


            float x      = (float) llp.getLongitude().getValue();
            float y      = (float) llp.getLatitude().getValue();
            float offset = (float) (1.0 / 60.0f);
            Rectangle2D.Float rect = new Rectangle2D.Float(x - offset,
                                         y - offset, offset * 2, offset * 2);
            if ( !getUseGlobeDisplay() && addressReprojectCbx.isSelected()) {
                TrivialMapProjection mp =
                    new TrivialMapProjection(
                        RealTupleType.SpatialEarth2DTuple, rect);

                setMapProjection(mp, true);
            } else {
                getMapDisplay().center(GeoUtils.toEarthLocation(llp),
                                       shouldAnimateViewChanges());
                //                getMapDisplay().center(GeoUtils.toEarthLocation(llp), false);
            }
        } catch (Exception e) {
            getIdvUIManager().showNormalCursor();
            logException("Error going to address", e);
        }
    }



    /**
     * Get the map display.
     *
     * @return The map display. This is the main display for thie view manager.
     */
    public NavigatedDisplay getMapDisplay() {
        return (NavigatedDisplay) getMaster();
    }

    /**
     * Are we in 3d mode
     *
     * @return Is display in 3d?
     */
    public boolean isDisplay3D() {
        return (getMapDisplay().getDisplayMode() == NavigatedDisplay.MODE_3D);
    }

    /**
     * Handle the receipt of shared data
     *
     * @param from Who is it from
     * @param dataId What is it
     * @param data Here it is
     */
    public void receiveShareData(Sharable from, Object dataId,
                                 Object[] data) {
        if ( !getInitDone()) {
            return;
        }
        if (dataId.equals(SHARE_PROJECTION)) {
            setMapProjection(((MapProjection) data[0]), false);
            return;
        }
        super.receiveShareData(from, dataId, data);
    }




    /**
     * Add the PIP panel if needed
     *
     * @param sideLegend The side legend
     *
     * @return The side legend or the sidelegend coupled with the pip panel
     */

    /**
         *
    protected JComponent getSideComponent(JComponent sideLegend) {
        if (false && getUseGlobeDisplay()) {
            return sideLegend;
        }
        pipPanel = new PipPanel(this);
        pipPanel.setPreferredSize(new Dimension(100, 100));
        JButton closeBtn =
            GuiUtils.makeImageButton("/auxdata/ui/icons/Cancel16.gif", this,
                                     "hidePip");
        pipPanelWrapper = GuiUtils.topCenter(GuiUtils.right(closeBtn),
                                             pipPanel);
        if ( !getShowPip()) {
            pipPanelWrapper.setVisible(false);
        }
        return GuiUtils.centerBottom(sideLegend, pipPanelWrapper);
    }


    /**
     * Make the GUI contents.
     *
     * @return The GUI contents
     */
    protected Container doMakeContents() {
        NavigatedDisplay navDisplay   = getMapDisplay();
        JComponent       navComponent = getComponent();
        navComponent.setPreferredSize(getMySize());
        earthNavPanelWrapper = new JPanel(new BorderLayout());
        JPanel contents = GuiUtils.centerBottom(navComponent,
                              earthNavPanelWrapper);

        earthNavPanel = new EarthNavPanel(this);
        if (getShowEarthNavPanel()) {
            earthNavPanelWrapper.add(BorderLayout.CENTER, earthNavPanel);
        }
        return contents;
    }


    /**
     * Initialize the toolbars for the GUI
     */
    protected void initToolBars() {
        if (isDisplay3D()) {
            addToolBar(doMakeViewPointToolBar(JToolBar.VERTICAL),
                       "perspective", "Viewpoint toolbar");
        }
        super.initToolBars();
    }



    /**
     * Set the viewpoint info
     *
     * @param viewpointInfo the viewpoint info
     */
    public void setViewpointInfo(ViewpointInfo viewpointInfo) {
        getViewpointControl().setViewpointInfo(viewpointInfo);
    }






    /**
     * Dynamically initialize the view menu
     *
     * @param viewMenu the view menu
     */
    public void initializeViewMenu(JMenu viewMenu) {
        super.initializeViewMenu(viewMenu);
        if (isDisplay3D()) {
            viewMenu.add(getViewpointControl().getMenu());
        }
        viewMenu.add(makeColorMenu());
        viewMenu.addSeparator();

        if (isFullScreen()) {
            viewMenu.add(
                GuiUtils.setIcon(
                    GuiUtils.makeMenuItem(
                        "Reset Full Screen", this,
                        "resetFullScreen"), "/auxdata/ui/icons/arrow_in.png"));
        } else {
            viewMenu.add(
                GuiUtils.setIcon(
                    GuiUtils.makeMenuItem(
                        "Full Screen", this,
                        "setFullScreen"), "/auxdata/ui/icons/arrow_out.png"));
        }
        viewMenu.addSeparator();
        viewMenu.add(
            GuiUtils.setIcon(
                GuiUtils.makeMenuItem(
                    "Animation Timeline", this,
                    "showTimeline"), "/auxdata/ui/icons/timeline_marker.png"));

        viewMenu.add(GuiUtils.setIcon(GuiUtils.makeMenuItem("Flythrough",
                this, "showFlythrough"), "/auxdata/ui/icons/plane.png"));

        viewMenu.add(GuiUtils.setIcon(GuiUtils.makeMenuItem("Properties",
                this,
                "showPropertiesDialog"), "/auxdata/ui/icons/information.png"));
    }

    /**
     * Create and return the list of menus for the menu bar.
     * Just the map and view menu.
     *
     * @return List of menus.
     */
    public ArrayList doMakeMenuList() {
            }
        ArrayList menus = super.doMakeMenuList();
        menus.add(makeViewMenu());
        menus.add(makeProjectionMenu());
        return menus;
    }


    /**
     * Create and return the show menu.
     *
     * @return The Show menu
     */
    protected JMenu makeShowMenu() {
        JMenu showMenu = super.makeShowMenu();
        if (globeBackgroundDisplayable != null) {
            createCBMI(showMenu, PREF_SHOWGLOBEBACKGROUND);
        }

        createCBMI(showMenu, PREF_SHOWSCALES);
        createCBMI(showMenu, PREF_ANIREADOUT);
        createCBMI(showMenu, PREF_SHOWPIP);
        createCBMI(showMenu, PREF_SHOWEARTHNAVPANEL);
        createCBMI(showMenu, PREF_LOGO_VISIBILITY);
        return showMenu;
    }



    /**
     * Center the display (animated) to the center of the given mapprojection
     *
     * @param mp  map projection
     *
     * @throws RemoteException  Java RMI problem
     * @throws VisADException   VisAD problem
     */
    public void center(MapProjection mp)
            throws RemoteException, VisADException {
        LatLonPoint center = mp.getCenterLatLon();
        getNavigatedDisplay().center(
            new EarthLocationTuple(
                center.getLatitude(), center.getLongitude(),
                new Real(RealType.Altitude, 0)), true);

    }


    /**
     * Set the projection to the first  projection  found in the displays
     */
    public void setProjectionFromFirstDisplay() {
        List controls = getControls();
        for (int i = 0; i < controls.size(); i++) {
            DisplayControl display = (DisplayControl) controls.get(i);
            MapProjection  mp      = display.getDataProjection();
            if (displayProjectionOk(mp)) {
                setMapProjection(
                    mp, true,
                    getDisplayConventions().getMapProjectionLabel(
                        mp, display));
                break;
            }
        }
    }


    /**
     * If we are using a ProjectionImpl then see if it hsa
     *
     * @throws RemoteException On badness
     * @throws VisADException On badness
     */
    protected void updateProjection() throws RemoteException, VisADException {
        if ((mainProjection == null)
                || !(mainProjection instanceof ProjectionCoordinateSystem)) {
            return;
        }
        ProjectionImpl myProjection =
            (ProjectionImpl) ((ProjectionCoordinateSystem) mainProjection)
                .getProjection();
        ProjectionImpl newProjection =
            getIdv().getIdvProjectionManager().findProjectionByName(
                myProjection.getName());
        if ((newProjection != null) && !myProjection.equals(newProjection)) {
            double[] matrix = getDisplayMatrix();
            setProjection(newProjection);
            setDisplayMatrix(matrix);
        }
    }


     * Find and set the projection by name
     *
     * @param projName projection name
     */
    public void setProjectionByName(String projName) {
        List projections = getProjectionList();
        for (int i = 0; i < projections.size(); i++) {
            ProjectionImpl p = (ProjectionImpl) projections.get(i);
            if (p.getName().equals(projName)) {
                setProjection(p);
                return;
        }

        for (int i = 0; i < projections.size(); i++) {
            ProjectionImpl p = (ProjectionImpl) projections.get(i);
            if (StringUtil.stringMatch(p.getName(), projName)) {
                setProjection(p);
                return;
            }
        }
        //        System.err.println("Could not find projection:" + projName);
    }



    /**
     * Set the current projection
     *
     * @param p The new projection.
     */
    public void setProjection(ProjectionImpl p) {
        p = (ProjectionImpl) p.constructCopy();
        try {
            setMapProjection(new ProjectionCoordinateSystem(p), true);
            if (pipPanel != null) {
                pipPanel.setProjectionImpl(p);
            }
        } catch (Exception excp) {
            logException("setMapProjection ()", excp);
        }
    }

    /**
     * Class ProjectionCommand manages changes to the projection
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.382 $
     */
    public static class ProjectionCommand extends Command {

        /** THe view manager I am in */
        MapViewManager viewManager;

        /** old state */
        String oldName;

        /** old state */
        String oldProjection;

        /** new state */
        String newName;

        /** new state */
        String newProjection;


        /**
         * ctor
         *
         * @param viewManager The vm
         * @param oldName old state
         * @param oldProjection old state
         * @param newName new state
         * @param newProjection new state
         */
        public ProjectionCommand(MapViewManager viewManager, String oldName,
                                 MapProjection oldProjection, String newName,
                                 MapProjection newProjection) {
            this.viewManager   = viewManager;
            this.oldName       = oldName;
            this.oldProjection = encode(oldProjection);
            this.newName       = newName;
            this.newProjection = encode(newProjection);
        }

        /**
         * Encode the map projection
         *
         * @param projection the map projection
         *
         * @return the encoded XML
         */
        private String encode(MapProjection projection) {
            try {
                return viewManager.getIdv().encodeObject(projection, false);
            } catch (Exception exc) {
                throw new RuntimeException(exc);
            }
        }

        /**
         * Decode a MapProjection XML spec
         *
         * @param xml a MapProjection XML spec
         * @return  the decoded MapProjection
         */
        private MapProjection decode(String xml) {
            try {
                return (MapProjection) viewManager.getIdv().decodeObject(xml);
            } catch (Exception exc) {
                throw new RuntimeException(exc);
            }
        }


        /**
         * Redo
         */
        public void redoCommand() {
            viewManager.setMapProjection(decode(newProjection), true,
                                         newName, false, false);
        }

        /**
         * Undo
         */
        public void undoCommand() {
            viewManager.setMapProjection(decode(oldProjection), true,
                                         oldName, false, false);
        }
    }





    /**
     *  A wrapper aroung the setMapProjection call that takes a projection name.
     *  This passes in null.
     *
     * @param projection The projection
     * @param fromWidget Is it from the projection selection widget
     */
    public void setMapProjection(MapProjection projection,
                                 boolean fromWidget) {
        setMapProjection(projection, fromWidget, null);
    }


    /**
     * Get the current projection.Used for xml encoding/decoding.
     *
     * @return The current projection
     */
    public MapProjection getMainProjection() {
        return mainProjection;
    }

    /**
     * Set the current projection. Used for xml encoding/decoding.
     *
     * @param projection The new projection
     */
    public void setMainProjection(MapProjection projection) {
        mainProjection = projection;
    }


    /**
     * The main projection name.Used for xml encoding/decoding.
     *
     * @return Projection name
     */
    public String getMainProjectionName() {
        return mainProjectionName;
    }

    /**
     * The main projection name.Used for xml encoding/decoding.
     *
     * @param projectionName Projection name
     */
    public void setMainProjectionName(String projectionName) {
        mainProjectionName = projectionName;
    }

    /**
     * Set the DfltProjectionName property.
     *
     * @param value The new value for DfltProjectionName
     */
    public void setDefaultProjectionName(String value) {
        this.defaultProjectionName = value;
    }

    /**
     * Get the DfltProjectionName property.
     *
     * @return The DfltProjectionName
     */
    public String getDefaultProjectionName() {
        return this.defaultProjectionName;
    }





    /**
     * Add the projection and the given name into the history list. Add a menu item
     * into the history menu.
     *
     * @param projection The projection
     * @param name Its name
     */
    private void addProjectionToHistory(MapProjection projection,
                                        String name) {
        String encodedProjection = getIdv().encodeObject(projection, false);
        TwoFacedObject tfo = TwoFacedObject.findId(encodedProjection,
                                 projectionHistory);
        if (tfo != null) {
            projectionHistory.remove(tfo);
            projectionHistory.add(0, tfo);
            return;

        }
        tfo = new TwoFacedObject(name, encodedProjection);
        projectionHistory.add(0, tfo);


        String label = ((name != null)
                        ? name
                        : projection.toString());
    }


    /**
     * Set map projection in the main display.
     *
     * @param projection a Projection
     * @param fromWidget  true if this was from a widget (ie. widget or
     *                    menu item)
     * @param name        name to put in the history list (may be null)
     *
     * @return  true if successful
     */
    public boolean setMapProjection(MapProjection projection,
                                    boolean fromWidget, String name) {
        return setMapProjection(projection, fromWidget, name, false);
    }

    /**
     * Set map projection in the main display.
     *
     * @param projection a Projection
     * @param fromWidget  true if this was from a widget (ie. widget or
     *                    menu item)
     * @param name        name to put in the history list (may be null)
     * @param checkDefault  if true, check to see if we
     *                    should call getUseProjectionFromData()
     *
     * @return  true if successful
     */
    public boolean setMapProjection(MapProjection projection,
                                    boolean fromWidget, String name,
                                    boolean checkDefault) {
        return setMapProjection(projection, fromWidget, name, checkDefault,
                                true);
    }



    /**
     * Set map projection in the main display.
     *
     * @param projection a Projection
     * @param fromWidget  true if this was from a widget (ie. widget or
     *                    menu item)
     * @param name        name to put in the history list (may be null)
     * @param checkDefault  if true, check to see if we
     *                    should call getUseProjectionFromData()
     * @param addToCommandHistory Add this projection to the command history
     *
     * @return  true if successful
     */
    public boolean setMapProjection(MapProjection projection,
                                    boolean fromWidget, String name,
                                    boolean checkDefault,
                                    boolean addToCommandHistory) {
        return setMapProjection(projection, fromWidget, name, checkDefault,
                                addToCommandHistory, false);
    }


    /**
     * Set map projection in the main display.
     *
     * @param projection a Projection
     * @param fromWidget  true if this was from a widget (ie. widget or
     *                    menu item)
     * @param name        name to put in the history list (may be null)
     * @param checkDefault  if true, check to see if we
     *                    should call getUseProjectionFromData()
     * @param addToCommandHistory Add this projection to the command history
     * @param maintainViewpoint  maintain the viewpoint
     *
     * @return  true if successful
     */
    public boolean setMapProjection(MapProjection projection,
                                    boolean fromWidget, String name,
                                    boolean checkDefault,
                                    boolean addToCommandHistory,
                                    boolean maintainViewpoint) {


        IdvUIManager.startTime = System.currentTimeMillis();

        if (checkDefault) {
            if ( !getUseProjectionFromData()) {
                return false;
            }
            if (doNotSetProjection) {
                return false;
            }
        }

        if (projection == null) {
            return false;
        }




        if (getUseGlobeDisplay() && !getViewpointControl().getAutoRotate()) {
            try {
                LatLonPoint center = projection.getCenterLatLon();
                getNavigatedDisplay().center(
                    new EarthLocationTuple(
                        center.getLatitude(), center.getLongitude(),
                        new Real(RealType.Altitude, 0)), true);
                return true;
            } catch (Exception e) {
                logException("setProjection", e);
                return false;
            }
        }




        boolean  actuallyChangedProjection = false;
        double[] matrix                    = getDisplayMatrix();
        try {

            setMasterInactive();
            if (addToCommandHistory && (mainProjection != null)) {
                addCommand(new ProjectionCommand(this, mainProjectionName,
                        mainProjection, name, projection));
            }

            if ( !Misc.equals(mainProjection, projection)) {
                if (name == null) {
                    name = getDisplayConventions().getMapProjectionName(
                        projection);
                }

                //If this is the first time we've put one in then save the current (default) proj.
                if ((projectionHistory.size() == 0)
                        && (mainProjection != null)) {
                    addProjectionToHistory(mainProjection, "Default");
                }
                mainProjectionName = name;
                addProjectionToHistory(projection, name);

                mainProjection = projection;
                if (fromWidget) {
                    doShare(SHARE_PROJECTION, projection);
                }
                try {
                    actuallyChangedProjection = true;
                    getMapDisplay().setMapProjection(mainProjection);
                    setAspectRatio(getMapDisplay().getDisplayAspect());


                    // override the aspect ratio
                    //if (getAspectRatio() != null) {
                    //getMapDisplay().setDisplayAspect(getAspectRatio());
                    //}
                    setDisplayMatrix(matrix);
                } catch (Exception e) {
                    logException("setProjection", e);
                }
                notifyDisplayControls(SHARE_PROJECTION);
                checkPipPanel();
            }
            // if the projections are the same, reset to main view in case
            // they are zoomed/panned
            try {
                if ( !maintainViewpoint
                        && projection.equals(mainProjection)) {
                    getMaster().resetProjection();
                }
            } catch (Exception e) {
                logException("setProjection", e);
            }
        } finally {
            updateDisplayList();
            setMasterActive();
        }
        return actuallyChangedProjection;
    }

    /**
     * Check the pip panel. If non-null have it reset its box
     */
    public void checkPipPanel() {
        try {
            synchronized (PIP_MUTEX) {
                if (pipPanel == null) {
                    return;
                }
                pipPanel.resetDrawBounds();
            }
        } catch (Exception exc) {
            pipPanel = null;
            logException("Error setting pip panel", exc);
        }
    }



    /**
     * can this viewmanager import the given display control
     *
     * @param control the control
     *
     * @return ok to import
     */
    public boolean okToImportDisplay(DisplayControl control) {
        //Base class method checks for non-null and class equality
        if ( !super.okToImportDisplay(control)) {
            return false;
        }
        MapViewManager vm = (MapViewManager) control.getViewManager();
        return this.getUseGlobeDisplay() == vm.getUseGlobeDisplay();
    }


    /**
     * Received the first frame done event  from the display
     */
    protected void doneFirstFrame() {
        super.doneFirstFrame();
        checkPipPanel();
    }

    /**
     * Search through the list of display controls
     * looking for a {@link ucar.unidata.idv.control.MapDisplayControl}
     * that has been set to be the "default map"
     *
     * @return The default map display control
     */
    private MapDisplayControl findDefaultMap() {
        List controls = getControls();
        for (int i = controls.size() - 1; i >= 0; i--) {
            DisplayControl control = (DisplayControl) controls.get(i);
            if ( !(control instanceof MapDisplayControl)) {
                continue;
            }
            if (((MapDisplayControl) control).getIsDefaultMap()) {
                return (MapDisplayControl) control;
            }
        }
        return null;
    }


    /**
     * Apply the properties
     *
     * @return true if successful
     */
    public boolean applyProperties() {
        if ( !super.applyProperties()) {
            return false;
        }
        if (useGlobeDisplay && (globeBackgroundDisplayable != null)) {
            globeBackgroundColor = globeBackgroundColorComp.getBackground();
            globeBackgroundLevel = globeBackgroundLevelSlider.getValue();
            setGlobeBackground((GlobeDisplay) getMapDisplay());
            return true;
        }
        return applyAxisVisibility();
    }

    /**
     * Apply axis visibility choices.
     *
     * @return
     */
    private boolean applyAxisVisibility() {

        if (latLonScaleWidget == null) {
            return false;
        }

        boolean b = !useGlobeDisplay
                    && (getBp(PREF_SHOWSCALES)
                        || latLonScaleWidget.isLatVisible()
                        || latLonScaleWidget.isLonVisible());

        setBp(PREF_SHOWSCALES, b);

        return latLonScaleWidget.doApply();
    }

    /**

    /**
     * Add the properties components for this ViewManager
     *
     * @param tabbedPane  the tabbed pane to add to
     */
    protected void addPropertiesComponents(JTabbedPane tabbedPane) {
        super.addPropertiesComponents(tabbedPane);
        if ( !useGlobeDisplay) {
            MapProjectionDisplay mpDisplay =
                (MapProjectionDisplay) getNavigatedDisplay();
            mpDisplay.getLatScaleInfo().setVisible(getBp(PREF_SHOWSCALES));
            mpDisplay.getLonScaleInfo().setVisible(getBp(PREF_SHOWSCALES));
            latLonScaleWidget = new LatLonScalePanel(mpDisplay);
            tabbedPane.add("Horizontal Scale",
                           GuiUtils.topLeft(latLonScaleWidget));
        }
        if (globeBackgroundDisplayable == null) {
            return;
        }


        globeBackgroundLevelSlider = new ZSlider(globeBackgroundLevel);
        JComponent levelComp = globeBackgroundLevelSlider.getContents();
        JComponent[] bgComps =
            GuiUtils.makeColorSwatchWidget(getGlobeBackgroundColorToUse(),
                                           "Globe Background Color");


        globeBackgroundColorComp = bgComps[0];
        JComponent comp = GuiUtils.formLayout(new Component[] {
                              GuiUtils.rLabel("Color:"),
                              GuiUtils.left(bgComps[0]),
                              GuiUtils.rLabel("Level:"),
                              levelComp });
        tabbedPane.add("Globe Background", GuiUtils.top(comp));
    }


    /**
     * Set the globe background
     *
     * @param globe  the globe display
     */
    private void setGlobeBackground(GlobeDisplay globe) {
        try {
            if (globeBackgroundDisplayable == null) {
                FlatField ff = ucar.visad.Util.makeField(-180, 180, 180, 90,
                                   -90, 180, 1, "celsius");
                Data d = ff;
                globeBackgroundDisplayable = new LineDrawing("background");
                globeBackgroundDisplayable.setData(d);
                globe.addDisplayable(globeBackgroundDisplayable);
            }

            //            Color c = new Color(globeBackgroundColor.getRed(),
            //                                globeBackgroundColor.getGreen(),
            //                                globeBackgroundColor.getBlue(),
            //                                (int)(255*0.5));

            globeBackgroundDisplayable.setColor(
                getGlobeBackgroundColorToUse());
            globeBackgroundDisplayable.setVisible(getGlobeBackgroundShow());

            DisplayRealType drt          = globe.getDisplayAltitudeType();
            double[]        range        = new double[2];
            double          realPosition = 1;
            if (drt.getRange(range)) {
                double pcnt = (globeBackgroundLevel - (-1)) / 2;
                realPosition = Math.min((range[0]
                                         + (range[1] - range[0])
                                           * pcnt), range[1]);
            }

            ConstantMap constantMap = new ConstantMap(realPosition, drt);
            globeBackgroundDisplayable.addConstantMap(constantMap);
        } catch (Exception exc) {
            throw new RuntimeException(exc);

        }
    }


    /**
     * Destroy this ViewManager
     */
    public void destroy() {
        if (flythrough != null) {
            try {
                flythrough.destroy();
            } catch (Exception ignore) {}
            flythrough = null;
        }
        super.destroy();
    }



    /**
     * Show the fly through
     */
    public void showFlythrough() {
        if (flythrough == null) {
            flythrough = new Flythrough(this);
        }
        flythrough.show();
    }


     * Do a flythrough
     *
     * @param pts  the flythrough points
     */
    public void flythrough(final float[][] pts) {
        if (flythrough == null) {
            flythrough = new Flythrough(this);
        }
        flythrough.flythrough(pts);
    }


    /**
     * Flythrough the points
     *
     * @param pts  the List of points
     */
    public void flythrough(List pts) {
        if (flythrough == null) {
            flythrough = new Flythrough(this);
        }
        flythrough.flythrough(pts);
    }

    /**
     * Initialize after unpersistence
     *
     * @param idv  the IDV
     *
     * @throws RemoteException Java RMI exception
     * @throws VisADException  VisAD problem
     */
    public final void initAfterUnPersistence(IntegratedDataViewer idv)
            throws VisADException, RemoteException {
        super.initAfterUnPersistence(idv);
    }


    /**
     * Handle a change to the data in a DisplayControl
     *
     * @param display  the DisplayControl
     */
    public void displayDataChanged(DisplayControl display) {
        displayDataChanged(display, false);
    }


    /**
     * Handle a change to the data in a DisplayControl
     *
     * @param display  the DisplayControl
     * @param fromInitialLoad  if this is from the initial load
     */
    public void displayDataChanged(DisplayControl display,
                                   boolean fromInitialLoad) {
        try {
            if (getUseProjectionFromData()
                    && !getStateManager().getProperty(
                        IdvConstants.PROP_LOADINGXML, false)) {
                MapProjection mp = display.getDataProjection();

                if (getUseGlobeDisplay()
                        && !getViewpointControl().getAutoRotate()) {
                    LatLonPoint center = mp.getCenterLatLon();
                    getNavigatedDisplay().center(
                        new EarthLocationTuple(
                            center.getLatitude(), center.getLongitude(),
                            new Real(RealType.Altitude, 0)), true);
                    return;
                }


                if (displayProjectionOk(mp)) {
                    if ((mainProjection == null)
                            || !mp.equals(mainProjection)) {
                        boolean maintainViewpoint = !fromInitialLoad;
                        setMapProjection(
                            mp, true,
                            getDisplayConventions().getMapProjectionLabel(
                                mp, display), true, true, maintainViewpoint);
                        if (displayProjectionZoom != 0) {
                            getMapDisplay().zoom(displayProjectionZoom);
                        }
                    }
                }
            }
        } catch (Exception exp) {
            // ignore, don't set anything.   Uncomment for debugging
            // LogUtil.logException ( "addDisplayInfo:setMapProjection()", exp);
        }
        super.displayDataChanged(display);
    }


    /**
     * Reset projection of display based control's getDataProjection().
     * called by DisplayInfo.addDisplayable (), usually from control's init.
     *
     * @param displayInfos The List of new display infos to add
     *
     * @throws RemoteException  Java RMI Exception
     * @throws VisADException   Problem creating VisAD object
     */
    public void addDisplayInfos(List displayInfos)
            throws RemoteException, VisADException {

        if (getIsDestroyed()) {
            return;
        }

        if (displayInfos.size() == 0) {
            return;
        }

        //Check if we are adding a default map.
        DisplayControl display = displayInfos.get(0).getDisplayControl();
        if ((display instanceof MapDisplayControl)
                && ((MapDisplayControl) display).getIsDefaultMap()) {
            MapDisplayControl defaultMap = findDefaultMap();
            if ((defaultMap != null) && (defaultMap != display)) {
                defaultMap.loadNewMap((MapDisplayControl) display);
                //This rebuilds the legends, etc.
                displayControlChanged(defaultMap);
                return;
            }
        }

        displayDataChanged(display, true);


        super.addDisplayInfos(displayInfos);

    }

    /**
     * Reset projection of display based on data.
     * @deprecated  no substitute.  Use setMapProjection()
     *
     * @param data The data form the display
     * @param display The display
     */
    public void checkProjection(FieldImpl data, DisplayControl display) {
        try {
            MapProjection mp = GridUtil.getNavigation(data);
            if (mp == null) {
                return;
            }
            setMapProjection(
                mp, false,
                getDisplayConventions().getMapProjectionLabel(mp, display));
        } catch (Exception exp) {}  // do nothing - no projection in data
    }





    /**
     * Required interface for ActionEvents, to implement ActionListener
     * for the UI objects such as JButton-s and MenuItem-s
     *
     * @param event an ActionEvent
     */
    public void actionPerformed(ActionEvent event) {
        String cmd = event.getActionCommand();
        if (cmd.equals(CMD_FLY_FORWARD) && (flythrough != null)) {
            flythrough.driveForward();
        } else if (cmd.equals(CMD_FLY_BACK) && (flythrough != null)) {
            flythrough.driveBack();
        } else if (cmd.equals(CMD_FLY_LEFT) && (flythrough != null)) {
            flythrough.driveLeft();
        } else if (cmd.equals(CMD_FLY_RIGHT) && (flythrough != null)) {
            flythrough.driveRight();
        } else if (cmd.equals(CMD_NAV_ZOOMIN)) {
            getMapDisplay().zoom(ZOOM_FACTOR);
        } else if (cmd.equals(CMD_NAV_ROTATELEFT)) {
            getMapDisplay().rotateZ(-5.0);
        } else if (cmd.equals(CMD_NAV_ROTATERIGHT)) {
            getMapDisplay().rotateZ(5.0);
        } else if (cmd.equals(CMD_NAV_ZOOMOUT)) {
            getMapDisplay().zoom(1.0 / (double) ZOOM_FACTOR);
        } else if (cmd.equals(CMD_NAV_HOME)) {
            try {
                getMapDisplay().resetProjection();
            } catch (Exception exc) {}
        } else if (cmd.equals(CMD_NAV_RIGHT)) {
                                          true, true);
            getMapDisplay().translate(-TRANSLATE_FACTOR, 0.0);
        } else if (cmd.equals(CMD_NAV_LEFT)) {
            getMapDisplay().translate(TRANSLATE_FACTOR, 0.0);
        } else if (cmd.equals(CMD_NAV_UP)) {
            getMapDisplay().translate(0.0, -TRANSLATE_FACTOR);
        } else if (cmd.equals(CMD_NAV_DOWN)) {
            getMapDisplay().translate(0.0, TRANSLATE_FACTOR);
        } else if (cmd.equals(CMD_NAV_SMALLZOOMIN)) {
            getMapDisplay().zoom(ZOOM_FACTOR);
            }
        } else if (cmd.equals(CMD_NAV_SMALLZOOMOUT)) {
            getMapDisplay().zoom(1.0 / ZOOM_FACTOR);
        } else if (cmd.equals(CMD_NAV_SMALLROTATELEFT)) {
            getMapDisplay().rotateZ(-2.0);
        } else if (cmd.equals(CMD_NAV_SMALLROTATERIGHT)) {
            getMapDisplay().rotateZ(2.0);
        } else if (cmd.equals(CMD_NAV_SMALLTILTUP)) {
            getMapDisplay().rotateX(-2.0);
        } else if (cmd.equals(CMD_NAV_SMALLTILTDOWN)) {
            getMapDisplay().rotateX(2.0);
        } else if (cmd.equals(CMD_NAV_SMALLRIGHT)) {
            getMapDisplay().translate(-0.02, 0.0);
        } else if (cmd.equals(CMD_NAV_SMALLLEFT)) {
            getMapDisplay().translate(0.02, 0.0);
        } else if (cmd.equals(CMD_NAV_SMALLUP)) {
            getMapDisplay().translate(0.0, -0.02);
        } else if (cmd.equals(CMD_NAV_SMALLDOWN)) {
            getMapDisplay().translate(0.0, 0.02);
        }

    }


    /**
     * Get the bounds that are visible
     *
     * @return bounds
     */
    public GeoLocationInfo getVisibleGeoBounds() {
        NavigatedDisplay navDisplay = getMapDisplay();
        Rectangle        screenBounds;

        screenBounds = navDisplay.getDisplayComponent().getBounds();
        if ( !getIdv().getArgsManager().getIsOffScreen()) {
            //            screenBounds = navDisplay.getComponent().getBounds();
        } else {
            //            Dimension 
        }
        double[] ulXY = getMapDisplay().getSpatialCoordinatesFromScreen(0, 0);
        double[] lrXY = getMapDisplay().getSpatialCoordinatesFromScreen(
                            screenBounds.width, screenBounds.height);

        LatLonPoint ulLLP =
            getMapDisplay().getEarthLocation(ulXY).getLatLonPoint();
        LatLonPoint lrLLP =
            getMapDisplay().getEarthLocation(lrXY).getLatLonPoint();



        double minX = Math.min(ulLLP.getLongitude().getValue(),
                               lrLLP.getLongitude().getValue());
        double maxX = Math.max(ulLLP.getLongitude().getValue(),
                               lrLLP.getLongitude().getValue());
        double minY = Math.min(ulLLP.getLatitude().getValue(),
                               lrLLP.getLatitude().getValue());
        double maxY = Math.max(ulLLP.getLatitude().getValue(),
                               lrLLP.getLatitude().getValue());
        Rectangle2D.Float rect = new Rectangle2D.Float((float) minX,
                                     (float) minY, (float) (maxX - minX),
                                     (float) (maxY - minY));

        return new GeoLocationInfo(maxY, minX, minY, maxX);
    }

    /**
     * Set the current viewpoint as the projection
     */
    public void setCurrentAsProjection() {
        try {
            NavigatedDisplay display      = getMapDisplay();
            Rectangle        screenBounds =
                display.getComponent().getBounds();
            LatLonPoint      ulLLP        = null;
            LatLonPoint      lrLLP        = null;

            int              sw           = screenBounds.width;
            int              sh           = screenBounds.height;
            int              x            = 0;
            int              y            = 0;

            while ((x < sw) && (y < sh)) {
            if (names.size() > 0) {
                double[] ulXY = display.getSpatialCoordinatesFromScreen(x, y);
                ulLLP =
                    getMapDisplay().getEarthLocation(ulXY).getLatLonPoint();
                if ( !ulLLP.getLatitude().isMissing()
                        && !ulLLP.getLongitude().isMissing()) {
                    break;
                }
                ulLLP = null;
                x++;
                y++;

            while ((sw > 0) && (sh > 0)) {
                double[] lrXY = display.getSpatialCoordinatesFromScreen(sw,
                                    sh);
                lrLLP =
                    getMapDisplay().getEarthLocation(lrXY).getLatLonPoint();
                if ( !lrLLP.getLatitude().isMissing()
                        && !lrLLP.getLongitude().isMissing()) {
                    break;
                }
                lrLLP = null;
                sw--;
                sh--;
            }

            if ((ulLLP == null) || (lrLLP == null)) {
                LogUtil.userMessage("Could not create a valid projection");
                return;
            }


            double minX = Math.min(ulLLP.getLongitude().getValue(),
                                   lrLLP.getLongitude().getValue());
            double maxX = Math.max(ulLLP.getLongitude().getValue(),
                                   lrLLP.getLongitude().getValue());
            double minY = Math.min(ulLLP.getLatitude().getValue(),
                                   lrLLP.getLatitude().getValue());
            double maxY = Math.max(ulLLP.getLatitude().getValue(),
                                   lrLLP.getLatitude().getValue());
            Rectangle2D.Float rect = new Rectangle2D.Float((float) minX,
                                         (float) minY, (float) (maxX - minX),
                                         (float) (maxY - minY));

            MapProjection mp = ucar.visad.Util.makeMapProjection(minY, minX,
                                   maxY, maxX);
            setMapProjection(mp, true);
            //            getMapDisplay().zoom(ZOOM_FACTOR);
            getMapDisplay().saveProjection();
        } catch (Exception exp) {
            logException("Setting projection", exp);
        }
    }


    /**
     * Show the projection manager.
     */
    public void showProjectionManager() {
        getIdv().showIdvProjectionManager();
    }




    /**
     * Set or reset map area of view, using NavigatedDisplay method.
     *
     * @param mapArea ProjectionRect the map area of view
     */
    public void setMapArea(ProjectionRect mapArea) {
        try {
            getMapDisplay().setMapArea(mapArea);
        } catch (Exception e) {
            logException("setMapArea", e);
        }
    }

    /**
     * Check if the display projection is okay
     *
     * @param mp  map projection to check
     *
     * @return true if okay
     */
    private boolean displayProjectionOk(MapProjection mp) {
        if (mp == null) {
            return false;
        }
        if (doNotSetProjection) {
            return false;
        }

        Rectangle2D rect = mp.getDefaultMapArea();
        if ((rect.getWidth() == 0) || (rect.getHeight() == 0)) {
            return false;
        } else {
            if (rect.getWidth() / rect.getHeight() < 0.1) {
                return false;
            } else if (rect.getHeight() / rect.getWidth() < 0.1) {
                return false;
            }
        }
        return true;
    }



    /**
     * Init menu
     *
     * @param projectionsMenu menu
     */
    }
    public void initializeProjectionMenu(JMenu projectionsMenu) {
        List projections = getProjectionList();
        List controls    = getControls();
        if ( !getUseGlobeDisplay()) {
            //            projectionsMenu.add(GuiUtils.makeMenuItem("Use Displayed Area",
            //                    this, "setCurrentAsProjection"));
        }
        ProjectionImpl currentProjection = null;
        if ((mainProjection != null)
                && (mainProjection instanceof ProjectionCoordinateSystem)) {
            currentProjection =
                ((ProjectionCoordinateSystem) mainProjection).getProjection();
        }


        makeProjectionsMenu(projectionsMenu, projections, this,
                            "setProjection", currentProjection);
    }



    /**
     * Init menu
     *
     * @param displaysMenu menu
     */
    public void initializeDisplaysProjectionMenu(JMenu displaysMenu) {
        List controls = getControls();
        int  cnt      = 0;
        for (int i = 0; i < controls.size(); i++) {
            final DisplayControl control = (DisplayControl) controls.get(i);
            final MapProjection  mp      = control.getDataProjection();
            if ( !displayProjectionOk(mp)) {
                continue;
            }

            final String label =
                getDisplayConventions().getMapProjectionLabel(mp, control);
            JMenuItem mi = new JMenuItem(label);
            displaysMenu.add(mi);
            mi.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent ae) {
                    setMapProjection(mp, false, label);
                }
            });
            cnt++;
        }
        if (cnt == 0) {
            displaysMenu.add(new JMenuItem("None Defined"));
        }
    }




    /**
     * Make the projections menu. Call the method on the given object.
     *
     * @param projectionsMenu Menu to add to
     * @param projections List of projections
     * @param object object to call
     * @param method method to call
     */
    public static void makeProjectionsMenu(JMenu projectionsMenu,
                                           List projections, Object object,
                                           String method) {
        makeProjectionsMenu(projectionsMenu, projections, object, method,
                            null);
    }


    /**
     * Make the projections menu
     *
     * @param projectionsMenu the menu to add to
     * @param projections list of projections
     * @param object object to call
     * @param method  method to call
     * @param currentProjection current projection
     */
    public static void makeProjectionsMenu(JMenu projectionsMenu,
                                           List projections, Object object,
                                           String method,
                                           ProjectionImpl currentProjection) {
        if (currentProjection != null) {
            List names = StringUtil.split(currentProjection.getName(), ">",
                String name = "Current: "
                              + (String) names.get(names.size() - 1);
                JMenuItem mi = GuiUtils.makeMenuItem(name, object, method,
                                   currentProjection);

                projectionsMenu.add(mi);
                projectionsMenu.addSeparator();
            }
        }


        Hashtable catMenus = new Hashtable();
        for (int i = 0; i < projections.size(); i++) {
            ProjectionImpl p = (ProjectionImpl) projections.get(i);
            List names = StringUtil.split(p.getName(), ">", true,
                                     true);
            JMenu  theMenu  = projectionsMenu;
            String catSoFar = "";
            int    catIdx   = 0;
            for (catIdx = 0; catIdx < names.size() - 1; catIdx++) {
                String cat = (String) names.get(catIdx);
                catSoFar += "-" + cat;
                JMenu tmpMenu = (JMenu) catMenus.get(catSoFar);
                if (tmpMenu == null) {
                    tmpMenu = new JMenu(cat);
                    catMenus.put(catSoFar, tmpMenu);
                    theMenu.add(tmpMenu);
                }
                theMenu = tmpMenu;
            }
            String  name      = ((catIdx < names.size())
                                 ? names.get(catIdx)
                                 : "");
            boolean isCurrent = Misc.equals(p, currentProjection);
            if (isCurrent) {
                //              name = "> " + name;
            }
            JMenuItem mi = GuiUtils.makeMenuItem(name, object, method, p);
            theMenu.add(mi);
            if (isCurrent) {
                GuiUtils.italicizeFont(mi.getComponent());
            }
        }


    }




    /**
     * Init menu
     *
     * @param menu menu
     */
    public void initializeProjectionHistoryMenu(JMenu menu) {
        menu.removeAll();
        for (int i = 0; i < projectionHistory.size(); i++) {
            final TwoFacedObject tfo =
                (TwoFacedObject) projectionHistory.get(i);
            JMenuItem mi = new JMenuItem(tfo.toString());
            mi.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    try {
                        String encodedProjection = (String) tfo.getId();
                        MapProjection mp =
                            (MapProjection) getIdv().decodeObject(
                                encodedProjection);
                        setMapProjection(mp, true);
                    } catch (Exception exc) {
                        logException("Failed to instantiate the projection",
                                     exc);
                    }
                }
            });
            menu.add(mi);
        }
    }


    /**
     * Is this a compatible ViewManager
     *
     * @param vm  the other
     *
     * @return  true if compatible
     */
    public boolean isCompatibleWith(ViewManager vm) {
        if ( !super.isCompatibleWith(vm)) {
            return false;
        }
        MapViewManager that = (MapViewManager) vm;
        return this.getUseGlobeDisplay() == that.getUseGlobeDisplay();
    }


    /**
     * Is this compatible with the ViewState
     *
     * @param viewState  the view state
     *
     * @return  true if compatible
     */
    public boolean isCompatibleWith(ViewState viewState) {
        if ( !super.isCompatibleWith(viewState)) {
            return false;
        }
        Boolean b = (Boolean) viewState.get(ViewState.PROP_GLOBE);
        if (b != null) {
            return getUseGlobeDisplay() == b.booleanValue();
        }
        return true;

    /**
     * Make the "Projections" menu to be added to the menu bar, which provides
     * controls for maps.
     * @return JMenu
     */
    private JMenu makeProjectionMenu() {
        JMenu projMenu = new JMenu("Projections");
        projMenu.setMnemonic(GuiUtils.charToKeyCode("P"));
        projectionsMenu = GuiUtils.makeDynamicMenu("Predefined", this,
                "initializeProjectionMenu");
        JMenu displaysMenu = GuiUtils.makeDynamicMenu("From Displays", this,
                                 "initializeDisplaysProjectionMenu");

        if ( !getUseGlobeDisplay()) {
            projMenu.add(projectionsMenu);
            projMenu.add(displaysMenu);
        }
        projMenu.add(makeSavedViewsMenu());
        JMenu projectionHistoryMenu = GuiUtils.makeDynamicMenu("History",
                                          this,
                                          "initializeProjectionHistoryMenu");

        if ( !getUseGlobeDisplay()) {
            projMenu.add(projectionHistoryMenu);
        }


        if ( !getUseGlobeDisplay()) {
            projMenu.addSeparator();
            projMenu.add(GuiUtils.setIcon(GuiUtils.makeMenuItem("New/Edit...",
                    this,
                    "showProjectionManager"), "/auxdata/ui/icons/world_edit.png"));
            projMenu.add(GuiUtils.setIcon(GuiUtils.makeMenuItem("Use Displayed Area",
                    this,
                    "setCurrentAsProjection"), "/auxdata/ui/icons/world_rect.png"));
        }
        projMenu.add(GuiUtils.setIcon(GuiUtils.makeMenuItem("Go to Address",
                this, "goToAddress"), "/auxdata/ui/icons/house_go.png"));


        projMenu.addSeparator();
        if ( !getUseGlobeDisplay()) {
            createCBMI(projMenu, PREF_PROJ_USEFROMDATA).setToolTipText(
                "Automatically change the projection to the native data projection of new displays");
        } else {
            createCBMI(projMenu, PREF_PROJ_USEFROMDATA).setToolTipText(
                "Automatically change viewpoint to the native data projection of new displays");
        }
        createCBMI(projMenu, PREF_SHAREVIEWS);
        projMenu.add(GuiUtils.makeMenuItem("Set Share Group", this,
                                           "showSharableDialog"));
        return projMenu;
    }


    /**
     * Have this so we don't get warnings on unpersisting old bundles
     *
     * @param location The location
     */
    public void setMapConfigFile(String location) {}



    /**
     * Use globe display. Used for xml encoding/decoding.
     *
     * @param use The globe display value
     */
    public void setUseGlobeDisplay(boolean use) {
        useGlobeDisplay = use;
    }

    /**
     * Get the globe display flag. Used for xml encoding/decoding.
     *
     * @return The globe display value
     */
    public boolean getUseGlobeDisplay() {
        return useGlobeDisplay;
    }


    /**
     * Use a 3D display. Used for xml encoding/decoding.
     *
     * @param use The use 3D display value
     */
    public void setUse3D(boolean use) {
        use3D = use;
    }

    /**
     * Get the use 3D display flag. Used for xml encoding/decoding.
     *
     * @return The use 3D value
     */
    public boolean getUse3D() {
        return use3D;
    }



    /**
     * The BooleanProperty identified byt he given id has changed.
     * Apply the change to the display.
     *
     * @param id Id of the changed BooleanProperty
     * @param value Its new value
     *
     * @throws Exception problem handeling the change
     */
    protected void handleBooleanPropertyChange(String id, boolean value)
            throws Exception {
        super.handleBooleanPropertyChange(id, value);
        if (id.equals(PREF_AUTOROTATE)) {
            if (hasViewpointControl()) {
                getViewpointControl().setAutoRotate(value);
            }
        } else if (id.equals(PREF_SHOWSCALES)) {
            if (hasDisplayMaster()) {
                getNavigatedDisplay().setScalesVisible(value);
            }
        } else if (id.equals(PREF_SHOWEARTHNAVPANEL)) {
            if (earthNavPanelWrapper != null) {
                earthNavPanelWrapper.removeAll();
                if (value) {
                    earthNavPanelWrapper.add(BorderLayout.CENTER,
                                             earthNavPanel);
                }
            }
        } else if (id.equals(PREF_SHOWPIP)) {
            if (pipPanelWrapper != null) {
                pipPanelWrapper.setVisible(value);
            }
        } else if (id.equals(PREF_SHOWGLOBEBACKGROUND)) {
            if (globeBackgroundDisplayable != null) {
                globeBackgroundDisplayable.setVisible(value);
            }
        } else if (id.equals(PREF_PERSPECTIVEVIEW)) {
            if (hasViewpointControl()) {
                getViewpointControl().setPerspectiveView(value);
            }
        }
    }

    /**
     * Apply preferences
     */
    public void applyPreferences() {
        super.applyPreferences();
        applyAxisVisibility();
    }

    /**
     * Create the set of {@link ucar.unidata.util.BooleanProperty}s.
     * These hold all of the different flag based display state.
     *
     * @param props the list of properties
     */
    protected void getInitialBooleanProperties(List props) {
        super.getInitialBooleanProperties(props);
        props.add(new BooleanProperty(PREF_SHOWSCALES, "Show Display Scales",
                                      "Show Display Scales", false));

        props.add(
            new BooleanProperty(
                PREF_PROJ_USEFROMDATA, "Auto-set Projection",
                "Use projection from newly loaded data", true));
        props.add(new BooleanProperty(PREF_PERSPECTIVEVIEW,
                                      "Perspective View",
                                      "Toggle perspective view", false));
        props.add(new BooleanProperty(PREF_AUTOROTATE, "Auto-rotate", "",
                                      false));
        props.add(new BooleanProperty(PREF_SHOWEARTHNAVPANEL,
                                      "Show Earth Navigation Panel",
                                      "Show Earth Navigation Panel", false));

        props.add(new BooleanProperty(PREF_SHOWPIP, "Show Overview Map",
                                      "Show Overview Map", false));

        if (useGlobeDisplay) {
            props.add(new BooleanProperty(PREF_SHOWGLOBEBACKGROUND,
                                          "Show Globe Background",
                                          "Show Globe Background",
                                          defaultGlobeBackground));
        }
    }



    /**
     * Set the autorotate property
     *
     * @param value The value
     */
     *
    public void setAutoRotate(boolean value) {
        setBp(PREF_AUTOROTATE, value);
    }

    /**
     * Get  the autorotate flag
     * @return The flag value
     */
    public boolean getAutoRotate() {
        return getBp(PREF_AUTOROTATE);
    }

    /**
     * Set the  perspective view flag
     *
     * @param value The value
     */
    public void setPerspectiveView(boolean value) {
        setBp(PREF_PERSPECTIVEVIEW, value);
    }

    /**
     * Get  the perspective view  flag
     * @return The flag value
     */
    public boolean getPerspectiveView() {
        return getBp(PREF_PERSPECTIVEVIEW);
    }


    /**
     * Dummy for old bundles
     *
     * @param value The value
     */
    public void setShowMap(boolean value) {}


    /**
     * Dummy for old bundles
     *
     * @param value The value
     */
    public void setShowElevation(boolean value) {}


    /**
     * Set the  use projection from data flag
     *
     * @param value The value
     */
    public void setUseProjectionFromData(boolean value) {
        setBp(PREF_PROJ_USEFROMDATA, value);
    }

    /**
     * Get  the use projection from data  flag
     * @return The flag value
     */
    public boolean getUseProjectionFromData() {
        //   if ( !isInteractive()) {
        //       return true;
        //   }
        return getBp(PREF_PROJ_USEFROMDATA);
    }


    /**
     * Set the background color property.
     * @deprecated  Keep this around for old bundles
     * @param bgColor The value
     */
    public void setBgColor(boolean bgColor) {
        if ( !bgColor) {
            setBackground(Color.white);
            setForeground(Color.black);
        } else {
            setBackground(Color.black);
            setForeground(Color.white);
        }
    }


    /**
     * Set the ShowEarthNavPanel property.
     *
     * @param value The new value for ShowEarthNavPanel
     */
    public void setShowEarthNavPanel(boolean value) {
        setBp(PREF_SHOWEARTHNAVPANEL, value);
    }

    /**
     * Get the ShowEarthNavPanel property.
     *
     * @return The ShowEarthNavPanel
     */
    public boolean getShowEarthNavPanel() {
        return getBp(PREF_SHOWEARTHNAVPANEL);
    }


    /**
     * Hide the pip panel
     */
    public void hidePip() {
        setShowPip(false);
    }

    /**
     * Set the ShowPipPanel property.
     *
     * @param value The new value for ShowPipPanel
     */
    public void setShowPip(boolean value) {
        setBp(PREF_SHOWPIP, value);
    }

    /**
     * Get the ShowPipPanel property.
     * @return The ShowPipPanel
     */
    public boolean getShowPip() {
        return getBp(PREF_SHOWPIP, false);
    }


    /**
     * Set the InitialMapResources property. This gets set
     * by the viewmanager properties and is a comma separated
     * list of map resource paths.
     *
     * @param value The new value for InitialMapResources
     */
    public void setInitialMapResources(String value) {
        initialMapResources = value;
    }



    /**
     * What type of view is this
     *
     * @return The type of view
     */
    public String getTypeName() {
        if (getUseGlobeDisplay()) {
            return "Globe";
        }
        return "Map";
    }



    /**
     * Get the default map position from a property
     *
     * @return the value in the range of -1 to 1
     */
    public float getDefaultMapPosition() {
        if (getUseGlobeDisplay()) {
            return (float) getStateManager().getProperty(
                IdvConstants.PROP_MAP_GLOBE_LEVEL, 0.005f);
        } else {
            return (float) getStateManager().getProperty(
                IdvConstants.PROP_MAP_MAP_LEVEL, -0.99f);
        }
    }

    /**
     *  Set the GlobeBackgroundColor property.
     *
     *  @param value The new value for GlobeBackgroundColor
     */
    public void setGlobeBackgroundColor(Color value) {
        globeBackgroundColor = value;
    }


    /**
     *  Get the GlobeBackgroundColor property.
     *
     *  @return The GlobeBackgroundColor
     */
    public Color getGlobeBackgroundColor() {
        return globeBackgroundColor;
    }

    /**
     *  Get the GlobeBackgroundColor property to be used. If it has not been set then get the preference
     *
     *  @return The GlobeBackgroundColor to use
     */
    public Color getGlobeBackgroundColorToUse() {
        Color backgroundColor = globeBackgroundColor;
        if (backgroundColor == null) {
            backgroundColor = getStore().get(PREF_GLOBEBACKGROUND,
                                             Color.white);
        }
        return backgroundColor;
    }


    /**
     *  Set the GlobeBackgroundShow property.
     *
     *  @param value The new value for GlobeBackgroundShow
     */
    public void setGlobeBackgroundShow(boolean value) {
        defaultGlobeBackground = value;
        setBp(PREF_SHOWGLOBEBACKGROUND, value);
    }

    /**
     *  Get the GlobeBackgroundShow property.
     *
     *  @return The GlobeBackgroundShow
     */
    public boolean getGlobeBackgroundShow() {
        if (hasBooleanProperty(PREF_SHOWGLOBEBACKGROUND)) {
            return getBp(PREF_SHOWGLOBEBACKGROUND, false);
        }
        XmlObjectStore store = getStore();
        if (store != null) {
            return store.get(PREF_SHOWGLOBEBACKGROUND, false);
        }
        return false;
    }

    /**

     *  Set the GlobeBackgroundLevel property.
     *
     *  @param value The new value for GlobeBackgroundLevel
     */
    public void setGlobeBackgroundLevel(double value) {
        globeBackgroundLevel = value;
    }

    /**
     *  Get the GlobeBackgroundLevel property.
     *
     *  @return The GlobeBackgroundLevel
     */
    public double getGlobeBackgroundLevel() {
        return globeBackgroundLevel;
    }

    /**
     * Set the Flythrough property.
     *
     * @param value The new value for Flythrough
     */
    public void setFlythrough(Flythrough value) {
        this.flythrough = value;
    }

    /**
     * Get the Flythrough property.
     *
     * @return The Flythrough
     */
    public Flythrough getFlythrough() {
        return this.flythrough;
    }


    /**
     *  Set the ShowMaps property.
     *
     *  @param value The new value for ShowMaps
     */
    public void setShowMaps(boolean value) {
        this.showMaps = value;
    }

    /**
     *  Get the ShowMaps property.
     *
     *  @return The ShowMaps
     */
    public boolean getShowMaps() {
        return this.showMaps;
    }





    /**
     *  Set the DisplayProjectionZoom property.
     *
     *  @param value The new value for DisplayProjectionZoom
     */
    public void setDisplayProjectionZoom(double value) {
        this.displayProjectionZoom = value;
    }

    /**
     *  Get the DisplayProjectionZoom property.
     *
     *  @return The DisplayProjectionZoom
     */
    public double getDisplayProjectionZoom() {
        return this.displayProjectionZoom;
    }

    /**
     *  Set the InitMapPaths property.
     *
     *  @param value The new value for InitMapPaths
     */
    public void setInitMapPaths(String value) {
        this.initMapPaths = value;
    }


    /**
     *  Get the InitMapPaths property.
     *
     *  @return The InitMapPaths
     */
    public String getInitMapPaths() {
        return this.initMapPaths;
    }

    /**
     * Set the intial lat/lon visible
     *
     * @param v  true or false
     */
    public void setInitLatLonVisible(boolean v) {
        initLatLonVisible = v;
    }



    /**
     * Set the intial lat/lon color
     *
     * @param v  the color
     */
    public void setInitLatLonColor(Color v) {
        initLatLonColor = v;
    }


    /**
     * Set the initial lat/lon line width
     *
     * @param v  the width
     */
    public void setInitLatLonWidth(int v) {
        initLatLonWidth = v;
    }

    /**
     * Set the initial lat/lon spacing
     *
     * @param v  the spacing
     */
    public void setInitLatLonSpacing(float v) {
        initLatLonSpacing = v;
    }

    /**
     * Set the InitMapWidth property.
     *
     * @param value The new value for InitMapWidth
     */
    public void setInitMapWidth(float value) {
        this.initMapWidth = value;
    }

    /**
     * Get the InitMapWidth property.
     *
     * @return The InitMapWidth
     */
    public float getInitMapWidth() {
        return this.initMapWidth;
    }

    /**
     * Set the InitMapColor property.
     *
     * @param value The new value for InitMapColor
     */
    public void setInitMapColor(Color value) {
        this.initMapColor = value;
    }

    /**
     * Get the InitMapColor property.
     *
     * @return The InitMapColor
     */
    public Color getInitMapColor() {
        return this.initMapColor;
    }

    /**
     * Set the InitLatLonBounds property.
     *
     * @param value The new value for InitLatLonBounds
     */
    public void setInitLatLonBounds(Rectangle2D.Float value) {
        this.initLatLonBounds = value;
    }

    /**
     * Get the InitLatLonBounds property.
     *
     * @return The InitLatLonBounds
     */
    public Rectangle2D.Float getInitLatLonBounds() {
        return this.initLatLonBounds;
    }

    /**
     * Gets the lat axis scale info.
     *
     * @return the lat axis scale info
     */
    public LatLonAxisScaleInfo getLatAxisScaleInfo() {
        if ( !hasDisplayMaster()) {
            return latAxisScaleInfo;
        }

        if ( !useGlobeDisplay) {
            MapProjectionDisplay d =
                (MapProjectionDisplay) getNavigatedDisplay();
            return d.getLatScaleInfo();
        } else {
            return null;
        }
    }

    /**
     * Sets the lat axis scale info.
     *
     * @param axisScaleInfo
     *            the new lat axis scale info
     * @throws RemoteException
     *             the remote exception
     * @throws VisADException
     *             the VisAD exception
     * @deprecated
     * public void setLatAxisScaleInfo(AxisScaleInfo axisScaleInfo)
     *       throws RemoteException, VisADException {
     *
     *   setLatAxisScaleInfo((LatLonAxisScaleInfo) axisScaleInfo);
     * }
     */

    /**
     * Sets the lat axis scale info.
     *
     * @param axisScaleInfo
     *            the new lat axis scale info
     * @throws RemoteException
     *             the remote exception
     * @throws VisADException
     *             the VisAD exception
     */
    public void setLatAxisScaleInfo(LatLonAxisScaleInfo axisScaleInfo)
            throws RemoteException, VisADException {
        this.latAxisScaleInfo = axisScaleInfo;

        if ( !hasDisplayMaster()) {
            return;
        }

        if ( !useGlobeDisplay) {
            MapProjectionDisplay d =
                (MapProjectionDisplay) getNavigatedDisplay();
            d.setLatScaleInfo(axisScaleInfo);
        }
    }

    /**
     * Gets the lon axis scale info.
     *
     * @return the lon axis scale info
     */
    public LatLonAxisScaleInfo getLonAxisScaleInfo() {
        if ( !hasDisplayMaster()) {
            return lonAxisScaleInfo;
        }

        if ( !useGlobeDisplay) {
            MapProjectionDisplay d =
                (MapProjectionDisplay) getNavigatedDisplay();
            return d.getLonScaleInfo();
        } else {
            return null;
        }
    }

    /**
     * Sets the lon axis scale info.
     *
     * @param axisScaleInfo
     *            the new lon axis scale info
     * @throws RemoteException
     *             the remote exception
     * @throws VisADException
     *             the vis ad exception
     * @deprecated
     * public void setLonAxisScaleInfo(AxisScaleInfo axisScaleInfo)
     *       throws RemoteException, VisADException {
     *   setLonAxisScaleInfo((LatLonAxisScaleInfo) axisScaleInfo);
     * }
     */

    /**
     * Sets the lon axis scale info.
     *
     * @param axisScaleInfo
     *            the new lon axis scale info
     * @throws RemoteException
     *             the remote exception
     * @throws VisADException
     *             the vis ad exception
     */
    public void setLonAxisScaleInfo(LatLonAxisScaleInfo axisScaleInfo)
            throws RemoteException, VisADException {

        this.lonAxisScaleInfo = axisScaleInfo;

        if ( !hasDisplayMaster()) {
            return;
        }

        if ( !useGlobeDisplay) {
            MapProjectionDisplay d =
                (MapProjectionDisplay) getNavigatedDisplay();
            d.setLonScaleInfo(axisScaleInfo);
        }
    }
}
>>>>>>> 72c1c20662f16e1b98193bc09dd2baf4024fb28a
Solution content
/*
 * Copyright 1997-2013 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 * 
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package ucar.unidata.idv;


import ucar.unidata.collab.Sharable;
import ucar.unidata.data.GeoLocationInfo;
import ucar.unidata.data.grid.GridUtil;
import ucar.unidata.geoloc.ProjectionImpl;
import ucar.unidata.geoloc.ProjectionRect;
import ucar.unidata.gis.maps.MapData;
import ucar.unidata.gis.maps.MapInfo;
import ucar.unidata.idv.control.MapDisplayControl;
import ucar.unidata.idv.control.ZSlider;
import ucar.unidata.idv.flythrough.Flythrough;
import ucar.unidata.idv.flythrough.FlythroughPoint;
import ucar.unidata.idv.ui.ContourInfoDialog;
import ucar.unidata.idv.ui.EarthNavPanel;
import ucar.unidata.idv.ui.IdvUIManager;
import ucar.unidata.idv.ui.PipPanel;
import ucar.unidata.ui.Command;
import ucar.unidata.ui.FontSelector;
import ucar.unidata.util.BooleanProperty;
import ucar.unidata.util.ContourInfo;
import ucar.unidata.util.FileManager;
import ucar.unidata.util.GuiUtils;
import ucar.unidata.util.LogUtil;
import ucar.unidata.util.Misc;
import ucar.unidata.util.StringUtil;
import ucar.unidata.util.Trace;
import ucar.unidata.util.TwoFacedObject;
import ucar.unidata.view.geoloc.GlobeDisplay;
import ucar.unidata.view.geoloc.LatLonAxisScaleInfo;
import ucar.unidata.view.geoloc.LatLonScalePanel;
import ucar.unidata.view.geoloc.MapProjectionDisplay;
import ucar.unidata.view.geoloc.NavigatedDisplay;
import ucar.unidata.view.geoloc.ViewpointInfo;
import ucar.unidata.xml.PreferenceManager;
import ucar.unidata.xml.XmlObjectStore;
import ucar.unidata.xml.XmlResourceCollection;
import ucar.unidata.xml.XmlUtil;

import ucar.visad.GeoUtils;
import ucar.visad.ProjectionCoordinateSystem;
import ucar.visad.display.DisplayMaster;
import ucar.visad.display.LineDrawing;

import visad.ConstantMap;
import visad.ContourControl;
import visad.Data;
import visad.DisplayEvent;
import visad.DisplayRealType;
import visad.FieldImpl;
import visad.FlatField;
import visad.MouseBehavior;
import visad.Real;
import visad.RealTupleType;
import visad.RealType;
     *



import visad.VisADException;

import visad.georef.EarthLocation;
import visad.georef.EarthLocationTuple;
import visad.georef.LatLonPoint;
import visad.georef.MapProjection;
import visad.georef.TrivialMapProjection;


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.Rectangle2D;

import java.rmi.RemoteException;

import java.text.Collator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
import java.util.TreeSet;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;





/**
 * A wrapper around a MapProjectDisplay display master.
 * Provides an interface for managing user interactions, gui creation, etc.
 *
 * @author IDV development team
 */

public class MapViewManager extends NavigatedViewManager {

    /** front globe clipping distance */
    public static final String PROP_CLIPDISTANCE_GLOBE_FRONT =
        "idv.clipdistance.globe.front";

    /** back globe clipping distance */

    public static final String PROP_CLIPDISTANCE_GLOBE_BACK =
        "idv.clipdistance.globe.back";

    /** fron map clipping distance */
    public static final String PROP_CLIPDISTANCE_MAP_FRONT =
        "idv.clipdistance.map.front";

    /** back map clipping distance */
    public static final String PROP_CLIPDISTANCE_MAP_BACK =
        "idv.clipdistance.map.back";


    /** flythrough command */
    public static final String CMD_FLY_LEFT = "cmd.fly.left";

    /** flythrough command */
    public static final String CMD_FLY_RIGHT = "cmd.fly.right";

    /** flythrough command */
    public static final String CMD_FLY_FORWARD = "cmd.fly.forward";

    /** flythrough command */
    public static final String CMD_FLY_BACK = "cmd.fly.back";

    /** preference id for the list of addresses in the geocode dialog */
    public static final String PREF_ADDRESS_LIST = "view.address.list";

    /** do we reproject when we go to an address */
    public static final String PREF_ADDRESS_REPROJECT =
        "view.address.reproject";

    /** Preference for autorotate in globe mode */
    public static final String PREF_AUTOROTATE = "View.AutoRotate";

    /** Preference for  showing display in perspective view_ */
    public static final String PREF_PERSPECTIVEVIEW = "View.PerspectiveView";

    /** Preference for  default projection */
    private LineDrawing globeBackgroundDisplayable;
    public static final String PREF_PROJ_DFLT = "View.ProjectionDflt";

    /** Preference for  setting projection automatically from data_ */
    public static final String PREF_PROJ_USEFROMDATA = "View.UseFromData";

    /** Preference for  showing the pip */
    public static final String PREF_SHOWPIP = "View.ShowPip";

    /** Preference for progressive resolution */
    public static final String PREF_USE_PROGRESSIVE_RESOLUTION = 
    	"View.UseProgressiveResolution";

    /** label for progressive resolution/disclosure/whatever we call it */
    private static final String PR_LABEL = "Use Progressive Disclosure";
    
    /** Preference for showing the globe background */
    public static final String PREF_SHOWGLOBEBACKGROUND =
        "View.ShowGlobeBackground";

    /** Preference for the globe background  color */
    public static final String PREF_GLOBEBACKGROUND = "View.GlobeBackground";

    /** Preference for  showing the earth nav panel */
    public static final String PREF_SHOWEARTHNAVPANEL =
        "View.ShowEarthNavPanel";


    /** Defines the projection when sharing state */
    public static final String SHARE_PROJECTION =
        "MapViewManager.SHARE_PROJECTION";

    /**
     * This got set from the ViewManager properties. It is a comma
     * delimited list of map resources
     */
    private String initialMapResources;


    /** The display projection we are currently using */
    private MapProjection mainProjection;

    /** The name of the display projection we are currently using */
    private String mainProjectionName = null;

    /** The name of the default projection */
    private String defaultProjectionName = null;


    /** Keep track of the projections we have used */
    private ArrayList projectionHistory = new ArrayList();

    /** Main projections menu */
    private JMenu projectionsMenu;

    /** Big blob of xml map state from the map widget */
    private String mapState;

    /** Are we using the globe display */
    private boolean useGlobeDisplay = false;
    /** Are we 2d or 3d */
    private boolean use3D = true;

    /** The earth nav panel */
    EarthNavPanel earthNavPanel;

    /** Where the earth nav panel goes */
    JPanel earthNavPanelWrapper;

    /** The map panel in the GUI */
    private PipPanel pipPanel;

    /** mutex for dealing with the pip map */
    private Object PIP_MUTEX = new Object();

    /** Holds the pip map */
    private JComponent pipPanelWrapper;


    /** Do we reproject when we goto address */
    private static JCheckBox addressReprojectCbx;

    /** For checking if kmz capture is ok */
    private JCheckBox fixViewpointCbx;

    /** For checking if kmz capture is ok */
    private JCheckBox fixProjectionCbx;

    /** rotate button */
    JToggleButton rotateBtn;

    /** contour info dialog for preferences */
    ContourInfoDialog cid;

    /** background color for filled globe */
    private Color globeBackgroundColor = null;


    /** z level (really radius) for where to put the globe fill layer */
    private double globeBackgroundLevel = -0.01;

    /** globe fill background stuff */
    /** globe fill background stuff */
    private JComponent globeBackgroundColorComp;

    /** globe fill background stuff */
    private ZSlider globeBackgroundLevelSlider;


    /** The flythrough */
    private Flythrough flythrough;

    /** show maps flag */
    private boolean showMaps = true;

    /** init maps flag */
    private String initMapPaths;

    /** initial map width */
    private float initMapWidth = -1;

    /** initial map color */
    private Color initMapColor = null;

    /** initial lat/lon visibility */
    private boolean initLatLonVisible = false;

    /** initial lat/lon width */
    private int initLatLonWidth = 1;

    /** initial lat/lon spacing */
    private float initLatLonSpacing = 15;

    /** initial lat/lon color */
    private Color initLatLonColor = Color.white;

    /** initial lat/lon bounds */
    private Rectangle2D.Float initLatLonBounds;

    /** use default globe background flag */
    private boolean defaultGlobeBackground = false;

    /** initial display projection zoom */
    private double displayProjectionZoom = 0;

    /** do not set projection flag */
    private boolean doNotSetProjection = false;

    /** lat/lon scale widget */
    private LatLonScalePanel latLonScaleWidget;

    /** Lat axis scale info for unpersistence */
    private LatLonAxisScaleInfo latAxisScaleInfo;

    /** Lon axis scale info for unpersistence */
    private LatLonAxisScaleInfo lonAxisScaleInfo;

    /**
     *  Default constructor
     */
    public MapViewManager() {}


    /**
     * Construct a MapViewManager from an IDV
     *
     * @param viewContext Really the IDV
     */
    public MapViewManager(ViewContext viewContext) {
        super(viewContext);
    }

    /**
     * Construct a MapViewManager with the specified params
     * @param viewContext   context in which this MVM exists
     * @param desc   ViewDescriptor
     * @param properties   semicolon separated list of properties (can be null)
     *
     * @throws RemoteException Java RMI problem
     * @throws VisADException  Couldn't create the VisAD object
     */
    public MapViewManager(ViewContext viewContext, ViewDescriptor desc,
                          String properties)
            throws VisADException, RemoteException {
        super(viewContext, desc, properties);
    }


    /**
     * Get the default projection to use
     *
     * @return The default projection
     */
    public ProjectionImpl getDefaultProjection() {
        return getIdv().getIdvProjectionManager().getDefaultProjection();
    }




    /**
     * Make the DisplayMaster for this ViewManager
     *
     * @return the DisplayMaster
     *
     * @throws RemoteException Java RMI problem
     * @throws VisADException  Couldn't create the VisAD object
     */
    protected DisplayMaster doMakeDisplayMaster()
            throws VisADException, RemoteException {
        StateManager         stateManager = getStateManager();
        IntegratedDataViewer idv          = getIdv();
        if ((idv == null) || (stateManager == null)) {
            return null;
        }
        boolean mode3d = stateManager.getProperty(IdvConstants.PROP_3DMODE,
                             use3D);
        mode3d = getStore().get(PREF_DIMENSION, mode3d);
        // let property override the preference
        use3D = mode3d && use3D;
        int mode = (use3D
                    ? NavigatedDisplay.MODE_3D
                    : NavigatedDisplay.MODE_2Din3D);

        if ( !visad.util.Util.canDoJava3D()) {
            mode = NavigatedDisplay.MODE_2D;
        }

        boolean useGlobe = getUseGlobeDisplay()
                           && (mode != NavigatedDisplay.MODE_2D);

        Dimension dimension = stateManager.getViewSize();
        if (dimension == null) {
            if ((getFullScreenWidth() > 0) && (getFullScreenHeight() > 0)) {
                dimension = new Dimension(getFullScreenWidth(),
                                          getFullScreenHeight());
            } else if (displayBounds != null) {
                dimension = new Dimension(displayBounds.width,
                                          displayBounds.height);
            }
        }

        if ((dimension == null) || (dimension.width == 0)
                || (dimension.height == 0)) {
            dimension = null;
        }

        NavigatedDisplay navDisplay = null;


        if (useGlobe) {
            //TODO: Set the dimension
            GlobeDisplay globeDisplay =
                new GlobeDisplay(idv.getArgsManager().getIsOffScreen(),
                                 dimension, null);
            globeDisplay.setClipDistanceFront(
                getStateManager().getProperty(
                    PROP_CLIPDISTANCE_GLOBE_FRONT,
                    NavigatedDisplay.CLIP_FRONT_DEFAULT));
            globeDisplay.setClipDistanceBack(
                getStateManager().getProperty(
                    PROP_CLIPDISTANCE_GLOBE_BACK,
                    NavigatedDisplay.CLIP_BACK_DEFAULT));
            navDisplay = globeDisplay;
            setGlobeBackground(globeDisplay);
            navDisplay.setPolygonOffset(
                getStateManager().getProperty("idv.globe.polygonoffset", 1));
            navDisplay.setPolygonOffsetFactor(
                getStateManager().getProperty(
                    "idv.globe.polygonoffsetfactor", 1));
        } else {
            Trace.call1("MapViewManager.doMakeDisplayMaster projection");
            if (mainProjection == null) {
                if (initLatLonBounds != null) {
                    doNotSetProjection = true;
                    mainProjection =
                        ucar.visad.Util
                            .makeMapProjection(initLatLonBounds.getY()
                                - initLatLonBounds
                                    .getHeight(), initLatLonBounds.getX(),
                                        initLatLonBounds.getY(),
                                        initLatLonBounds.getX()
                                        + initLatLonBounds.getWidth(), false);


                } else {
                    ProjectionImpl dfltProjection = null;
                    if (defaultProjectionName != null) {
                        dfltProjection =
                            idv.getIdvProjectionManager()
                                .findProjectionByName(defaultProjectionName);
                        if (dfltProjection != null) {
                            doNotSetProjection = true;
                        }
                    }

                    if (dfltProjection == null) {
                        dfltProjection = getDefaultProjection();
                    }
                    mainProjection =
                        new ProjectionCoordinateSystem(dfltProjection);
                }
            }
            if ( !idv.getArgsManager().isScriptingMode()) {
                addProjectionToHistory(mainProjection, "Default");
            }

            Trace.call1("MapViewManager.new MPD");
            MapProjectionDisplay mapDisplay =
                MapProjectionDisplay.getInstance(mainProjection, mode,
                    idv.getArgsManager().getIsOffScreen(), dimension);
            Trace.call2("MapViewManager.new MPD");

            mapDisplay.setClipDistanceFront(
                getStateManager().getProperty(
                    PROP_CLIPDISTANCE_MAP_FRONT,
                    NavigatedDisplay.CLIP_FRONT_DEFAULT));
            mapDisplay.setClipDistanceBack(
                getStateManager().getProperty(
                    PROP_CLIPDISTANCE_MAP_BACK,
                    NavigatedDisplay.CLIP_BACK_DEFAULT));


            if (initLatLonBounds == null) {
                double[] aspect = getAspectRatio();
                if (aspect == null) {
                    aspect = new double[] { 1.0, 1.0, 0.4 };
                }
                mapDisplay.setDisplayAspect((mode == NavigatedDisplay.MODE_2D)
                                            ? new double[] { aspect[0],
                        aspect[1] }
                                            : aspect);
            } else {
                //If we have a a latlonbounds then we want to display exactly that area
                double[] aspect = new double[] { 1.0,
                        initLatLonBounds.getHeight()
                        / initLatLonBounds.getWidth(),
                        1.0 };
                double[] scaleMatrix =
                    mapDisplay.getMouseBehavior().make_matrix(0.0, 0.0, 0.0,
                        aspect[0], aspect[1], 1, 0.0, 0.0, 0.0);
                //                mapDisplay.setProjectionMatrix(scaleMatrix);
            }


            navDisplay = mapDisplay;
            navDisplay.setPerspectiveView(getPerspectiveView());

            if ((defaultProjectionName != null)
                    && (displayProjectionZoom != 0)) {
                navDisplay.zoom(displayProjectionZoom);
            }
            defaultProjectionName = null;
            initLatLonBounds      = null;

            Trace.call2("MapViewManager.doMakeDisplayMaster projection");
            navDisplay.setPolygonOffset(
                getStateManager().getProperty("idv.map.polygonoffset", 1));
            navDisplay.setPolygonOffsetFactor(
                getStateManager().getProperty(
                    "idv.map.polygonoffsetfactor", 1));
        }





        return navDisplay;
    }



    /**
     * Get the earth location of the screen center
     *
     * @return screen center
     *
     * @throws RemoteException On badness
     * @throws VisADException On badness
     */
    public EarthLocation getScreenCenter()
            throws VisADException, RemoteException {
        return getNavigatedDisplay().getEarthLocation(
            getNavigatedDisplay().getScreenCenter());
    }





    /**
     * Get a list of named locations of the different points of the view rectangle. e.g., center, upper left, etc.
     *
     * @return list of locations
     *
     * @throws RemoteException On badness
     * @throws VisADException On badness
     */
     *
    public List getScreenCoordinates()
            throws VisADException, RemoteException {
        List l = getNavigatedDisplay().getScreenCoordinates();
        List result = new ArrayList();
        for (TwoFacedObject tfo : l) {
            result.add(
                new TwoFacedObject(
                    tfo.toString(),
                    getNavigatedDisplay().getEarthLocation(
                        (double[]) tfo.getId())));
        }
        return result;
    }


    /**
     * Initialize this object.
     *
     * @throws RemoteException
     * @throws VisADException
     */
    protected void init() throws VisADException, RemoteException {
        if (getHaveInitialized()) {
            return;
        }
        super.init();
        Trace.call1("MapViewManager.init checkDefaultMap",
                    " showMap:" + showMaps);

        checkDefaultMap();
        Trace.call2("MapViewManager.init checkDefaultMap");

        if (useGlobeDisplay) {
            //            if(!hasBooleanProperty(PREF_SHOWGLOBEBACKGROUND)) {
            initializeBooleanProperty(
                new BooleanProperty(
                    PREF_SHOWGLOBEBACKGROUND, "Show Globe Background",
                    "Show Globe Background", defaultGlobeBackground));
            //            }
        }





    }


    /**
     * Initialize the UI
     */
    protected void initUI() {
        super.initUI();
        //Initialize the flythrough here
        if (flythrough != null) {
            flythrough.init(this);
            if (flythrough.getShown()) {
                flythrough.show();
            }
        }
    }


    /**
     * Fill the legends
     */
    }
    protected void fillLegends() {
        super.fillLegends();
        if (flythrough != null) {
            flythrough.displayControlChanged();
        }
    }

    /**
     * Handle a perspective view change
     *
     * @param v the value
     */
    protected void perspectiveViewChanged(boolean v) {
        setPerspectiveView(v);
        super.perspectiveViewChanged(v);
        notifyDisplayControls(PREF_PERSPECTIVEVIEW);
    }

    /**
     * Handle a vertical scale change
     * @deprecated see {@link #verticalRangeChanged()}
     */
    protected void verticalScaleChanged() {
        verticalRangeChanged();
    }

    /**
     * Handle a vertical range change
     */
    protected void verticalRangeChanged() {
        super.verticalRangeChanged();
        notifyDisplayControls(SHARE_PROJECTION);
    }

    /**
     * Should we animate view changes
     *
     * @return true if not running ISL
     */
    public boolean shouldAnimateViewChanges() {
        return !getStateManager().getRunningIsl();
    }

    /**
     * Handle the event
     *
     * @param event The event
                        matrix);

     * @throws RemoteException On badness
     * @throws VisADException On badness
     */
    public void displayChanged(DisplayEvent event)
            throws VisADException, RemoteException {
        if (getIsDestroyed()) {
            return;
        }

        checkPipPanel();

        if (flythrough != null) {
            flythrough.displayChanged(event);
        }


        NavigatedDisplay navDisplay = getMapDisplay();
        if ( !navDisplay.getAutoRotate()
                && getViewpointControl().getAutoRotate()) {
            getViewpointControl().setAutoRotate(false);
        }

        int        id         = event.getId();
        InputEvent inputEvent = event.getInputEvent();
        if ((id == DisplayEvent.KEY_PRESSED)
                && (inputEvent instanceof KeyEvent)) {
            KeyEvent keyEvent = (KeyEvent) inputEvent;
            if (GuiUtils.isControlKey(keyEvent, KeyEvent.VK_N)) {
                EarthLocation center = getScreenCenter();
                getMapDisplay().centerAndZoom(center, null, 1.0,
                        shouldAnimateViewChanges(), true);
                return;
            }

            if (GuiUtils.isControlKey(keyEvent, KeyEvent.VK_S)) {
                EarthLocation center = getScreenCenter();
                getMapDisplay().centerAndZoom(center, null, 1.0,
                        shouldAnimateViewChanges(), false);
                return;
            }


            if (GuiUtils.isControlKey(keyEvent)
                    && ((keyEvent.getKeyCode() == KeyEvent.VK_H)
                        || (keyEvent.getKeyCode() == KeyEvent.VK_J)
                        || (keyEvent.getKeyCode() == KeyEvent.VK_K)
                        || (keyEvent.getKeyCode() == KeyEvent.VK_L))) {
                double[] matrix = getProjectionControl().getMatrix();
                double[] rot   = new double[3];
                double[] scale = new double[3];
                double[] trans = new double[3];
                MouseBehavior mouseBehavior =
                    getNavigatedDisplay().getMouseBehavior();
                mouseBehavior.instance_unmake_matrix(rot, scale, trans,
                double[] t = null;
                if (keyEvent.getKeyCode() == KeyEvent.VK_H) {
                    t = mouseBehavior.make_matrix(-5, 0.0, 0, 1.0, 0.0, 0.0,
                            0.0);
                } else if (keyEvent.getKeyCode() == KeyEvent.VK_J) {
                    t = mouseBehavior.make_matrix(5, 0.0, 0, 1.0, 0.0, 0.0,
                            0.0);
                } else if (keyEvent.getKeyCode() == KeyEvent.VK_K) {
                    t = mouseBehavior.make_matrix(0, -5.0, 0, 1.0, 0.0, 0.0,
                            0.0);
                } else if (keyEvent.getKeyCode() == KeyEvent.VK_L) {
                    t = mouseBehavior.make_matrix(0, 5.0, 0, 1.0, 0.0, 0.0,
                            0.0);
                }
                matrix = mouseBehavior.multiply_matrix(t, matrix);
                getMaster().setProjectionMatrix(matrix);

                return;
            }
        }


        super.displayChanged(event);
    }



    /**
     * Handle the mouse flicked
     *
     * @param startPoint  start point of flick
     * @param endPoint  end point of flick
     * @param startMatrix the start matrix
     * @param endMatrix the end matrix
     * @param speed  the speed of flicking
     */
    protected void mouseFlicked(Point startPoint, Point endPoint,
                                double[] startMatrix, double[] endMatrix,
        }
    /**
                                double speed) {
        if ( !getUseGlobeDisplay()) {
            return;
        }

        double[] trans = { 0.0, 0.0, 0.0 };
        double[] rot1  = { 0.0, 0.0, 0.0 };
        double[] rot2  = { 0.0, 0.0, 0.0 };
        double[] scale = { 0.0, 0.0, 0.0 };
        getNavigatedDisplay().getMouseBehavior().instance_unmake_matrix(rot1,
                scale, trans, startMatrix);
        getNavigatedDisplay().getMouseBehavior().instance_unmake_matrix(rot2,
                scale, trans, endMatrix);

        //If there was no rotation then return
        if ((rot1[0] == rot2[0]) && (rot1[1] == rot2[1])) {
            return;
        }

        double distance = GuiUtils.distance(startPoint.x, startPoint.y,
                                            endPoint.x, endPoint.y);
        if (distance == 0) {
            return;
        }
        double percentX = (endPoint.x - startPoint.x) / distance;
        double percentY = (endPoint.y - startPoint.y) / distance;
        speed *= 2;
        getNavigatedDisplay().setRotationMultiplierMatrix(speed * -percentY,
                speed * -percentX, 0.0);
        getViewpointControl().setAutoRotate(true);
    }


    /**
     * Check if its ok to capture a kmz file
     *
     * @return ok to capture kmz
     */
    protected boolean checkForKmlImageCapture() {

        //Assume when we are running isl everything is ok
        if (getIdv().getArgsManager().getIsOffScreen()) {
            return true;
        }

        NavigatedDisplay navDisplay = getMapDisplay();
        double[]         rotMatrix  = navDisplay.getRotation();
        if ((rotMatrix[0] != 0) || (rotMatrix[1] != 0)
                || (rotMatrix[2] != 0)) {
            if (fixViewpointCbx == null) {
                fixViewpointCbx = new JCheckBox("Fix it", true);
            }

            JComponent question =
                GuiUtils
                    .vbox(new JLabel(
                        "The viewpoint is not overhead. This will result in an incorrect image capture."), GuiUtils
                            .left(fixViewpointCbx));
            if ( !GuiUtils.askOkCancel("KML Capture", question)) {
                return false;
            }
            if (fixViewpointCbx.isSelected()) {
                try {
                    navDisplay.resetProjection();
                } catch (Exception exc) {
                    throw new RuntimeException(exc);
                }
            } else {
                return true;
            }
        }


        int cnt = 0;
        while (true) {
            cnt++;
            Rectangle sb = navDisplay.getDisplayComponent().getBounds();
            LatLonPoint ul =
                getMapDisplay().getEarthLocation(
                    getMapDisplay().getSpatialCoordinatesFromScreen(
                        0, 0)).getLatLonPoint();
            LatLonPoint ur =
                getMapDisplay().getEarthLocation(
                    getMapDisplay().getSpatialCoordinatesFromScreen(
                        sb.width, 0)).getLatLonPoint();
            LatLonPoint lr =
                getMapDisplay().getEarthLocation(
                    getMapDisplay().getSpatialCoordinatesFromScreen(
                        sb.width, sb.height)).getLatLonPoint();
            LatLonPoint ll =
                getMapDisplay().getEarthLocation(
                    getMapDisplay().getSpatialCoordinatesFromScreen(
                        0, sb.height)).getLatLonPoint();

            double width = Math.abs(ul.getLongitude().getValue()
                                    - ur.getLongitude().getValue());

            double height = Math.abs(ul.getLatitude().getValue()
                                     - ll.getLatitude().getValue());


            boolean projOk = true;

            if ( !isClose(width, ul.getLongitude().getValue(),
                          ll.getLongitude().getValue())) {
                projOk = false;
            }
            if ( !isClose(width, ur.getLongitude().getValue(),
                          lr.getLongitude().getValue())) {
                projOk = false;
            }
            if ( !isClose(height, ul.getLatitude().getValue(),
                          ur.getLatitude().getValue())) {
                projOk = false;
            }
            if ( !isClose(height, ll.getLatitude().getValue(),
                          lr.getLatitude().getValue())) {
                projOk = false;
            }

            if (projOk) {
                return true;
            }

            if (fixProjectionCbx == null) {
                fixProjectionCbx = new JCheckBox("Fix it", true);
            }
            String msg = ((cnt == 1)
                          ? "The projection is not lat/lon. This will result in an incorrect image capture."
                          : "For some reason the projection is still not lat/lon.");
            JComponent question = GuiUtils.vbox(new JLabel(msg),
                                      GuiUtils.left(fixProjectionCbx));
            if ( !GuiUtils.askOkCancel("KML Capture", question)) {
                return false;
            }

            if ( !fixProjectionCbx.isSelected()) {
                return true;
            }

            if (fixProjectionCbx.isSelected()) {
                try {
                    setCurrentAsProjection();
                } catch (Exception exc) {
                    throw new RuntimeException(exc);
                }
                Misc.sleep(1000);
            }
        }

    }



     * are the 2 values close
     *
     * @param span the range
     * @param value1 value 1
     * @param value2 value 2
     *
     * @return are the 2 values close
     */
    private boolean isClose(double span, double value1, double value2) {
        //Check that the difference of the two values is < 1% of the given span value
        if (Math.abs((value1 - value2) / span) > 0.01) {
            return false;
        }
        return true;
    }


    /**
     * Check for the default map
     */
    private void checkDefaultMap() {
        if ( !showMaps) {
            return;
        }
        MapDisplayControl defaultMap = findDefaultMap();
        if (defaultMap == null) {
            try {
                ControlDescriptor mapCD =
                    getIdv().getControlDescriptor(
                        ControlDescriptor.DISPLAYID_MAP);
                if (mapCD == null) {
                    return;
                }

                MapInfo mapInfo;
                if (mapState != null) {
                    Trace.call1("checkDefaultMap-1");
                    mapInfo = new MapInfo(XmlUtil.getRoot(mapState));
                    mapInfo.setJustLoadedLocalMaps(true);
                    Trace.call2("checkDefaultMap-1");
                    //SKIP the initial map resources for now
                } else if (false && (initialMapResources != null)) {
                    //This got set from the ViewManager properties. It is a comma
                    //delimited list of map resources 
                    Trace.call1("checkDefaultMap-2");
                    List resources = getResourceManager().getResourcePaths(
                                         StringUtil.split(
                                             initialMapResources, ",", true,
                                             true));
                    XmlResourceCollection customMapResources =
                        new XmlResourceCollection("custom maps");
                    customMapResources.addResources(resources);
                    mapInfo = new MapInfo(customMapResources, true, true);
                    Trace.call2("checkDefaultMap-2");
                } else {
                    XmlResourceCollection xrc =
                        getResourceManager().getMapResources(
                            getUseGlobeDisplay());
                    //                    XmlResourceCollection xrc = getResourceManager().getMapResources(false);
                    mapInfo = new MapInfo(xrc, false, true);
                }

                if (initLatLonVisible) {
                    mapInfo.getLatData().setVisible(true);
                    mapInfo.getLatData().setLineWidth(initLatLonWidth);
                    mapInfo.getLatData().setSpacing(initLatLonSpacing);
                    mapInfo.getLatData().setColor(initLatLonColor);

                    mapInfo.getLonData().setVisible(true);
                    mapInfo.getLonData().setLineWidth(initLatLonWidth);
                    mapInfo.getLonData().setSpacing(initLatLonSpacing);
                    mapInfo.getLonData().setColor(initLatLonColor);
                }

                if (initMapPaths != null) {
                    for (MapData mapData : mapInfo.getMapDataList()) {
                        mapData.setVisible(false);
                        if (initMapWidth > 0) {
                            mapData.setLineWidth(initMapWidth);
                        }
                        if (initMapColor != null) {
                            mapData.setColor(initMapColor);
                        }
                    }
                    for (String mapPath :
                            StringUtil.split(initMapPaths, ",", true, true)) {
                        for (MapData mapData : mapInfo.getMapDataList()) {
                            if (Misc.equals(mapData.getSource(), mapPath)) {

                                mapData.setVisible(true);
                            }
                        }
                    }
                }

                Trace.call1("checkDefaultMap-making map");
                defaultMap = new MapDisplayControl(this, mapInfo);
                defaultMap.setIsDefaultMap(true);
                Hashtable newProperties =
                    new Hashtable(mapCD.getProperties());
                newProperties.put("displayName", "Default Background Maps");
                mapCD.initControl(defaultMap, new ArrayList(), getIdv(),
                                  newProperties, null);
                Trace.call2("checkDefaultMap-making map");
            } catch (Exception exc) {
                logException("Initializing maps", exc);
            }
        }
    }




    /**
     * Can this view manager be used in exchange for the given view manager
     *
     * @param that The other view manager to check
     * @return Can this be used in place of that
     */
    public boolean canBe(ViewManager that) {
        if ( !super.canBe(that)) {
            return false;
        }
        MapViewManager mvm = (MapViewManager) that;
        if (this.getUseGlobeDisplay() != mvm.getUseGlobeDisplay()) {
            return false;
        }
        if (this.getUse3D() != mvm.getUse3D()) {
            return false;
        return true;
    }




    /**
     * Initialize with another view
     *
     * @param viewState  the view state
     *
     * @throws Exception  problems
     */
    public void initWith(ViewState viewState) throws Exception {

        MapProjection thatProjection =
            (MapProjection) viewState.get(ViewState.PROP_PROJECTION);
        if (thatProjection != null) {
            setMapProjection(thatProjection, false, "Projection");
        }
        double[] aspect =
            (double[]) viewState.get(ViewState.PROP_ASPECTRATIO);
        if (aspect != null) {
            this.setAspectRatio(aspect);
        }

        super.initWith(viewState);
    }

    /**
     * Handle the animation time changed
     */
    protected void animationTimeChanged() {
        super.animationTimeChanged();
        if (flythrough != null) {
            flythrough.animationTimeChanged();
        }
    }


    /**
     * Initialize this object's state with the state from that.
     *
     * @param that The other obejct to get state from
     * @param ignoreWindow If true then don't set the window size and location
     *
     * @throws RemoteException Java RMI problem
     * @throws VisADException  Couldn't create the VisAD object
     */
    protected void initWithInner(ViewManager that, boolean ignoreWindow)
            throws VisADException, RemoteException {

        if (getInitViewStateName() != null) {
            List vms = getIdv().getVMManager().getVMState();
            for (int i = 0; i < vms.size(); i++) {
                ViewState viewState = (ViewState) vms.get(i);
                if (viewState.getName().equals(getInitViewStateName())) {
                    if (isCompatibleWith(viewState)) {
                        try {
                            initWith(viewState);
                            break;
                        } catch (Exception exc) {
                            throw new RuntimeException(exc);
                        }
                    } else {
                        setInitViewStateName(null);
                        break;
                    }
                }
            }

        }



        if ( !(that instanceof MapViewManager)) {
            return;
        }


        if (displayProjectionZoom != 0) {
            getMapDisplay().zoom(displayProjectionZoom);
        }

        if (doNotSetProjection) {
            return;
        }




        MapViewManager mvm            = (MapViewManager) that;
        MapProjection  thatProjection = mvm.getMainProjection();
        if (getInitViewStateName() == null) {
            this.setAspectRatio(that.getAspectRatio());
        }

        if ((mvm.flythrough != null) && (mvm.flythrough != this.flythrough)) {
            if (this.flythrough != null) {
                this.flythrough.destroy();
                //                this.flythrough.initWith(mvm.flythrough);
            }
            this.flythrough = mvm.flythrough;
            this.flythrough.setViewManager(this);
            if (this.flythrough.getShown()) {
                this.flythrough.show();
            }
        }

        boolean setProjection = false;
        if (thatProjection != null) {
            setProjection = setMapProjection(thatProjection, false,
                                             mvm.mainProjectionName);
        }

        if (getInitViewStateName() == null) {
            if ( !setProjection) {
                if (getAspectRatio() != null) {
                    getMapDisplay().setDisplayAspect(getAspectRatio());
                }
            }
        }

        //Only save the projection if we're not a globe
        //        getMapDisplay().saveProjection();
        if ( !getUseGlobeDisplay()) {
            getMapDisplay().saveProjection();
        }

        this.globeBackgroundColor = mvm.globeBackgroundColor;
        this.globeBackgroundLevel = mvm.globeBackgroundLevel;
        if (globeBackgroundDisplayable != null) {
            setGlobeBackground((GlobeDisplay) getMapDisplay());
        }



        super.initWithInner(that, ignoreWindow);
        try {
            //If we have an old bundle then the other map view has a non-null
            //map state. If so we load it in.
            if (mvm.mapState != null) {
                MapDisplayControl defaultMap = findDefaultMap();
                if (defaultMap != null) {
                    MapInfo mapInfo =
                        new MapInfo(XmlUtil.getRoot(mvm.mapState));
                    MapDisplayControl newMap = new MapDisplayControl(this,
                                                   mapInfo);
                    newMap.init((ucar.unidata.data.DataChoice) null);
                    defaultMap.loadNewMap(newMap);
                }
            }

        } catch (Exception exc) {
            logException("Initializing with MapViewManager", exc);
        }

        LatLonAxisScaleInfo latAxisScaleInfo = mvm.getLatAxisScaleInfo();

        if (latAxisScaleInfo != null) {
            setLatAxisScaleInfo(latAxisScaleInfo);
        }

        LatLonAxisScaleInfo lonAxisScaleInfo = mvm.getLonAxisScaleInfo();

        if (lonAxisScaleInfo != null) {
            setLonAxisScaleInfo(lonAxisScaleInfo);
        }


    }


    /**
     * Initialize the ViewState
     *
     * @param viewState the ViewState
     */
    public void initViewState(ViewState viewState) {
        super.initViewState(viewState);
        viewState.put(ViewState.PROP_GLOBE,
                      new Boolean(getUseGlobeDisplay()));
        if ( !getUseGlobeDisplay()) {
            viewState.put(ViewState.PROP_PROJECTION, getMainProjection());
        }
    }


    /**
     * Get the JComponent for the VisAD display
     *
     * @return VisAD display's Component
     */
    public JComponent getInnerContents() {
        return (JComponent) getMapDisplay().getComponent();
    }





    /**
     * Leave this here for old bundles
     *
     * @param ms The map specification
     */
    public void setMapState(String ms) {
        mapState = ms;
    }




    /**
     * Add in the different preference panels.
     *
     * @param preferenceManager The preference manager to add things into
     */
    public void initPreferences(
            final IdvPreferenceManager preferenceManager) {

        super.initPreferences(preferenceManager);

        final JComponent[] bgComps =
            GuiUtils.makeColorSwatchWidget(getStore().get(PREF_BGCOLOR,
                getBackground()), "Set Background Color");

        final JComponent[] fgComps =
            GuiUtils.makeColorSwatchWidget(getStore().get(PREF_FGCOLOR,
                getForeground()), "Set Foreground Color");

        final JComponent[] border =
            GuiUtils
                .makeColorSwatchWidget(getStore()
                    .get(PREF_BORDERCOLOR, ViewManager
                        .borderHighlightColor), "Set Selected Panel Border Color");

        final JComponent[] globeComps =
            GuiUtils.makeColorSwatchWidget(getGlobeBackgroundColorToUse(),
                                           "Globe Background Color");
        StateManager stateManager = getStateManager();

        GuiUtils.tmpInsets = new Insets(5, 5, 5, 5);
        JPanel colorPanel = GuiUtils.left(GuiUtils.doLayout(new Component[] {
            GuiUtils.rLabel("  Background:"), bgComps[0], bgComps[1],
            GuiUtils.rLabel("  Foreground:"), fgComps[0], fgComps[1],
            GuiUtils.rLabel("  Selected Panel:"), border[0], border[1],
            GuiUtils.rLabel("  Globe Background:"), globeComps[0],
            globeComps[1],
        }, 3, GuiUtils.WT_N, GuiUtils.WT_N));
        colorPanel = GuiUtils.vbox(new JLabel("Color Scheme:"), colorPanel);

        cid        = new ContourInfoDialog("Preferences", false, null, false);
        ContourInfo ci =
            new ContourInfo(
                null, 0, 0, 10, true, false, false, 1, 0,
                ContourControl.LABEL_FREQ_LO,
                (int) stateManager
                    .getPreferenceOrProperty(
                        PREF_CONTOUR_LABELSIZE,
                        ContourInfo.DEFAULT_LABEL_SIZE), ContourInfoDialog
                            .getContourFont(
                                stateManager
                                    .getPreferenceOrProperty(
                                        PREF_CONTOUR_LABELFONT)), stateManager
                                            .getPreferenceOrProperty(
                                                PREF_CONTOUR_LABELALIGN,
                                                true));
        cid.setState(ci);

        JPanel contourPanel =
            GuiUtils.vbox(new JLabel("Contour Labels:"),
                          GuiUtils.inset(cid.getLabelPanel(),
                                         new Insets(5, 20, 0, 0)));
        contourPanel = GuiUtils.topCenter(contourPanel, GuiUtils.filler());
        colorPanel   = GuiUtils.hbox(colorPanel, contourPanel);

        final FontSelector fontSelector =
            new FontSelector(FontSelector.COMBOBOX_UI, false, false);
        Font f = getStore().get(PREF_DISPLAYLISTFONT, getDisplayListFont());
        fontSelector.setFont(f);
        final GuiUtils.ColorSwatch dlColorWidget =
            new GuiUtils.ColorSwatch(getStore().get(PREF_DISPLAYLISTCOLOR,
                getDisplayListColor()), "Set Display List Color");
        //GuiUtils.tmpInsets = new Insets(5, 5, 5, 5);
        JPanel fontPanel = GuiUtils.vbox(GuiUtils.lLabel("Display List:"),
                                         GuiUtils.doLayout(new Component[] {
                                             GuiUtils.rLabel("Font:"),
                                             GuiUtils.left(fontSelector.getComponent()),
                                             GuiUtils.rLabel("Color:"),
                                             GuiUtils.left(GuiUtils.hbox(dlColorWidget,
                                                 dlColorWidget.getSetButton(),
                                                 dlColorWidget.getClearButton(),
                                                 5)) }, 2, GuiUtils.WT_N,
                                                     GuiUtils.WT_N));


        List            projections = getProjectionList();
        final JComboBox projBox     = new JComboBox();
        final Hashtable projMap = new Hashtable();

        Collection projNames =
            new TreeSet(Collator.getInstance());

        for (int p = 0; p < projections.size(); p++) {
            String projName = ((ProjectionImpl) projections.get(p)).getName();
            projMap.put(projName, projections.get(p));
            projNames.add(projName);
        }

        GuiUtils.setListData(projBox, projNames.toArray());
        Object defaultProj = getDefaultProjection();
        if (defaultProj != null) {
            if (defaultProj instanceof ProjectionImpl) {
                projBox.setSelectedItem(
                    ((ProjectionImpl) defaultProj).getName());
            } else {
                projBox.setSelectedItem(defaultProj);
            }
        }

        final JCheckBox logoVizBox = new JCheckBox(
                                         "Show Logo in View",
                                         stateManager.getPreferenceOrProperty(
                                             PREF_LOGO_VISIBILITY, false));
        final JTextField logoField =
            new JTextField(stateManager.getPreferenceOrProperty(PREF_LOGO,
                ""));
        logoField.setToolTipText("Enter a file or URL");
        // top panel
        JButton browseButton = new JButton("Browse..");
        browseButton.setToolTipText("Choose a logo from disk");
        browseButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                String filename =
                    FileManager.getReadFile(FileManager.FILTER_IMAGE);
                if (filename == null) {
                    return;
                }
                logoField.setText(filename);
            }
        });

        String[] logos = parseLogoPosition(
                             stateManager.getPreferenceOrProperty(
                                 PREF_LOGO_POSITION_OFFSET, ""));
        final JComboBox logoPosBox = new JComboBox(logoPoses);
        logoPosBox.setToolTipText("Set the logo position on the screen");
        logoPosBox.setSelectedItem(findLoc(logos[0]));

        final JTextField logoOffsetField = new JTextField(logos[1]);
        logoOffsetField.setToolTipText(
            "Set an offset from the position (x,y)");

        float logoScaleFactor =
            (float) stateManager.getPreferenceOrProperty(PREF_LOGO_SCALE,
                1.0);
        final JLabel logoSizeLab = new JLabel("" + logoScaleFactor);
        JComponent[] sliderComps = GuiUtils.makeSliderPopup(0, 20,
                                       (int) (logoScaleFactor * 10), null);
        final JSlider  logoScaleSlider = (JSlider) sliderComps[1];
        ChangeListener listener        = new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                logoSizeLab.setText("" + logoScaleSlider.getValue() / 10.f);
            }
        };
        logoScaleSlider.addChangeListener(listener);
        sliderComps[0].setToolTipText("Change Logo Scale Value");

        JPanel logoPanel =
            GuiUtils.vbox(
                GuiUtils.left(logoVizBox),
                GuiUtils.centerRight(logoField, browseButton),
                GuiUtils.hbox(
                    GuiUtils.leftCenter(
                        GuiUtils.rLabel("Screen Position: "),
                        logoPosBox), GuiUtils.leftCenter(
                            GuiUtils.rLabel("Offset: "),
                            logoOffsetField), GuiUtils.leftCenter(
                                GuiUtils.rLabel("Scale: "),
                                GuiUtils.leftRight(
                                    logoSizeLab, sliderComps[0]))));
        logoPanel = GuiUtils.vbox(GuiUtils.lLabel("Logo: "),
                                  GuiUtils.left(GuiUtils.inset(logoPanel,
                                      new Insets(5, 5, 0, 0))));


        PreferenceManager miscManager = new PreferenceManager() {
            public void applyPreference(XmlObjectStore theStore,
                                        Object data) {
                IdvPreferenceManager.applyWidgets((Hashtable) data, theStore);
                if (projBox.getSelectedItem() instanceof String) {
                    theStore.put(PREF_PROJ_DFLT,
                                 projMap.get(projBox.getSelectedItem()));
                } else {
                    theStore.put(PREF_PROJ_DFLT, projBox.getSelectedItem());
                }
                theStore.put(PREF_BGCOLOR, bgComps[0].getBackground());
                theStore.put(PREF_GLOBEBACKGROUND,
                             globeComps[0].getBackground());
                theStore.put(PREF_FGCOLOR, fgComps[0].getBackground());
                theStore.put(PREF_BORDERCOLOR, border[0].getBackground());
                theStore.put(PREF_DISPLAYLISTFONT, fontSelector.getFont());
                theStore.put(PREF_DISPLAYLISTCOLOR,
                             dlColorWidget.getSwatchColor());
                checkToolBarVisibility();
                ViewManager.setHighlightBorder(border[0].getBackground());
                cid.doApply();
                ContourInfo ci = cid.getInfo();
                theStore.put(PREF_CONTOUR_LABELSIZE, ci.getLabelSize());
                theStore.put(PREF_CONTOUR_LABELFONT, ci.getFont());
                theStore.put(PREF_CONTOUR_LABELALIGN, ci.getAlignLabels());
                theStore.put(PREF_LOGO, logoField.getText());
                String lpos =
                    ((TwoFacedObject) logoPosBox.getSelectedItem()).getId()
                        .toString();
                String loff = logoOffsetField.getText().trim();
                theStore.put(PREF_LOGO_POSITION_OFFSET,
                             makeLogoPosition(lpos, loff));
                theStore.put(PREF_LOGO_VISIBILITY, logoVizBox.isSelected());
                theStore.put(PREF_LOGO_SCALE,
                             logoScaleSlider.getValue() / 10f);

            }
        };


        Hashtable  widgets     = new Hashtable();
        ArrayList  miscList    = new ArrayList();


        Object[][] miscObjects = {
            { "View:", null, null },
            { "Show Wireframe Box", PREF_WIREFRAME,
              new Boolean(getWireframe()) },
            { "Show Cursor Readout", PREF_SHOWCURSOR,
              new Boolean(getShowCursor()) },
            { "Clip View At Box", PREF_3DCLIP, new Boolean(getClipping()) },
            { "Show Display List", PREF_SHOWDISPLAYLIST,
              new Boolean(getShowDisplayList()) },
            { "Show Times In View", PREF_ANIREADOUT,
              new Boolean(getAniReadout()) },
            { "Show Map Display Scales", PREF_SHOWSCALES,
              new Boolean(getLabelsVisible()) },
            { "Show Transect Display Scales", PREF_SHOWTRANSECTSCALES,
              new Boolean(getTransectLabelsVisible()) },
            { "Show \"Please Wait\" Message", PREF_WAITMSG,
              new Boolean(getWaitMessageVisible()) },
            { "Reset Projection With New Data", PREF_PROJ_USEFROMDATA },
            { PR_LABEL, PREF_USE_PROGRESSIVE_RESOLUTION,
            	new Boolean(getUseProgressiveResolution())},
            { "Use 3D View", PREF_DIMENSION },
            { "Show Globe Background", PREF_SHOWGLOBEBACKGROUND,
              new Boolean(getStore().get(PREF_SHOWGLOBEBACKGROUND,
                                         defaultGlobeBackground)) }
        };


        Object[][] legendObjects = {
            { "Legends:", null, null },
            { "Show Side Legend", PREF_SHOWSIDELEGEND,
              new Boolean(getShowSideLegend()) },
            { "Show Bottom Legend", PREF_SHOWBOTTOMLEGEND,
              new Boolean(getShowBottomLegend()) },
            { "Show Animation Boxes", PREF_SHOWANIMATIONBOXES,
              new Boolean(getShowAnimationBoxes()) },
            { "Show Clock On Dashboard", IdvConstants.PROP_SHOWCLOCK_DASH,
              new Boolean(
                  getStateManager().getPreferenceOrProperty(
                      IdvConstants.PROP_SHOWCLOCK_DASH, "true")) },
            { "Show Clock On View Windows", IdvConstants.PROP_SHOWCLOCK_VIEW,
              new Boolean(
                  getStateManager().getPreferenceOrProperty(
                      IdvConstants.PROP_SHOWCLOCK_VIEW, "true")) },
            { "Show Overview Map", PREF_SHOWPIP,
              new Boolean(getStore().get(PREF_SHOWPIP, false)) }
        };

        Object[][] toolbarObjects = {
            { "Toolbars:", null, null },
            { "Show Earth Navigation Panel", PREF_SHOWEARTHNAVPANEL,
              new Boolean(getShowEarthNavPanel()) },
            { "Show Viewpoint Toolbar", PREF_SHOWTOOLBAR + "perspective" },
            { "Show Zoom/Pan Toolbar", PREF_SHOWTOOLBAR + "zoompan" },
            { "Show Undo/Redo Toolbar", PREF_SHOWTOOLBAR + "undoredo" }
        };

        JPanel miscPanel = IdvPreferenceManager.makePrefPanel(miscObjects,
                               widgets, getStore());
        JPanel legendPanel =
            IdvPreferenceManager.makePrefPanel(legendObjects, widgets,
                getStore());
        JPanel toolbarPanel =
            IdvPreferenceManager.makePrefPanel(toolbarObjects, widgets,
                getStore());
        JPanel projPanel =
            GuiUtils.vbox(GuiUtils.lLabel("Default Projection: "),
                          GuiUtils.left(GuiUtils.inset(projBox,
                              new Insets(5, 20, 0, 0))));

        JPanel colorFontPanel = GuiUtils.vbox(GuiUtils.top(colorPanel),
                                    GuiUtils.top(fontPanel)  //,
        //GuiUtils.top(projPanel)
        );




        GuiUtils.tmpInsets = new Insets(5, 5, 5, 5);
        JPanel miscContents = GuiUtils.doLayout(Misc.newList(new Component[] {
            GuiUtils.top(legendPanel), GuiUtils.top(toolbarPanel),
            GuiUtils.top(miscPanel), GuiUtils.top(colorFontPanel),
            GuiUtils.top(projPanel), GuiUtils.top(logoPanel)
        }), 2, GuiUtils.WT_N, GuiUtils.WT_N);


        miscContents = GuiUtils.inset(GuiUtils.left(miscContents), 5);
        preferenceManager.add("View", "View Preferences", miscManager,
                              miscContents, widgets);


    }


    /**
     * Go the a street address
     */
    public void goToAddress() {
        Misc.run(new Runnable() {
            public void run() {
                goToAddressInner();
            }
        });
    }


    /**
     * Popup the address location dialog and translate to the lat/lon.
     */
    private void goToAddressInner() {
        try {
            if (addressReprojectCbx == null) {

                addressReprojectCbx = new JCheckBox("Reproject",
                        getStore().get(PREF_ADDRESS_REPROJECT, true));
                addressReprojectCbx.setToolTipText(
                    "When checked make a simple map projection over the location");

                List savedAddresses =
                    (List) getStore().get(PREF_ADDRESS_LIST);
                if (savedAddresses != null) {
                    GeoUtils.setSavedAddresses(savedAddresses);
                }
            }
            getIdvUIManager().showWaitCursor();
            LatLonPoint llp = GeoUtils.getLocationOfAddress(
                                  GuiUtils.left(getUseGlobeDisplay()
                    ? GuiUtils.filler()
                    : (JComponent) addressReprojectCbx));
            //            System.out.println ("{" + GeoUtils.lastAddress+"}  -ll {" + llp.getLatitude()+","+llp.getLongitude()+"}");
            getIdvUIManager().showNormalCursor();
            if (llp == null) {
                return;
            }

            getStore().put(PREF_ADDRESS_LIST, GeoUtils.getSavedAddresses());
            getStore().put(PREF_ADDRESS_REPROJECT,
                           addressReprojectCbx.isSelected());


            float x      = (float) llp.getLongitude().getValue();
            float y      = (float) llp.getLatitude().getValue();
            float offset = (float) (1.0 / 60.0f);
            Rectangle2D.Float rect = new Rectangle2D.Float(x - offset,
                                         y - offset, offset * 2, offset * 2);
            if ( !getUseGlobeDisplay() && addressReprojectCbx.isSelected()) {
                TrivialMapProjection mp =
                    new TrivialMapProjection(
                        RealTupleType.SpatialEarth2DTuple, rect);

                setMapProjection(mp, true);
            } else {
                getMapDisplay().center(GeoUtils.toEarthLocation(llp),
                                       shouldAnimateViewChanges());
                //                getMapDisplay().center(GeoUtils.toEarthLocation(llp), false);
            }
        } catch (Exception e) {
            getIdvUIManager().showNormalCursor();
            logException("Error going to address", e);
        }
    }



    /**
     * Get the map display.
     *
     * @return The map display. This is the main display for thie view manager.
     */
    public NavigatedDisplay getMapDisplay() {
        return (NavigatedDisplay) getMaster();
    }

    /**
     * Are we in 3d mode
     *
     * @return Is display in 3d?
     */
    public boolean isDisplay3D() {
        return (getMapDisplay().getDisplayMode() == NavigatedDisplay.MODE_3D);
    }

    /**
     * Handle the receipt of shared data
     *
     * @param from Who is it from
     * @param dataId What is it
     * @param data Here it is
     */

    public void receiveShareData(Sharable from, Object dataId,
                                 Object[] data) {
        if ( !getInitDone()) {
            return;
        }
        if (dataId.equals(SHARE_PROJECTION)) {
            setMapProjection(((MapProjection) data[0]), false);
            return;
        }
        super.receiveShareData(from, dataId, data);
    }




    /**
     * Add the PIP panel if needed
     *
     * @param sideLegend The side legend
     *
     * @return The side legend or the sidelegend coupled with the pip panel
     */
    protected JComponent getSideComponent(JComponent sideLegend) {
        if (false && getUseGlobeDisplay()) {
            return sideLegend;
        }
        pipPanel = new PipPanel(this);
        pipPanel.setPreferredSize(new Dimension(100, 100));
        JButton closeBtn =
            GuiUtils.makeImageButton("/auxdata/ui/icons/Cancel16.gif", this,
                                     "hidePip");
        pipPanelWrapper = GuiUtils.topCenter(GuiUtils.right(closeBtn),
                                             pipPanel);
        if ( !getShowPip()) {
            pipPanelWrapper.setVisible(false);
        }
        return GuiUtils.centerBottom(sideLegend, pipPanelWrapper);
    }


    /**
     * Make the GUI contents.
     *
     * @return The GUI contents
     */
    protected Container doMakeContents() {
        NavigatedDisplay navDisplay   = getMapDisplay();
        JComponent       navComponent = getComponent();
        navComponent.setPreferredSize(getMySize());
        earthNavPanelWrapper = new JPanel(new BorderLayout());
        JPanel contents = GuiUtils.centerBottom(navComponent,
                              earthNavPanelWrapper);

        earthNavPanel = new EarthNavPanel(this);
        if (getShowEarthNavPanel()) {
            earthNavPanelWrapper.add(BorderLayout.CENTER, earthNavPanel);
        }
        return contents;
    }


    /**
     * Initialize the toolbars for the GUI
     */
    protected void initToolBars() {
        if (isDisplay3D()) {
            addToolBar(doMakeViewPointToolBar(JToolBar.VERTICAL),
                       "perspective", "Viewpoint toolbar");
        }
        super.initToolBars();
    }



    /**
     * Set the viewpoint info
     *
     * @param viewpointInfo the viewpoint info
     */
    public void setViewpointInfo(ViewpointInfo viewpointInfo) {
        getViewpointControl().setViewpointInfo(viewpointInfo);
    }






    /**
     * Dynamically initialize the view menu
     *
     * @param viewMenu the view menu
     */
    public void initializeViewMenu(JMenu viewMenu) {
        super.initializeViewMenu(viewMenu);
        if (isDisplay3D()) {
            viewMenu.add(getViewpointControl().getMenu());
        }
        viewMenu.add(makeColorMenu());
        viewMenu.addSeparator();

        if (isFullScreen()) {
            viewMenu.add(
                GuiUtils.setIcon(
                    GuiUtils.makeMenuItem(
                        "Reset Full Screen", this,
    /**
                        "resetFullScreen"), "/auxdata/ui/icons/arrow_in.png"));
        } else {
            viewMenu.add(
                GuiUtils.setIcon(
                    GuiUtils.makeMenuItem(
                        "Full Screen", this,
                        "setFullScreen"), "/auxdata/ui/icons/arrow_out.png"));
        }
        viewMenu.addSeparator();
        viewMenu.add(
            GuiUtils.setIcon(
                GuiUtils.makeMenuItem(
                    "Animation Timeline", this,
                    "showTimeline"), "/auxdata/ui/icons/timeline_marker.png"));

        viewMenu.add(GuiUtils.setIcon(GuiUtils.makeMenuItem("Flythrough",
                this, "showFlythrough"), "/auxdata/ui/icons/plane.png"));

        viewMenu.add(GuiUtils.setIcon(GuiUtils.makeMenuItem("Properties",
                this,
                "showPropertiesDialog"), "/auxdata/ui/icons/information.png"));
    }

    /**
     * Create and return the list of menus for the menu bar.
     * Just the map and view menu.
     *
     * @return List of menus.
     */
    public ArrayList doMakeMenuList() {
        ArrayList menus = super.doMakeMenuList();
        menus.add(makeViewMenu());
        menus.add(makeProjectionMenu());
        return menus;
    }


    /**
     * Create and return the show menu.
     *
     * @return The Show menu
     */
    protected JMenu makeShowMenu() {
        JMenu showMenu = super.makeShowMenu();
        if (globeBackgroundDisplayable != null) {
            createCBMI(showMenu, PREF_SHOWGLOBEBACKGROUND);
        }

        createCBMI(showMenu, PREF_SHOWSCALES);
        createCBMI(showMenu, PREF_ANIREADOUT);
        createCBMI(showMenu, PREF_SHOWPIP);
        createCBMI(showMenu, PREF_SHOWEARTHNAVPANEL);
        createCBMI(showMenu, PREF_LOGO_VISIBILITY);
        return showMenu;
    }



    /**
     * Center the display (animated) to the center of the given mapprojection
     *
     * @param mp  map projection
     *
     * @throws RemoteException  Java RMI problem
     * @throws VisADException   VisAD problem
     */
    public void center(MapProjection mp)
            throws RemoteException, VisADException {
        LatLonPoint center = mp.getCenterLatLon();
        getNavigatedDisplay().center(
            new EarthLocationTuple(
                center.getLatitude(), center.getLongitude(),
                new Real(RealType.Altitude, 0)), true);

    }


    /**
     * Set the projection to the first  projection  found in the displays
     */
    public void setProjectionFromFirstDisplay() {
        List controls = getControls();
        for (int i = 0; i < controls.size(); i++) {
            DisplayControl display = (DisplayControl) controls.get(i);
            MapProjection  mp      = display.getDataProjection();
            if (displayProjectionOk(mp)) {
                setMapProjection(
                    mp, true,
                    getDisplayConventions().getMapProjectionLabel(
                        mp, display));
                break;
            }
        }
    }


    /**
     * If we are using a ProjectionImpl then see if it hsa
     *
     * @throws RemoteException On badness
     * @throws VisADException On badness
     */
                }
    protected void updateProjection() throws RemoteException, VisADException {
        if ((mainProjection == null)
                || !(mainProjection instanceof ProjectionCoordinateSystem)) {
            return;
        }
        ProjectionImpl myProjection =
            (ProjectionImpl) ((ProjectionCoordinateSystem) mainProjection)
                .getProjection();
        ProjectionImpl newProjection =
            getIdv().getIdvProjectionManager().findProjectionByName(
                myProjection.getName());
        if ((newProjection != null) && !myProjection.equals(newProjection)) {
            double[] matrix = getDisplayMatrix();
            setProjection(newProjection);
            setDisplayMatrix(matrix);
        }
    }



    /**
     * Find and set the projection by name
     *
     * @param projName projection name
     */
    public void setProjectionByName(String projName) {
        List projections = getProjectionList();
        for (int i = 0; i < projections.size(); i++) {
            ProjectionImpl p = (ProjectionImpl) projections.get(i);
            if (p.getName().equals(projName)) {
                setProjection(p);
                return;
            }
        }

        for (int i = 0; i < projections.size(); i++) {
            ProjectionImpl p = (ProjectionImpl) projections.get(i);
            if (StringUtil.stringMatch(p.getName(), projName)) {
                setProjection(p);
                return;
            }
        }
        //        System.err.println("Could not find projection:" + projName);
    }



    /**
     * Set the current projection
     *
     * @param p The new projection.
     */
    public void setProjection(ProjectionImpl p) {
        p = (ProjectionImpl) p.constructCopy();
        try {
            setMapProjection(new ProjectionCoordinateSystem(p), true);
            if (pipPanel != null) {
                pipPanel.setProjectionImpl(p);
            }
        } catch (Exception excp) {
            logException("setMapProjection ()", excp);
        }
    }

    /**
     * Class ProjectionCommand manages changes to the projection
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.382 $
     */
    public static class ProjectionCommand extends Command {

        /** THe view manager I am in */
        MapViewManager viewManager;

        /** old state */
        String oldName;

        /** old state */
        String oldProjection;

        /** new state */
        String newName;

        /** new state */
        String newProjection;


        /**
         * ctor
         *
         * @param viewManager The vm
         * @param oldName old state
         * @param oldProjection old state
         * @param newName new state
         * @param newProjection new state
         */
        public ProjectionCommand(MapViewManager viewManager, String oldName,
                                 MapProjection oldProjection, String newName,
                                 MapProjection newProjection) {
            this.viewManager   = viewManager;
            this.oldName       = oldName;
            this.oldProjection = encode(oldProjection);
            this.newName       = newName;
            this.newProjection = encode(newProjection);
        }

        /**
         * Encode the map projection
         *
         * @param projection the map projection
         *
         * @return the encoded XML
         */
        private String encode(MapProjection projection) {
            try {
                return viewManager.getIdv().encodeObject(projection, false);
            } catch (Exception exc) {
                throw new RuntimeException(exc);
            }
        }

        /**
         * Decode a MapProjection XML spec
         *
         * @param xml a MapProjection XML spec
         *
         * @return  the decoded MapProjection
         */
        private MapProjection decode(String xml) {
            try {
                return (MapProjection) viewManager.getIdv().decodeObject(xml);
            } catch (Exception exc) {
                throw new RuntimeException(exc);
            }
        }


        /**
         * Redo
         */
        public void redoCommand() {
            viewManager.setMapProjection(decode(newProjection), true,
                                         newName, false, false);
        }

        /**
         * Undo
         */
        public void undoCommand() {
            viewManager.setMapProjection(decode(oldProjection), true,
                                         oldName, false, false);
        }
    }





    /**
     *  A wrapper aroung the setMapProjection call that takes a projection name.
     *  This passes in null.
     *
     * @param projection The projection
     * @param fromWidget Is it from the projection selection widget
     */
    public void setMapProjection(MapProjection projection,
                                 boolean fromWidget) {
        setMapProjection(projection, fromWidget, null);
    }


    /**
     * Get the current projection.Used for xml encoding/decoding.
     *
     * @return The current projection
     */
    public MapProjection getMainProjection() {
        return mainProjection;
    }

    /**
     * Set the current projection. Used for xml encoding/decoding.
     *
     * @param projection The new projection
     */
    public void setMainProjection(MapProjection projection) {
        mainProjection = projection;
    }


    /**
     * The main projection name.Used for xml encoding/decoding.
     *
     * @return Projection name
     */
    public String getMainProjectionName() {
        return mainProjectionName;
    }

    /**
     * The main projection name.Used for xml encoding/decoding.
     *
     * @param projectionName Projection name
     */
    public void setMainProjectionName(String projectionName) {
        mainProjectionName = projectionName;
    }

    /**
     * Set the DfltProjectionName property.
     *
     * @param value The new value for DfltProjectionName
     */
    public void setDefaultProjectionName(String value) {
        this.defaultProjectionName = value;
    }

    /**
     * Get the DfltProjectionName property.
     *
     * @return The DfltProjectionName
     */
    public String getDefaultProjectionName() {
        return this.defaultProjectionName;
    }





    /**
     * Add the projection and the given name into the history list. Add a menu item
     * into the history menu.
     *
     * @param projection The projection
     * @param name Its name
     */
    private void addProjectionToHistory(MapProjection projection,
                                        String name) {
        String encodedProjection = getIdv().encodeObject(projection, false);
        TwoFacedObject tfo = TwoFacedObject.findId(encodedProjection,
                                 projectionHistory);
        if (tfo != null) {
            projectionHistory.remove(tfo);
            projectionHistory.add(0, tfo);
            return;

        }
        tfo = new TwoFacedObject(name, encodedProjection);
        projectionHistory.add(0, tfo);


        String label = ((name != null)
                        ? name
                        : projection.toString());
    }


    /**
     * Set map projection in the main display.
     *
     * @param projection a Projection
     * @param fromWidget  true if this was from a widget (ie. widget or
     *                    menu item)
     * @param name        name to put in the history list (may be null)
     *
     * @return  true if successful
     */
    public boolean setMapProjection(MapProjection projection,
                                    boolean fromWidget, String name) {
        return setMapProjection(projection, fromWidget, name, false);
    }

    /**
     * Set map projection in the main display.
     *
     * @param projection a Projection
     * @param fromWidget  true if this was from a widget (ie. widget or
     *                    menu item)
     * @param name        name to put in the history list (may be null)
     * @param checkDefault  if true, check to see if we
     *                    should call getUseProjectionFromData()
     *
     * @return  true if successful
     */
    public boolean setMapProjection(MapProjection projection,
                                    boolean fromWidget, String name,
                                    boolean checkDefault) {
        return setMapProjection(projection, fromWidget, name, checkDefault,
                                true);
    }



    /**
     * Set map projection in the main display.
     *
     * @param projection a Projection
     * @param fromWidget  true if this was from a widget (ie. widget or
     *                    menu item)
     * @param name        name to put in the history list (may be null)
     * @param checkDefault  if true, check to see if we
     *                    should call getUseProjectionFromData()
     * @param addToCommandHistory Add this projection to the command history
     *
     * @return  true if successful
     */
    public boolean setMapProjection(MapProjection projection,
                                    boolean fromWidget, String name,
                                    boolean checkDefault,
                                    boolean addToCommandHistory) {
        return setMapProjection(projection, fromWidget, name, checkDefault,
                                addToCommandHistory, false);
     * Set map projection in the main display.
     *
     * @param projection a Projection
     * @param fromWidget  true if this was from a widget (ie. widget or
     *                    menu item)
     * @param name        name to put in the history list (may be null)
     * @param checkDefault  if true, check to see if we
     *                    should call getUseProjectionFromData()
     * @param addToCommandHistory Add this projection to the command history
     * @param maintainViewpoint  maintain the viewpoint
     *
     * @return  true if successful
     */
    public boolean setMapProjection(MapProjection projection,
                                    boolean fromWidget, String name,
                                    boolean checkDefault,
                                    boolean addToCommandHistory,
                                    boolean maintainViewpoint) {


        IdvUIManager.startTime = System.currentTimeMillis();

        if (checkDefault) {
            if ( !getUseProjectionFromData()) {
                return false;
            }
            if (doNotSetProjection) {
                return false;
            }
        }

        if (projection == null) {
            return false;
        }




        if (getUseGlobeDisplay() && !getViewpointControl().getAutoRotate()) {
            try {
                LatLonPoint center = projection.getCenterLatLon();
                getNavigatedDisplay().center(
                    new EarthLocationTuple(
                        center.getLatitude(), center.getLongitude(),
                        new Real(RealType.Altitude, 0)), true);
                return true;
            } catch (Exception e) {
                logException("setProjection", e);
                return false;
            }
        }




        boolean  actuallyChangedProjection = false;
        double[] matrix                    = getDisplayMatrix();
        try {

            setMasterInactive();
            if (addToCommandHistory && (mainProjection != null)) {
                addCommand(new ProjectionCommand(this, mainProjectionName,
                        mainProjection, name, projection));
            }

            if ( !Misc.equals(mainProjection, projection)) {
                if (name == null) {
                    name = getDisplayConventions().getMapProjectionName(
                        projection);
                }

                //If this is the first time we've put one in then save the current (default) proj.
                if ((projectionHistory.size() == 0)
                        && (mainProjection != null)) {
                    addProjectionToHistory(mainProjection, "Default");
                }
                mainProjectionName = name;
                addProjectionToHistory(projection, name);

                mainProjection = projection;
                if (fromWidget) {
                    doShare(SHARE_PROJECTION, projection);
                }
                try {
                    actuallyChangedProjection = true;
                    getMapDisplay().setMapProjection(mainProjection);
                    setAspectRatio(getMapDisplay().getDisplayAspect());


                    // override the aspect ratio
                    //if (getAspectRatio() != null) {
                    //getMapDisplay().setDisplayAspect(getAspectRatio());
                    //}
                    setDisplayMatrix(matrix);
                } catch (Exception e) {
                    logException("setProjection", e);
                notifyDisplayControls(SHARE_PROJECTION);
                checkPipPanel();
            }
            // if the projections are the same, reset to main view in case
            // they are zoomed/panned
            try {
                if ( !maintainViewpoint
                        && projection.equals(mainProjection)) {
                    getMaster().resetProjection();
                }
            } catch (Exception e) {
                logException("setProjection", e);
            }
        } finally {
            updateDisplayList();
            setMasterActive();
        }
        return actuallyChangedProjection;
    }

    /**
     * Check the pip panel. If non-null have it reset its box
     */
    public void checkPipPanel() {
        try {
            synchronized (PIP_MUTEX) {
                if (pipPanel == null) {
                    return;
                }
                pipPanel.resetDrawBounds();
            }
        } catch (Exception exc) {
            pipPanel = null;
            logException("Error setting pip panel", exc);
        }
    }



    /**
     * can this viewmanager import the given display control
     *
     * @param control the control
     *
     * @return ok to import
     */
    public boolean okToImportDisplay(DisplayControl control) {
        //Base class method checks for non-null and class equality
        if ( !super.okToImportDisplay(control)) {
            return false;
        }
        MapViewManager vm = (MapViewManager) control.getViewManager();
        return this.getUseGlobeDisplay() == vm.getUseGlobeDisplay();
    }


    /**
     * Received the first frame done event  from the display
     */
    protected void doneFirstFrame() {
        super.doneFirstFrame();
        checkPipPanel();
    }

    /**
     * Search through the list of display controls
     * looking for a {@link ucar.unidata.idv.control.MapDisplayControl}
     * that has been set to be the "default map"
     *
     * @return The default map display control
     */
    private MapDisplayControl findDefaultMap() {
        List controls = getControls();
        for (int i = controls.size() - 1; i >= 0; i--) {
            DisplayControl control = (DisplayControl) controls.get(i);
            if ( !(control instanceof MapDisplayControl)) {
                continue;
            }
            if (((MapDisplayControl) control).getIsDefaultMap()) {
                return (MapDisplayControl) control;
            }
        }
        return null;
    }


    /**
     * Apply the properties
     *
     * @return true if successful
     */
    public boolean applyProperties() {
        if ( !super.applyProperties()) {
            return false;
        }
        if (useGlobeDisplay && (globeBackgroundDisplayable != null)) {
            globeBackgroundColor = globeBackgroundColorComp.getBackground();
            globeBackgroundLevel = globeBackgroundLevelSlider.getValue();
            setGlobeBackground((GlobeDisplay) getMapDisplay());
            return true;
        }
        return applyAxisVisibility();
    }

    /**
     * Apply axis visibility choices.
     *
     * @return
     */
    private boolean applyAxisVisibility() {

        if (latLonScaleWidget == null) {
            return false;
        }

        boolean b = !useGlobeDisplay
                    && (getBp(PREF_SHOWSCALES)
                        || latLonScaleWidget.isLatVisible()
                        || latLonScaleWidget.isLonVisible());

        setBp(PREF_SHOWSCALES, b);

        return latLonScaleWidget.doApply();
    }



    /**
     * Add the properties components for this ViewManager
     *
     * @param tabbedPane  the tabbed pane to add to
     */
    protected void addPropertiesComponents(JTabbedPane tabbedPane) {
        super.addPropertiesComponents(tabbedPane);
        if ( !useGlobeDisplay) {
            MapProjectionDisplay mpDisplay =
                (MapProjectionDisplay) getNavigatedDisplay();
            mpDisplay.getLatScaleInfo().setVisible(getBp(PREF_SHOWSCALES));
            mpDisplay.getLonScaleInfo().setVisible(getBp(PREF_SHOWSCALES));
            latLonScaleWidget = new LatLonScalePanel(mpDisplay);
            tabbedPane.add("Horizontal Scale",
                           GuiUtils.topLeft(latLonScaleWidget));
        }
        if (globeBackgroundDisplayable == null) {
            return;
        }


        globeBackgroundLevelSlider = new ZSlider(globeBackgroundLevel);
        JComponent levelComp = globeBackgroundLevelSlider.getContents();
        JComponent[] bgComps =
            GuiUtils.makeColorSwatchWidget(getGlobeBackgroundColorToUse(),
                                           "Globe Background Color");


        globeBackgroundColorComp = bgComps[0];
        JComponent comp = GuiUtils.formLayout(new Component[] {
                              GuiUtils.rLabel("Color:"),
                              GuiUtils.left(bgComps[0]),
                              GuiUtils.rLabel("Level:"),
                              levelComp });
        tabbedPane.add("Globe Background", GuiUtils.top(comp));
    }


    /**
     * Set the globe background
     *
     * @param globe  the globe display
     */
    private void setGlobeBackground(GlobeDisplay globe) {
        try {
            if (globeBackgroundDisplayable == null) {
                FlatField ff = ucar.visad.Util.makeField(-180, 180, 180, 90,
                                   -90, 180, 1, "celsius");
                Data d = ff;
                globeBackgroundDisplayable = new LineDrawing("background");
                globeBackgroundDisplayable.setData(d);
                globe.addDisplayable(globeBackgroundDisplayable);
            }

            //            Color c = new Color(globeBackgroundColor.getRed(),
            //                                globeBackgroundColor.getGreen(),
            //                                globeBackgroundColor.getBlue(),
            //                                (int)(255*0.5));

            globeBackgroundDisplayable.setColor(
                getGlobeBackgroundColorToUse());
            globeBackgroundDisplayable.setVisible(getGlobeBackgroundShow());

            DisplayRealType drt          = globe.getDisplayAltitudeType();
            double[]        range        = new double[2];
            double          realPosition = 1;
            if (drt.getRange(range)) {
                double pcnt = (globeBackgroundLevel - (-1)) / 2;
                realPosition = Math.min((range[0]
                                         + (range[1] - range[0])
                                           * pcnt), range[1]);
            }

            ConstantMap constantMap = new ConstantMap(realPosition, drt);
            globeBackgroundDisplayable.addConstantMap(constantMap);
        } catch (Exception exc) {
            throw new RuntimeException(exc);

        }
    }


    /**
     * Destroy this ViewManager
     */
    public void destroy() {
        if (flythrough != null) {
            try {
                flythrough.destroy();
            } catch (Exception ignore) {}
            flythrough = null;
        }
        super.destroy();
    }



    /**
     * Show the fly through
     */
    public void showFlythrough() {
        if (flythrough == null) {
            flythrough = new Flythrough(this);
        }
        flythrough.show();
    }


    /**
     * Do a flythrough
     *
     * @param pts  the flythrough points
     */
    public void flythrough(final float[][] pts) {
        if (flythrough == null) {
            flythrough = new Flythrough(this);
        }
        flythrough.flythrough(pts);
    }


    /**
     * Flythrough the points
     *
     * @param pts  the List of points
     */
    public void flythrough(List pts) {
        if (flythrough == null) {
            flythrough = new Flythrough(this);
        }
        flythrough.flythrough(pts);
    }

    /**
     * Initialize after unpersistence
     *
     * @param idv  the IDV
     *
     * @throws RemoteException Java RMI exception
     * @throws VisADException  VisAD problem
     */
    public final void initAfterUnPersistence(IntegratedDataViewer idv)
            throws VisADException, RemoteException {
        super.initAfterUnPersistence(idv);
    }


    /**
     * Handle a change to the data in a DisplayControl
     *
     * @param display  the DisplayControl
     */
    public void displayDataChanged(DisplayControl display) {
        displayDataChanged(display, false);
    }


    /**
     * Handle a change to the data in a DisplayControl
     *
     * @param display  the DisplayControl
     * @param fromInitialLoad  if this is from the initial load
     */
    public void displayDataChanged(DisplayControl display,
                                   boolean fromInitialLoad) {
        try {
            if (getUseProjectionFromData()
                    && !getStateManager().getProperty(
                        IdvConstants.PROP_LOADINGXML, false)) {
                MapProjection mp = display.getDataProjection();

                if (getUseGlobeDisplay()
                        && !getViewpointControl().getAutoRotate()) {
                    LatLonPoint center = mp.getCenterLatLon();
                    getNavigatedDisplay().center(
                        new EarthLocationTuple(
                            center.getLatitude(), center.getLongitude(),
                            new Real(RealType.Altitude, 0)), true);
                    return;
                }


                if (displayProjectionOk(mp)) {
                    if ((mainProjection == null)
                            || !mp.equals(mainProjection)) {
                        boolean maintainViewpoint = !fromInitialLoad;
                        setMapProjection(
                            mp, true,
                            getDisplayConventions().getMapProjectionLabel(
                                mp, display), true, true, maintainViewpoint);
                        if (displayProjectionZoom != 0) {
                            getMapDisplay().zoom(displayProjectionZoom);
                        }
                    }
                }
            }
        } catch (Exception exp) {
            // ignore, don't set anything.   Uncomment for debugging
            // LogUtil.logException ( "addDisplayInfo:setMapProjection()", exp);
        }
        super.displayDataChanged(display);
    }


    /**
     * Reset projection of display based control's getDataProjection().
     * called by DisplayInfo.addDisplayable (), usually from control's init.
     *
     * @param displayInfos The List of new display infos to add
     *
     * @throws RemoteException  Java RMI Exception
     * @throws VisADException   Problem creating VisAD object
     */
    public void addDisplayInfos(List displayInfos)
            throws RemoteException, VisADException {

        if (getIsDestroyed()) {
            return;
        }

        if (displayInfos.size() == 0) {
            return;
        }

        //Check if we are adding a default map.
        DisplayControl display = displayInfos.get(0).getDisplayControl();
        if ((display instanceof MapDisplayControl)
                && ((MapDisplayControl) display).getIsDefaultMap()) {
            MapDisplayControl defaultMap = findDefaultMap();
            if ((defaultMap != null) && (defaultMap != display)) {
                defaultMap.loadNewMap((MapDisplayControl) display);
                //This rebuilds the legends, etc.
                displayControlChanged(defaultMap);
                return;
            }
        }

        displayDataChanged(display, true);


        super.addDisplayInfos(displayInfos);

    }

    /**
     * Reset projection of display based on data.
     * @deprecated  no substitute.  Use setMapProjection()
     *
     * @param data The data form the display
     * @param display The display
     */
    public void checkProjection(FieldImpl data, DisplayControl display) {
        try {
            MapProjection mp = GridUtil.getNavigation(data);
            if (mp == null) {
                return;
            }
            setMapProjection(
                mp, false,
                getDisplayConventions().getMapProjectionLabel(mp, display));
        } catch (Exception exp) {}  // do nothing - no projection in data
    }





    /**
     * Required interface for ActionEvents, to implement ActionListener
     * for the UI objects such as JButton-s and MenuItem-s
     *
     * @param event an ActionEvent
     */
    public void actionPerformed(ActionEvent event) {
        String cmd = event.getActionCommand();
        if (cmd.equals(CMD_FLY_FORWARD) && (flythrough != null)) {
            flythrough.driveForward();
        } else if (cmd.equals(CMD_FLY_BACK) && (flythrough != null)) {
            flythrough.driveBack();
        } else if (cmd.equals(CMD_FLY_LEFT) && (flythrough != null)) {
            flythrough.driveLeft();
     *
        } else if (cmd.equals(CMD_FLY_RIGHT) && (flythrough != null)) {
            flythrough.driveRight();
        } else if (cmd.equals(CMD_NAV_ZOOMIN)) {
            getMapDisplay().zoom(ZOOM_FACTOR);
        } else if (cmd.equals(CMD_NAV_ROTATELEFT)) {
            getMapDisplay().rotateZ(-5.0);
        } else if (cmd.equals(CMD_NAV_ROTATERIGHT)) {
            getMapDisplay().rotateZ(5.0);
        } else if (cmd.equals(CMD_NAV_ZOOMOUT)) {
            getMapDisplay().zoom(1.0 / (double) ZOOM_FACTOR);
        } else if (cmd.equals(CMD_NAV_HOME)) {
            try {
                getMapDisplay().resetProjection();
            } catch (Exception exc) {}
        } else if (cmd.equals(CMD_NAV_RIGHT)) {
            getMapDisplay().translate(-TRANSLATE_FACTOR, 0.0);
        } else if (cmd.equals(CMD_NAV_LEFT)) {
            getMapDisplay().translate(TRANSLATE_FACTOR, 0.0);
        } else if (cmd.equals(CMD_NAV_UP)) {
            getMapDisplay().translate(0.0, -TRANSLATE_FACTOR);
        } else if (cmd.equals(CMD_NAV_DOWN)) {
            getMapDisplay().translate(0.0, TRANSLATE_FACTOR);
        } else if (cmd.equals(CMD_NAV_SMALLZOOMIN)) {
            getMapDisplay().zoom(ZOOM_FACTOR);
        } else if (cmd.equals(CMD_NAV_SMALLZOOMOUT)) {
            getMapDisplay().zoom(1.0 / ZOOM_FACTOR);
        } else if (cmd.equals(CMD_NAV_SMALLROTATELEFT)) {
            getMapDisplay().rotateZ(-2.0);
        } else if (cmd.equals(CMD_NAV_SMALLROTATERIGHT)) {
            getMapDisplay().rotateZ(2.0);
        } else if (cmd.equals(CMD_NAV_SMALLTILTUP)) {
            getMapDisplay().rotateX(-2.0);
        } else if (cmd.equals(CMD_NAV_SMALLTILTDOWN)) {
            getMapDisplay().rotateX(2.0);
        } else if (cmd.equals(CMD_NAV_SMALLRIGHT)) {
            getMapDisplay().translate(-0.02, 0.0);
        } else if (cmd.equals(CMD_NAV_SMALLLEFT)) {
            getMapDisplay().translate(0.02, 0.0);
        } else if (cmd.equals(CMD_NAV_SMALLUP)) {
            getMapDisplay().translate(0.0, -0.02);
        } else if (cmd.equals(CMD_NAV_SMALLDOWN)) {
            getMapDisplay().translate(0.0, 0.02);
        }

    }


    /**
     * Get the bounds that are visible
     *
     * @return bounds
     */
    public GeoLocationInfo getVisibleGeoBounds() {
        NavigatedDisplay navDisplay = getMapDisplay();
        Rectangle        screenBounds;

        screenBounds = navDisplay.getDisplayComponent().getBounds();
        if ( !getIdv().getArgsManager().getIsOffScreen()) {
            //            screenBounds = navDisplay.getComponent().getBounds();
        } else {
            //            Dimension 
        }
        double[] ulXY = getMapDisplay().getSpatialCoordinatesFromScreen(0, 0);
        double[] lrXY = getMapDisplay().getSpatialCoordinatesFromScreen(
                            screenBounds.width, screenBounds.height);

        LatLonPoint ulLLP =
            getMapDisplay().getEarthLocation(ulXY).getLatLonPoint();
        LatLonPoint lrLLP =
            getMapDisplay().getEarthLocation(lrXY).getLatLonPoint();



        double minX = Math.min(ulLLP.getLongitude().getValue(),
                               lrLLP.getLongitude().getValue());
        double maxX = Math.max(ulLLP.getLongitude().getValue(),
                               lrLLP.getLongitude().getValue());
        double minY = Math.min(ulLLP.getLatitude().getValue(),
                               lrLLP.getLatitude().getValue());
        double maxY = Math.max(ulLLP.getLatitude().getValue(),
                               lrLLP.getLatitude().getValue());
        Rectangle2D.Float rect = new Rectangle2D.Float((float) minX,
                                     (float) minY, (float) (maxX - minX),
                                     (float) (maxY - minY));

        return new GeoLocationInfo(maxY, minX, minY, maxX);
    }

    /**

     * Set the current viewpoint as the projection
     */
    public void setCurrentAsProjection() {
        try {
            NavigatedDisplay display      = getMapDisplay();
            Rectangle        screenBounds =
                display.getComponent().getBounds();
            LatLonPoint      ulLLP        = null;
            LatLonPoint      lrLLP        = null;

            int              sw           = screenBounds.width;
            int              sh           = screenBounds.height;
            int              x            = 0;
            int              y            = 0;

            while ((x < sw) && (y < sh)) {
                double[] ulXY = display.getSpatialCoordinatesFromScreen(x, y);
                ulLLP =
                    getMapDisplay().getEarthLocation(ulXY).getLatLonPoint();
                if ( !ulLLP.getLatitude().isMissing()
                        && !ulLLP.getLongitude().isMissing()) {
                    break;
                }
                ulLLP = null;
                x++;
                y++;
            }

            while ((sw > 0) && (sh > 0)) {
                double[] lrXY = display.getSpatialCoordinatesFromScreen(sw,
                                    sh);
                lrLLP =
                    getMapDisplay().getEarthLocation(lrXY).getLatLonPoint();
                if ( !lrLLP.getLatitude().isMissing()
                        && !lrLLP.getLongitude().isMissing()) {
                    break;
                }
                lrLLP = null;
                sw--;
                sh--;
            }

            if ((ulLLP == null) || (lrLLP == null)) {
                LogUtil.userMessage("Could not create a valid projection");
                return;
            }


            double minX = Math.min(ulLLP.getLongitude().getValue(),
                                   lrLLP.getLongitude().getValue());
            double maxX = Math.max(ulLLP.getLongitude().getValue(),
                                   lrLLP.getLongitude().getValue());
            double minY = Math.min(ulLLP.getLatitude().getValue(),
                                   lrLLP.getLatitude().getValue());
            double maxY = Math.max(ulLLP.getLatitude().getValue(),
                                   lrLLP.getLatitude().getValue());
            Rectangle2D.Float rect = new Rectangle2D.Float((float) minX,
                                         (float) minY, (float) (maxX - minX),
                                         (float) (maxY - minY));

            MapProjection mp = ucar.visad.Util.makeMapProjection(minY, minX,
                                   maxY, maxX);
            setMapProjection(mp, true);
            //            getMapDisplay().zoom(ZOOM_FACTOR);
            getMapDisplay().saveProjection();
        } catch (Exception exp) {
            logException("Setting projection", exp);
        }
    }


    /**
     * Show the projection manager.
     */
    public void showProjectionManager() {
        getIdv().showIdvProjectionManager();
    }




    /**
     * Set or reset map area of view, using NavigatedDisplay method.
     *
     * @param mapArea ProjectionRect the map area of view
     */
    public void setMapArea(ProjectionRect mapArea) {
        try {
            getMapDisplay().setMapArea(mapArea);
        } catch (Exception e) {
            logException("setMapArea", e);
        }
    }

    /**
     * Check if the display projection is okay
     *
     * @param mp  map projection to check
     * @return true if okay
     */
    private boolean displayProjectionOk(MapProjection mp) {
        if (mp == null) {
            return false;
        }
        if (doNotSetProjection) {
            return false;
        }

        Rectangle2D rect = mp.getDefaultMapArea();
        if ((rect.getWidth() == 0) || (rect.getHeight() == 0)) {
            return false;
        } else {
            if (rect.getWidth() / rect.getHeight() < 0.1) {
                return false;
            } else if (rect.getHeight() / rect.getWidth() < 0.1) {
                return false;
            }
        }
        return true;
    }



    /**
     * Init menu
     *
     * @param projectionsMenu menu
     */
    public void initializeProjectionMenu(JMenu projectionsMenu) {
        List projections = getProjectionList();
        List controls    = getControls();
        if ( !getUseGlobeDisplay()) {
            //            projectionsMenu.add(GuiUtils.makeMenuItem("Use Displayed Area",
            //                    this, "setCurrentAsProjection"));
        }
        ProjectionImpl currentProjection = null;
        if ((mainProjection != null)
                && (mainProjection instanceof ProjectionCoordinateSystem)) {
            currentProjection =
                ((ProjectionCoordinateSystem) mainProjection).getProjection();
        }


        makeProjectionsMenu(projectionsMenu, projections, this,
                            "setProjection", currentProjection);
    }



    /**
     * Init menu
     *
     * @param displaysMenu menu
     */
    public void initializeDisplaysProjectionMenu(JMenu displaysMenu) {
        List controls = getControls();
        int  cnt      = 0;
        for (int i = 0; i < controls.size(); i++) {
            final DisplayControl control = (DisplayControl) controls.get(i);
            final MapProjection  mp      = control.getDataProjection();
            if ( !displayProjectionOk(mp)) {
                continue;
            }

            final String label =
                getDisplayConventions().getMapProjectionLabel(mp, control);
            JMenuItem mi = new JMenuItem(label);
            displaysMenu.add(mi);
            mi.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent ae) {
                    setMapProjection(mp, false, label);
                }
            });
            cnt++;
        }
        if (cnt == 0) {
            displaysMenu.add(new JMenuItem("None Defined"));
        }
    }




    /**
     * Make the projections menu. Call the method on the given object.
     *
     * @param projectionsMenu Menu to add to
     * @param projections List of projections
     * @param object object to call
     * @param method method to call
     */
    public static void makeProjectionsMenu(JMenu projectionsMenu,
                                           List projections, Object object,
                                           String method) {
        makeProjectionsMenu(projectionsMenu, projections, object, method,
                            null);
    }


    /**
     * Make the projections menu
     *
     * @param projectionsMenu the menu to add to
     * @param projections list of projections
     * @param object object to call
     * @param method  method to call
     * @param currentProjection current projection
     */
    public static void makeProjectionsMenu(JMenu projectionsMenu,
                                           List projections, Object object,
                                           String method,
                                           ProjectionImpl currentProjection) {
        if (currentProjection != null) {
            List names = StringUtil.split(currentProjection.getName(), ">",
                                          true, true);
            if (names.size() > 0) {
                String name = "Current: "
                              + (String) names.get(names.size() - 1);
                JMenuItem mi = GuiUtils.makeMenuItem(name, object, method,
                                   currentProjection);

                projectionsMenu.add(mi);
                projectionsMenu.addSeparator();
            }
        }

        Hashtable catMenus = new Hashtable();
        for (int i = 0; i < projections.size(); i++) {
            ProjectionImpl p = (ProjectionImpl) projections.get(i);
            List names = StringUtil.split(p.getName(), ">", true,
                                     true);
            JMenu  theMenu  = projectionsMenu;
            String catSoFar = "";
            int    catIdx   = 0;
     *
            for (catIdx = 0; catIdx < names.size() - 1; catIdx++) {
                String cat = (String) names.get(catIdx);
                catSoFar += "-" + cat;
                JMenu tmpMenu = (JMenu) catMenus.get(catSoFar);
                if (tmpMenu == null) {
                    tmpMenu = new JMenu(cat);
                    catMenus.put(catSoFar, tmpMenu);
                    theMenu.add(tmpMenu);
                }
                theMenu = tmpMenu;
            }
            String  name      = ((catIdx < names.size())
                                 ? names.get(catIdx)
                                 : "");
            boolean isCurrent = Misc.equals(p, currentProjection);
            if (isCurrent) {
                //              name = "> " + name;
            }
            JMenuItem mi = GuiUtils.makeMenuItem(name, object, method, p);
            theMenu.add(mi);
            if (isCurrent) {
                GuiUtils.italicizeFont(mi.getComponent());
            }
        }


    }




    /**
     * Init menu
     *
     * @param menu menu
     */
    public void initializeProjectionHistoryMenu(JMenu menu) {
        menu.removeAll();
        for (int i = 0; i < projectionHistory.size(); i++) {
            final TwoFacedObject tfo =
                (TwoFacedObject) projectionHistory.get(i);
            JMenuItem mi = new JMenuItem(tfo.toString());
            mi.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    try {
                        String encodedProjection = (String) tfo.getId();
                        MapProjection mp =
                            (MapProjection) getIdv().decodeObject(
                                encodedProjection);
                        setMapProjection(mp, true);
                    } catch (Exception exc) {
                        logException("Failed to instantiate the projection",
                                     exc);
                    }
                }
            });
            menu.add(mi);
        }
    }


    /**
     * Is this a compatible ViewManager
     *
     * @param vm  the other
     *
     * @return  true if compatible
     */
    public boolean isCompatibleWith(ViewManager vm) {
        if ( !super.isCompatibleWith(vm)) {
            return false;
        }
        MapViewManager that = (MapViewManager) vm;
        return this.getUseGlobeDisplay() == that.getUseGlobeDisplay();
    }


    /**
     * Is this compatible with the ViewState
     *
     * @param viewState  the view state
     *
     * @return  true if compatible
     */
    public boolean isCompatibleWith(ViewState viewState) {
        if ( !super.isCompatibleWith(viewState)) {
            return false;
        }
        Boolean b = (Boolean) viewState.get(ViewState.PROP_GLOBE);
        if (b != null) {
            return getUseGlobeDisplay() == b.booleanValue();
        }
        return true;
    }


    /**
     * Make the "Projections" menu to be added to the menu bar, which provides
     * controls for maps.
     * @return JMenu
     */
    private JMenu makeProjectionMenu() {
        JMenu projMenu = new JMenu("Projections");
        projMenu.setMnemonic(GuiUtils.charToKeyCode("P"));
        projectionsMenu = GuiUtils.makeDynamicMenu("Predefined", this,
                "initializeProjectionMenu");
        JMenu displaysMenu = GuiUtils.makeDynamicMenu("From Displays", this,
                                 "initializeDisplaysProjectionMenu");

        if ( !getUseGlobeDisplay()) {
            projMenu.add(projectionsMenu);
            projMenu.add(displaysMenu);
        }
        projMenu.add(makeSavedViewsMenu());
        JMenu projectionHistoryMenu = GuiUtils.makeDynamicMenu("History",
                                          this,
                                          "initializeProjectionHistoryMenu");

        if ( !getUseGlobeDisplay()) {
            projMenu.add(projectionHistoryMenu);
        }


        if ( !getUseGlobeDisplay()) {
            projMenu.addSeparator();
            projMenu.add(GuiUtils.setIcon(GuiUtils.makeMenuItem("New/Edit...",
                    this,
                    "showProjectionManager"), "/auxdata/ui/icons/world_edit.png"));
            projMenu.add(GuiUtils.setIcon(GuiUtils.makeMenuItem("Use Displayed Area",
                    this,
                    "setCurrentAsProjection"), "/auxdata/ui/icons/world_rect.png"));
        }
        projMenu.add(GuiUtils.setIcon(GuiUtils.makeMenuItem("Go to Address",
                this, "goToAddress"), "/auxdata/ui/icons/house_go.png"));


        projMenu.addSeparator();
        if ( !getUseGlobeDisplay()) {
            createCBMI(projMenu, PREF_PROJ_USEFROMDATA).setToolTipText(
                "Automatically change the projection to the native data projection of new displays");
        } else {
            createCBMI(projMenu, PREF_PROJ_USEFROMDATA).setToolTipText(
                "Automatically change viewpoint to the native data projection of new displays");
        }
        createCBMI(projMenu, PREF_USE_PROGRESSIVE_RESOLUTION).setToolTipText("" +
        		"Progressively disclose higher resolution data on zoom");
        createCBMI(projMenu, PREF_SHAREVIEWS);
        projMenu.add(GuiUtils.makeMenuItem("Set Share Group", this,
                                           "showSharableDialog"));
        return projMenu;
    }


    /**
     * Have this so we don't get warnings on unpersisting old bundles
     *
     * @param location The location
     */
    public void setMapConfigFile(String location) {}


    /**
     * Use globe display. Used for xml encoding/decoding.
     *
     * @param use The globe display value
     */
    public void setUseGlobeDisplay(boolean use) {
        useGlobeDisplay = use;
    }

    /**
     * Get the globe display flag. Used for xml encoding/decoding.
     *
     * @return The globe display value
     */
    public boolean getUseGlobeDisplay() {
        return useGlobeDisplay;
    }


    /**
     * Use a 3D display. Used for xml encoding/decoding.
     *
     * @param use The use 3D display value
     */
    public void setUse3D(boolean use) {
        use3D = use;
    }

    /**
     * Get the use 3D display flag. Used for xml encoding/decoding.
     *
     * @return The use 3D value
     */
    public boolean getUse3D() {
        return use3D;
    }



    /**
     * The BooleanProperty identified byt he given id has changed.
     * Apply the change to the display.
     * @param id Id of the changed BooleanProperty
     * @param value Its new value
     *
     * @throws Exception problem handeling the change
     */
    protected void handleBooleanPropertyChange(String id, boolean value)
            throws Exception {
        super.handleBooleanPropertyChange(id, value);
        if (id.equals(PREF_AUTOROTATE)) {
            if (hasViewpointControl()) {
                getViewpointControl().setAutoRotate(value);
            }
        } else if (id.equals(PREF_SHOWSCALES)) {
            if (hasDisplayMaster()) {
                getNavigatedDisplay().setScalesVisible(value);
            }
        } else if (id.equals(PREF_SHOWEARTHNAVPANEL)) {
            if (earthNavPanelWrapper != null) {
                earthNavPanelWrapper.removeAll();
                if (value) {
                    earthNavPanelWrapper.add(BorderLayout.CENTER,
                                             earthNavPanel);
                }
            }
        } else if (id.equals(PREF_SHOWPIP)) {
            if (pipPanelWrapper != null) {
                pipPanelWrapper.setVisible(value);
            }
        } else if (id.equals(PREF_USE_PROGRESSIVE_RESOLUTION)) {
        	// TODO: what do we want to do?
        } else if (id.equals(PREF_SHOWGLOBEBACKGROUND)) {
            if (globeBackgroundDisplayable != null) {
                globeBackgroundDisplayable.setVisible(value);
            }
        } else if (id.equals(PREF_PERSPECTIVEVIEW)) {
            if (hasViewpointControl()) {
                getViewpointControl().setPerspectiveView(value);
            }
        }
    }

    /**
     * Apply preferences
     */
    public void applyPreferences() {
        super.applyPreferences();
        applyAxisVisibility();
    }

    /**
     * Create the set of {@link ucar.unidata.util.BooleanProperty}s.
     * These hold all of the different flag based display state.
     *
     * @param props the list of properties
     */
    protected void getInitialBooleanProperties(List props) {
        super.getInitialBooleanProperties(props);
        props.add(new BooleanProperty(PREF_SHOWSCALES, "Show Display Scales",
                                      "Show Display Scales", false));

        props.add(
            new BooleanProperty(
                PREF_PROJ_USEFROMDATA, "Auto-set Projection",
                "Use projection from newly loaded data", true));
        props.add(new BooleanProperty(PREF_PERSPECTIVEVIEW,
                                      "Perspective View",
                                      "Toggle perspective view", false));
        props.add(new BooleanProperty(PREF_AUTOROTATE, "Auto-rotate", "",
                                      false));
        props.add(new BooleanProperty(PREF_SHOWEARTHNAVPANEL,
                                      "Show Earth Navigation Panel",
                                      "Show Earth Navigation Panel", false));

        props.add(new BooleanProperty(PREF_SHOWPIP, "Show Overview Map",
                                      "Show Overview Map", false));

        props.add(new BooleanProperty(PREF_USE_PROGRESSIVE_RESOLUTION, 
        		                      PR_LABEL,
                                      PR_LABEL, true));

        if (useGlobeDisplay) {
            props.add(new BooleanProperty(PREF_SHOWGLOBEBACKGROUND,
                                          "Show Globe Background",
                                          "Show Globe Background",
                                          defaultGlobeBackground));
        }
    }



    /**
     * Set the autorotate property
     *
     * @param value The value
     */
    public void setAutoRotate(boolean value) {
        setBp(PREF_AUTOROTATE, value);
    }

    /**
     * Get  the autorotate flag
     * @return The flag value
     */
    public boolean getAutoRotate() {
        return getBp(PREF_AUTOROTATE);
    }

    /**
     * Set the  perspective view flag
     *
     * @param value The value
     */
    public void setPerspectiveView(boolean value) {
        setBp(PREF_PERSPECTIVEVIEW, value);
    }

    /**
     * Get  the perspective view  flag
     * @return The flag value
     */
    public boolean getPerspectiveView() {
        return getBp(PREF_PERSPECTIVEVIEW);
    }


    /**
     * Dummy for old bundles
     *
     * @param value The value
     */
    public void setShowMap(boolean value) {}


    /**
     * Dummy for old bundles
     *
     * @param value The value
     */
    public void setShowElevation(boolean value) {}

    /**
     * _more_
     *
     * @param value _more_
     */
    public void setProjectionFromData(boolean value) {
        setUseProjectionFromData(value);
    }

    /**
     * Set the  use projection from data flag
     *
     * @param value The value
     */
    public void setUseProjectionFromData(boolean value) {
        setBp(PREF_PROJ_USEFROMDATA, value);
    }

    /**
     * Get  the use projection from data  flag
     * @return The flag value
     */
    public boolean getUseProjectionFromData() {
        //   if ( !isInteractive()) {
        //       return true;
        //   }
        return getBp(PREF_PROJ_USEFROMDATA);
    }


    /**
     * Set the background color property.
     * @deprecated  Keep this around for old bundles
     * @param bgColor The value
     */
    public void setBgColor(boolean bgColor) {
        if ( !bgColor) {
            setBackground(Color.white);
            setForeground(Color.black);
        } else {
            setBackground(Color.black);
            setForeground(Color.white);
        }
    }


    /**
     * Set the ShowEarthNavPanel property.
     *
     * @param value The new value for ShowEarthNavPanel
     */
    public void setShowEarthNavPanel(boolean value) {
        setBp(PREF_SHOWEARTHNAVPANEL, value);
    }

    /**
     * Get the ShowEarthNavPanel property.
     *
     * @return The ShowEarthNavPanel
     */
    public boolean getShowEarthNavPanel() {
        return getBp(PREF_SHOWEARTHNAVPANEL);
    }


    /**
     * Hide the pip panel
     */
    public void hidePip() {
        setShowPip(false);
    }

    /**
     * Set the ShowPipPanel property.
     * @param value The new value for ShowPipPanel
     */
    public void setShowPip(boolean value) {
        setBp(PREF_SHOWPIP, value);
    }

    /**
     * Get the ShowPipPanel property.
     *
     * @return The ShowPipPanel
     */
    public boolean getShowPip() {
        return getBp(PREF_SHOWPIP, false);
    }

    /**
     * Set the Progressive Resolution property.
     *
     * @param value The new value for Progressive Resolution
     */
    public void setUseProgressiveResolution(boolean value) {
        setBp(PREF_USE_PROGRESSIVE_RESOLUTION, value);
    }

    /**
     * Get the ProgressiveResolution property.
     *
     * @return The ProgressiveResolution
     */
    public boolean getUseProgressiveResolution() {
        return getBp(PREF_USE_PROGRESSIVE_RESOLUTION, true);
    }


    /**
     * Set the InitialMapResources property. This gets set
     * by the viewmanager properties and is a comma separated
     * list of map resource paths.
     *
     * @param value The new value for InitialMapResources
     */
    public void setInitialMapResources(String value) {
        initialMapResources = value;
    }



    /**
     * What type of view is this
     *
     * @return The type of view
     */
    public String getTypeName() {
        if (getUseGlobeDisplay()) {
            return "Globe";
        }
        return "Map";
    }



    /**
     * Get the default map position from a property
     *
     * @return the value in the range of -1 to 1
     */
    public float getDefaultMapPosition() {
        if (getUseGlobeDisplay()) {
            return (float) getStateManager().getProperty(
                IdvConstants.PROP_MAP_GLOBE_LEVEL, 0.005f);
        } else {
            return (float) getStateManager().getProperty(
                IdvConstants.PROP_MAP_MAP_LEVEL, -0.99f);
        }
    }

    /**
     *  Set the GlobeBackgroundColor property.
     *
     *  @param value The new value for GlobeBackgroundColor
     */
    public void setGlobeBackgroundColor(Color value) {
        globeBackgroundColor = value;
    }


    /**
     *  Get the GlobeBackgroundColor property.
     *
     *  @return The GlobeBackgroundColor
     */
    public Color getGlobeBackgroundColor() {
        return globeBackgroundColor;
    }

    /**
     *  Get the GlobeBackgroundColor property to be used. If it has not been set then get the preference
     *
     *  @return The GlobeBackgroundColor to use
     */
    public Color getGlobeBackgroundColorToUse() {
        Color backgroundColor = globeBackgroundColor;
        if (backgroundColor == null) {
            backgroundColor = getStore().get(PREF_GLOBEBACKGROUND,
                                             Color.white);
        }
        return backgroundColor;
    }


    /**
     *  Set the GlobeBackgroundShow property.
     *
     *  @param value The new value for GlobeBackgroundShow
     */
    public void setGlobeBackgroundShow(boolean value) {
        defaultGlobeBackground = value;
        setBp(PREF_SHOWGLOBEBACKGROUND, value);
    }

    /**
     *  Get the GlobeBackgroundShow property.
     *
     *  @return The GlobeBackgroundShow
     */
    public boolean getGlobeBackgroundShow() {
        if (hasBooleanProperty(PREF_SHOWGLOBEBACKGROUND)) {
            return getBp(PREF_SHOWGLOBEBACKGROUND, false);
        }
        XmlObjectStore store = getStore();
        if (store != null) {
            return store.get(PREF_SHOWGLOBEBACKGROUND, false);
        }
        return false;
    }

    /**
     *  Set the GlobeBackgroundLevel property.
     *
     *  @param value The new value for GlobeBackgroundLevel
     */
    public void setGlobeBackgroundLevel(double value) {
        globeBackgroundLevel = value;
    }

    /**
     *  Get the GlobeBackgroundLevel property.
     *
     *  @return The GlobeBackgroundLevel
     */
    public double getGlobeBackgroundLevel() {
        return globeBackgroundLevel;
    }

    /**
     * Set the Flythrough property.
     *
     * @param value The new value for Flythrough
     */
    public void setFlythrough(Flythrough value) {
        this.flythrough = value;
    }

    /**
     * Get the Flythrough property.
     *
     * @return The Flythrough
     */
    public Flythrough getFlythrough() {
        return this.flythrough;
    }


    /**
     *  Set the ShowMaps property.
     *
     *  @param value The new value for ShowMaps
     */
    public void setShowMaps(boolean value) {
        this.showMaps = value;
    }

    /**
     *  Get the ShowMaps property.
     *
     *  @return The ShowMaps
     */
    public boolean getShowMaps() {
        return this.showMaps;
    }





    /**
     *  Set the DisplayProjectionZoom property.
     *
     *  @param value The new value for DisplayProjectionZoom
     */
    public void setDisplayProjectionZoom(double value) {
        this.displayProjectionZoom = value;
    }

    /**
     *  Get the DisplayProjectionZoom property.
     *
     *  @return The DisplayProjectionZoom
     */
    public double getDisplayProjectionZoom() {
        return this.displayProjectionZoom;
    }

    /**
     *  Set the InitMapPaths property.
     *
     *  @param value The new value for InitMapPaths
     */
    public void setInitMapPaths(String value) {
        this.initMapPaths = value;
    }


    /**
     *  Get the InitMapPaths property.
     *
     *  @return The InitMapPaths
     */
    public String getInitMapPaths() {
        return this.initMapPaths;
    }
    /**
     * Set the intial lat/lon visible
     *
     * @param v  true or false
     */
    public void setInitLatLonVisible(boolean v) {
        initLatLonVisible = v;
    }



    /**
     * Set the intial lat/lon color
     *
     * @param v  the color
     */
    public void setInitLatLonColor(Color v) {
        initLatLonColor = v;
    }


    /**
     * Set the initial lat/lon line width
     *
     * @param v  the width
     */
    public void setInitLatLonWidth(int v) {
        initLatLonWidth = v;
    }

    /**
     * Set the initial lat/lon spacing
     *
     * @param v  the spacing
     */
    public void setInitLatLonSpacing(float v) {
        initLatLonSpacing = v;
    }

    /**
     * Set the InitMapWidth property.
     *
     * @param value The new value for InitMapWidth
     */
    public void setInitMapWidth(float value) {
        this.initMapWidth = value;
    }

    /**
     * Get the InitMapWidth property.
     *
     * @return The InitMapWidth
     */
    public float getInitMapWidth() {
        return this.initMapWidth;
    }

    /**
     * Set the InitMapColor property.
     *
     * @param value The new value for InitMapColor
     */
    public void setInitMapColor(Color value) {
        this.initMapColor = value;
    }

    /**
     * Get the InitMapColor property.
     *
     * @return The InitMapColor
     */
    public Color getInitMapColor() {
        return this.initMapColor;
    }

    /**
     * Set the InitLatLonBounds property.
     *
     * @param value The new value for InitLatLonBounds
     */
    public void setInitLatLonBounds(Rectangle2D.Float value) {
        this.initLatLonBounds = value;
    }

    /**
     * Get the InitLatLonBounds property.
     *
     * @return The InitLatLonBounds
     */
    public Rectangle2D.Float getInitLatLonBounds() {
        return this.initLatLonBounds;
    }

    /**
     * Gets the lat axis scale info.
     *
     * @return the lat axis scale info
     */
    public LatLonAxisScaleInfo getLatAxisScaleInfo() {
        if ( !hasDisplayMaster()) {
            return latAxisScaleInfo;
        }

        if ( !useGlobeDisplay) {
            MapProjectionDisplay d =
                (MapProjectionDisplay) getNavigatedDisplay();
            return d.getLatScaleInfo();
        } else {
            return null;
        }
    }

    /**
     * Sets the lat axis scale info.
     *
     * @param axisScaleInfo
     *            the new lat axis scale info
     * @throws RemoteException
     *             the remote exception
     * @throws VisADException
     *             the VisAD exception
     * @deprecated
     * public void setLatAxisScaleInfo(AxisScaleInfo axisScaleInfo)
     *       throws RemoteException, VisADException {
     *
     *   setLatAxisScaleInfo((LatLonAxisScaleInfo) axisScaleInfo);
     * }
     */

    /**
     * Sets the lat axis scale info.
     *
     * @param axisScaleInfo
     *            the new lat axis scale info
     * @throws RemoteException
     *             the remote exception
     * @throws VisADException
     *             the VisAD exception
     */
    public void setLatAxisScaleInfo(LatLonAxisScaleInfo axisScaleInfo)
            throws RemoteException, VisADException {

        this.latAxisScaleInfo = axisScaleInfo;

        if ( !hasDisplayMaster()) {
            return;
        }

        if ( !useGlobeDisplay) {
            MapProjectionDisplay d =
                (MapProjectionDisplay) getNavigatedDisplay();
            d.setLatScaleInfo(axisScaleInfo);
        }
    }

    /**
     * Gets the lon axis scale info.
     *
     * @return the lon axis scale info
     */
    public LatLonAxisScaleInfo getLonAxisScaleInfo() {
        if ( !hasDisplayMaster()) {
            return lonAxisScaleInfo;
        }

        if ( !useGlobeDisplay) {
            MapProjectionDisplay d =
                (MapProjectionDisplay) getNavigatedDisplay();
            return d.getLonScaleInfo();
        } else {
            return null;
        }
    }

    /**
     * Sets the lon axis scale info.
     *
     * @param axisScaleInfo
     *            the new lon axis scale info
     * @throws RemoteException
     *             the remote exception
     * @throws VisADException
     *             the vis ad exception
     * @deprecated
     * public void setLonAxisScaleInfo(AxisScaleInfo axisScaleInfo)
     *       throws RemoteException, VisADException {
     *   setLonAxisScaleInfo((LatLonAxisScaleInfo) axisScaleInfo);
     * }
     */

    /**
     * Sets the lon axis scale info.
     *
     * @param axisScaleInfo
     *            the new lon axis scale info
     * @throws RemoteException
     *             the remote exception
     * @throws VisADException
     *             the vis ad exception
     */
    public void setLonAxisScaleInfo(LatLonAxisScaleInfo axisScaleInfo)
            throws RemoteException, VisADException {

        this.lonAxisScaleInfo = axisScaleInfo;

        if ( !hasDisplayMaster()) {
            return;
        }

        if ( !useGlobeDisplay) {
            MapProjectionDisplay d =
                (MapProjectionDisplay) getNavigatedDisplay();
            d.setLonScaleInfo(axisScaleInfo);
        }
    }




}
File
MapViewManager.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*
 * Copyright 1997-2013 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 * 
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License

 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package ucar.unidata.idv.ui;


import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;

import org.python.util.PythonInterpreter;

import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;

import ucar.unidata.data.DataChoice;
import ucar.unidata.data.DataSelection;
import ucar.unidata.data.DataSource;
import ucar.unidata.data.GeoLocationInfo;
import ucar.unidata.data.grid.GridDataSource;
import ucar.unidata.geoloc.ProjectionRect;
import ucar.unidata.idv.ControlDescriptor;
import ucar.unidata.idv.DisplayControl;
import ucar.unidata.idv.IdvManager;
import ucar.unidata.idv.IdvPersistenceManager;
import ucar.unidata.idv.IntegratedDataViewer;
import ucar.unidata.idv.MapViewManager;
import ucar.unidata.idv.VectorGraphicsRenderer;
import ucar.unidata.idv.ViewDescriptor;
import ucar.unidata.idv.ViewManager;
import ucar.unidata.idv.control.DisplayControlImpl;
import ucar.unidata.ui.ImageUtils;
import ucar.unidata.ui.colortable.ColorTableCanvas;
import ucar.unidata.util.ColorTable;
import ucar.unidata.util.FileManager;
import ucar.unidata.util.GuiUtils;
import ucar.unidata.util.IOUtil;
import ucar.unidata.util.LogUtil;
import ucar.unidata.util.Misc;
import ucar.unidata.util.PatternFileFilter;
import ucar.unidata.util.Range;
import ucar.unidata.util.StringUtil;
import ucar.unidata.util.Trace;
import ucar.unidata.view.geoloc.NavigatedDisplay;
import ucar.unidata.view.geoloc.ViewpointInfo;
import ucar.unidata.xml.XmlUtil;

import ucar.visad.GeoUtils;
import ucar.visad.display.Animation;
import ucar.visad.display.AnimationWidget;

import visad.CommonUnit;
import visad.MouseBehavior;
import visad.Real;
import visad.RealType;
import visad.Unit;

import visad.georef.EarthLocation;
import visad.georef.EarthLocationTuple;
import visad.georef.LatLonPoint;
import visad.georef.MapProjection;

import visad.util.BaseRGBMap;
import visad.util.ColorPreview;


import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;

import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.TimeZone;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;


/**
 * Manages the user interface for the IDV
 *
 *
 * @author IDV development team
 */
public class ImageGenerator extends IdvManager {


    /** attr value */

    public static final String VALUE_TOP = "top";

    /** attr value */
    public static final String VALUE_BOTTOM = "bottom";

    /** attr value */
    public static final String VALUE_RIGHT = "right";

    /** attr value */
    public static final String VALUE_LEFT = "left";

    /** attr value */
    public static final String VALUE_ALL = "all";

    /** attr value */
    public static final String VALUE_NONE = "none";

    /** attr value */
    public static final String VALUE_FIRST = "first";

    /** attr value */
    public static final String VALUE_LAST = "last";

    /** macro property */
    public static final String PROP_LOOPINDEX = "loopindex";

    /** padded loop index */
    public static final String PROP_LOOPINDEX_PAD2 = "loopindex_pad2";

    /** padded loop index */
    public static final String PROP_LOOPINDEX_PAD3 = "loopindex_pad3";

    /** padded loop index */
    public static final String PROP_LOOPINDEX_PAD4 = "loopindex_pad4";




    /** macro property */
    public static final String PROP_VIEWINDEX = "viewindex";


    /** macro property */
    public static final String PROP_VIEWNAME = "viewname";

    /** macro property */
    public static final String PROP_IMAGEINDEX = "imageindex";


    /** macro property */
    public static final String PROP_IMAGEFILE = "imagefile";

    /** macro property */
    public static final String PROP_IMAGEPATH = "imagepath";


    /** file property */
    public static final String PROP_FILE = "file";

    /** filenosuffix property */
    public static final String PROP_FILENOSUFFIX = "filenosuffix";

    /** filetail property */
    public static final String PROP_FILETAIL = "filetail";

    /** filetailnosuffix property */
    public static final String PROP_FILETAILNOSUFFIX = "filetailnosuffix";

    /** fileprefix property */
    public static final String PROP_FILEPREFIX = "fileprefix";

    /** macro property */
    public static final String PROP_CONTENTS = "contents";

    /** macro property */
    public static final String PROP_ANIMATIONTIME = "animationtime";


    /** macro property */
    public static final String PROP_OFFSCREEN = "offscreen";



    /** Macro name */
    private static final String[] DATE_PROPS = {
        "G", "yy", "yyyy", "MM", "M", "MMM", "MMMMM", "HH", "H", "k", "kk",
        "D", "d", "dd", "K", "KK", "a", "mm", "ss", "s", "S", "EEE", "Z"
    };

    /** List of SimpleDateFormat objects. One for each DATE_PROPS. */
    private static List DATE_FORMATS;


    /** isl tag */
    public static final String TAG_FILESET = "fileset";


    /** view tag */
    public static final String TAG_VIEW = "view";

    /** isl tag */
    public static final String TAG_TEMPLATE = "template";


    /** isl tag */
    public static final String TAG_APPEND = "append";

    /** isl tag */
    public static final String TAG_SETFILES = "setfiles";


    /** isl tag */
    public static final String TAG_ISL = "isl";

    /** viewpoint tag */
    public static final String TAG_VIEWPOINT = "viewpoint";

    /** isl tag */
    public static final String TAG_PROPERTY = "property";

    /** isl tag */
    public static final String TAG_IMPORT = "import";

    /** isl tag */
    public static final String TAG_IMAGE = "image";

    /** isl tag */
    public static final String TAG_GROUP = "group";

    /** isl tag */
    public static final String TAG_PAUSE = "pause";

    /** isl tag */
    public static final String TAG_MOVIE = "movie";

    /** isl tag */
    public static final String TAG_BUNDLE = "bundle";

    /** isl tag */
    public static final String TAG_ELSE = "else";

    /** isl tag */
    public static final String TAG_THEN = "then";


    /** isl tag */
    public static final String TAG_COLORBAR = "colorbar";

    /** isl tag */
    public static final String TAG_CLIP = "clip";



    /** publish tag */
    public static final String TAG_PUBLISH = "publish";

    /** isl tag */
    public static final String TAG_DISPLAY = "display";

    /** datasource tag */
    public static final String TAG_DATASOURCE = "datasource";

    /** isl tag */
    public static final String TAG_MATTE = "matte";

    /** show tag */
    public static final String TAG_SHOW = "show";

    /** displaylist tag */
    public static final String TAG_DISPLAYLIST = "displaylist";

    /** isl tag */
    public static final String TAG_OUTPUT = "output";


    /** isl tag */
    public static final String TAG_OVERLAY = "overlay";


    /** isl tag */
    public static final String TAG_KML = "kml";

    /** kml colorbar tag */
    public static final String TAG_KML_COLORBAR = "kmlcolorbar";

    /** isl tag */
    public static final String TAG_KMZFILE = "kmzfile";

    /** isl tag */
    public static final String TAG_SPLIT = "split";

    /** isl tag */
    public static final String TAG_RESIZE = "resize";

    /** isl tag */
    public static final String TAG_THUMBNAIL = "thumbnail";

    /** isl tag */
    public static final String TAG_TRANSPARENT = "transparent";

    /** background transparent tag */
    public static final String TAG_BGTRANSPARENT = "backgroundtransparent";


    /** index attribute */
    public static final String ATTR_INDEX = "index";

    /** stride attribute */
    public static final String ATTR_STRIDE = "stride";

    /** x stride attribute */
    public static final String ATTR_STRIDEX = "stridex";

    /** y stride attribute */
    public static final String ATTR_STRIDEY = "stridey";

    /** z stride attribute */
    public static final String ATTR_STRIDEZ = "stridez";
    /** bounding box attribute */
    public static final String ATTR_BBOX = "bbox";

    /** from level attribute */
    public static final String ATTR_LEVEL_FROM = "levelfrom";

    /** to level attribute */
    public static final String ATTR_LEVEL_TO = "levelto";

    /** azimuth attribute */
    public static final String ATTR_AZIMUTH = "azimuth";

    /** tilt attribute */
    public static final String ATTR_TILT = "tilt";

    /** x aspect attribute */
    public static final String ATTR_ASPECTX = "aspectx";

    /** y aspect attribute */
    public static final String ATTR_ASPECTY = "aspecty";

    /** z aspect attribute */
    public static final String ATTR_ASPECTZ = "aspectz";


    /** x rotation attribute */
    public static final String ATTR_ROTX = "rotx";

    /** y rotation attribute */
    public static final String ATTR_ROTY = "roty";

    /** z rotation attribute */
    public static final String ATTR_ROTZ = "rotz";

    /** scale attribute */
    public static final String ATTR_SCALE = "scale";

    /** x translation attribute */
    public static final String ATTR_TRANSX = "transx";

    /** y translation attribute */
    public static final String ATTR_TRANSY = "transy";

    /** z translation attribute */
    public static final String ATTR_TRANSZ = "transz";


    /** suffix attribute */
    public static final String ATTR_SUFFIX = "suffix";

    /** showunit atttribute */
    public static final String ATTR_SHOWUNIT = "showunit";

    /** transparency attribute */
    public static final String ATTR_TRANSPARENCY = "transparency";

    /** top attribute */
    public static final String ATTR_TOP = "top";


    /** left space attribute */
    public static final String ATTR_SPACE_LEFT = "space_left";

    /** right space attribute */
    public static final String ATTR_SPACE_RIGHT = "space_right";

    /** top space attribute */
    public static final String ATTR_SPACE_TOP = "space_top";

    /** bottom space attribute */
    public static final String ATTR_SPACE_BOTTOM = "space_bottom";




    /** isl tag */
    public static final String TAG_WRITE = "write";



    /** isl tag */
    public static final String ATTR_ANCHOR = "anchor";


    /** isl attr */
    public static final String ATTR_FROM = "from";

    /** isl attr */
    public static final String ATTR_TO = "to";


    /** isl attribute */
    public static final String ATTR_GLOBAL = "global";

    /** isl attribute */
    public static final String ATTR_ONERROR = "onerror";

    /** isl attribute */
    public static final String ATTR_SORT = "sort";

    /** isl attribute */
    public static final String ATTR_SORTDIR = "sortdir";

    /** isl attribute */
    public static final String VALUE_TIME = "time";

    /** isl attribute */
    public static final String VALUE_ASCENDING = "ascending";

    /** isl attribute */
    public static final String VALUE_DESCENDING = "descending";


    /** isl attribute */
    public static final String ATTR_FIRST = "first";

    /** isl attribute */
    public static final String ATTR_LAST = "last";



    /** isl tag */
    public static final String ATTR_USEPROJECTION = "useprojection";

    /** isl tag */
    public static final String ATTR_EXPR = "expr";

    /** isl tag */
    public static final String ATTR_COPY = "copy";

    /** the count tag */
    public static final String ATTR_COUNT = "count";

    /** isl tag */
    public static final String ATTR_COLUMNS = "columns";

    /** isl attribute */
    public static final String ATTR_DATASOURCE = "datasource";


    /** isl attribute */
    public static final String ATTR_DESTINATION = "destination";

    /** isl attribute */
    public static final String ATTR_SERVER = "server";

    /** isl attribute */
    public static final String ATTR_PASSWORD = "password";

    /** isl attribute */
    public static final String ATTR_USER = "user";

    /** isl tag */
    public static final String ATTR_ROWS = "rows";

    /** isl tag */
    public static final String ATTR_CLASS = "class";

    /** isl tag */
    public static final String ATTR_ANGLE = "angle";

    /** isl tag */
    public static final String ATTR_WHERE = "where";

    /** isl tag */
    public static final String ATTR_BACKGROUND = "background";

    /** isl attribute */
    public static final String ATTR_BUNDLE = "bundle";

    /** isl tag */
    public static final String ATTR_SHOWLINES = "showlines";

    /** isl tag */
    public static final String ATTR_LINECOLOR = "linecolor";

    /** isl tag */
    public static final String ATTR_COLOR = "color";

    /** isl tag */
    public static final String ATTR_COMMAND = "command";

    /** isl tag */
    public static final String ATTR_FONTFACE = "fontface";

    /** isl attr */
    public static final String ATTR_FORMAT = "format";

    /** that latlonlabels tag */
    public static final String TAG_LATLONLABELS = "latlonlabels";

    /** that latvalues tag */
    public static final String ATTR_LAT_VALUES = "latvalues";

    /** that latlabels tag */
    public static final String ATTR_LAT_LABELS = "latlabels";

    /** that lonvalues tag */
    public static final String ATTR_LON_VALUES = "lonvalues";


    /** that lonlabels tag */
    public static final String ATTR_LON_LABELS = "lonlabels";

    /** the draw lon lines tag */
    public static final String ATTR_DRAWLONLINES = "drawlonlines";

    /** the draw lat lines tag */
    public static final String ATTR_DRAWLATLINES = "drawlatlines";

    /** dashes tag */
    public static final String ATTR_DASHES = "dashes";

    /** linewidth tag */
    public static final String ATTR_LINEWIDTH = "linewidth";

    /** isl tag */
    public static final String ATTR_LINEOFFSET_RIGHT = "lineoffsetright";

    /** isl tag */
    public static final String ATTR_LINEOFFSET_LEFT = "lineoffsetleft";

    /** isl tag */
    public static final String ATTR_LINEOFFSET_TOP = "lineoffsettop";

    /** isl tag */
    public static final String ATTR_LINEOFFSET_BOTTOM = "lineoffsetbottom";

    /** isl tag */
    public static final String ATTR_LABELBACKGROUND = "labelbackground";

    /** isl tag */
    public static final String ATTR_SHOWTOP = "showtop";

    /** isl tag */
    public static final String ATTR_SHOWBOTTOM = "showbottom";

    /** isl tag */
    public static final String ATTR_SHOWLEFT = "showleft";

    /** isl tag */
    public static final String ATTR_SHOWRIGHT = "showright";

    /** isl tag */
    public static final String ATTR_FONTSIZE = "fontsize";

    /** isl tag */
    public static final String ATTR_FRAMERATE = "framerate";

    /** isl tag for ending frame pause for animated gifs */
    public static final String ATTR_ENDFRAMEPAUSE = "endframepause";

    /** isl tag */
    public static final String ATTR_CAPTION = "caption";

    /** isl tag */
    public static final String ATTR_DEBUG = "debug";

    /** isl tag */
    public static final String ATTR_DEFAULT = "default";

    /** isl tag */
    public static final String ATTR_DISPLAY = "display";

    /** isl tag */
    public static final String ATTR_OFFSCREEN = "offscreen";

    /** isl tag */
    public static final String ATTR_TIMES = "times";

    /** isl tag */
    public static final String ATTR_ENSEMBLES = "ensembles";

    /** isl tag */
    public static final String ATTR_DIR = "dir";

    /** isl tag */
    public static final String ATTR_PATTERN = "pattern";


    /** isl attribute */
    public static final String ATTR_WAIT = "wait";

    /** isl tag */
    public static final String ATTR_PROPERTY = "property";

    /** isl tag */
    public static final String ATTR_QUALITY = "quality";

    /** isl tag */
    public static final String ATTR_LOOP = "loop";

    /** isl tag */
    public static final String ATTR_ENTRY = "entry";

    /** isl tag */
    public static final String ATTR_ID = "id";

    /** isl tag */
    public static final String ATTR_IMAGE = "image";

    /** isl tag */
    public static final String ATTR_INTERVAL = "interval";

    /** isl tag */
    public static final String ATTR_LEFT = "left";

    /** isl tag */
    public static final String ATTR_MESSAGE = "message";

    /** isl tag */
    public static final String ATTR_MATTEBG = "mattebg";

    /** isl tag */
    public static final String ATTR_NAME = "name";

    /** isl tag */
    public static final String ATTR_RIGHT = "right";

    /** isl tag */
    public static final String ATTR_TICKMARKS = "tickmarks";

    /** isl tag */
    public static final String ATTR_SPACE = "space";

    /** isl tag */
    public static final String ATTR_HSPACE = "hspace";

    /** isl tag */
    public static final String ATTR_VSPACE = "vspace";

    /** isl tag */
    public static final String ATTR_BOTTOM = "bottom";

    /** the valign attribute */
    public static final String ATTR_VALIGN = "valign";

    /** isl tag */
    public static final String ATTR_TEXT = "text";

    /** isl tag */
    public static final String ATTR_TEMPLATE = "template";

    /** isl tag */
    public static final String ATTR_TYPE = "type";

    /** isl tag */
    public static final String ATTR_EVERY = "every";

    /** isl tag */
    public static final String ATTR_VALUE = "value";

    /** isl tag */
    public static final String ATTR_VALUES = "values";

    /** isl tag */
    public static final String ATTR_ORIENTATION = "orientation";


    /** isl tag */
    public static final String ATTR_PARAM = "param";

    /** isl tag */
    public static final String ATTR_PLACE = "place";

    /** isl tag */
    public static final String ATTR_VIEW = "view";

    /** the view dir attribute */
    public static final String ATTR_VIEWDIR = "viewdir";

    /** isl tag */
    public static final String ATTR_URL = "url";

    /** isl tag */
    public static final String ATTR_FILE = "file";

    /** isl tag */
    public static final String ATTR_FROMFILE = "fromfile";

    /** isl tag */
    public static final String ATTR_NORTH = "north";

    /** isl tag */
    public static final String ATTR_SOUTH = "south";

    /** isl tag */
    public static final String ATTR_EAST = "east";

    /** isl tag */
    public static final String ATTR_WEST = "west";

    /** isl tag */
    public static final String ATTR_WIDTH = "width";

    /** isl tag */
    public static final String ATTR_HEIGHT = "height";

    /** isl tag */
    public static final String ATTR_SLEEP = "sleep";

    /** isl tag */
    public static final String ATTR_SECONDS = "seconds";

    /** isl tag */
    public static final String ATTR_MINUTES = "minutes";

    /** isl tag */
    public static final String ATTR_HOURS = "hours";

    /** isl tag */
    public static final String ATTR_CLEAR = "clear";

    /** isl tag */
    public static final String ATTR_CODE = "code";

    /** isl tag */
            }
    public static final String ATTR_LAT = "lat";

    /** isl tag */
    public static final String ATTR_LON = "lon";

    /** isl attribute */
    public static final String ATTR_WHAT = "what";

    /** isl attribute */
    public static final String ATTR_COMBINE = "combine";

    /** isl attribute */
    public static final String ATTR_ANIMATION_INDEX = "animation_index";

        return inner;
    /** isl attribute */
    private static final String ATTR_SUFFIXFREQUENCY = "suffixfrequency";

    /** Show debug messages */
    private boolean debug = false;

    /** Stack of properties hashtables */
    private List propertiesStack = new ArrayList();

    /** Maps id to data source */
    private Hashtable idToDataSource = new Hashtable();

    /** The interpreter */
    private PythonInterpreter interpreter;

    /** Stack of active OutputInfo objects */
    private List outputStack = new ArrayList();

    /** When we are looping this gets set so we can use in as a macro value */
    private int currentLoopIndex = 0;

    /** Holds the procedure elements. Maps procedure name to element. */
    private Hashtable procs;

    /** Holds the tag name to method */
    private Hashtable methods = new Hashtable();

    /** current xml node we are processing */
    private Element currentNode;


    /** Keep around the last image captured */
    private Image lastImage;

    /** an object map */
    private Hashtable objectMap;



    /**
     * Create me with the IDV
     *
     * @param idv The IDV
     */
    public ImageGenerator(IntegratedDataViewer idv) {
        super(idv);
    }

    /**
     * Create me with the IDV and start processing files
     *
     * @param idv The IDV
     * @param scriptFiles List of isl files
     */
    public ImageGenerator(IntegratedDataViewer idv, List scriptFiles) {
        super(idv);
        processScriptFiles(scriptFiles);
    }


    /**
     * Process the list of isl files
     *
     * @param scriptFiles isl files
     */
    public void processScriptFiles(List scriptFiles) {
        for (int fileIdx = 0; fileIdx < scriptFiles.size(); fileIdx++) {
            String filename = (String) scriptFiles.get(fileIdx);
            if ( !processScriptFile(filename)) {
                return;
            }
        }

    }


    /**
     * Process the  isl files
     *
     * @param islFile file
     *
     * @return Was it successful
     */
    public synchronized boolean processScriptFile(String islFile) {
        return processScriptFile(islFile, new Hashtable());
    }


    /**
     * Process the script file
     *
     * @param islFile  the ISL file
     * @param properties optional properties
     *
     * @return true if successful
     */
    public synchronized boolean processScriptFile(String islFile,
            Hashtable properties) {
        procs           = new Hashtable();
        idToDataSource  = new Hashtable();
        propertiesStack = new ArrayList();
        pushProperties();

        if (islFile.endsWith(".jy") || islFile.endsWith(".py")) {
            try {
                String islPath = IOUtil.getFileRoot(islFile);
                putProperty("islpath", islPath);
                String            jythonCode = IOUtil.readContents(islFile);
    }

                PythonInterpreter interp     = getInterpreter();
                if (getIdv().getJythonManager().getInError()) {
                    return false;
                }

                interp.exec(jythonCode);
                popProperties();
                return true;
            } catch (Exception exc) {
                exc.printStackTrace();
                return error("Error running jython script:" + islFile + "\n"
                             + exc);
            }

        }


        Element root = null;
        try {
            InputStream is = null;
            try {
                if (islFile.startsWith("xml:")) {
                    String xml = islFile.substring(4);
                    is      = new ByteArrayInputStream(xml.getBytes());
                    islFile = "Inline isl";
                } else if (islFile.startsWith("b64:")) {
                    is = new ByteArrayInputStream(
                        XmlUtil.decodeBase64(islFile.substring(4)));
                    islFile = "Inline base 64 encoded isl";
                } else {
                    is = IOUtil.getInputStream(islFile, getClass());
                }
            } catch (FileNotFoundException fnfe) {}
            catch (IOException ioe) {}
            if (is == null) {
                return error(
                    "Given script file does not exist or could not be read: "
                    + islFile);
            }
            root = XmlUtil.getRoot(is);
        } catch (Exception exc) {
            exc.printStackTrace();
            return error("Could not load script file:" + islFile + "\n"
                         + exc);
        }
        if (root == null) {
            return error("Could not load script file:" + islFile);
        }
        try {
            String islPath = IOUtil.getFileRoot(islFile);
            putProperty("islpath", islPath);
            objectMap = properties;
            processNode(root);
            popProperties();
        } catch (InvocationTargetException ite) {
            Throwable inner = ite.getTargetException();
            while (inner instanceof InvocationTargetException) {
                inner =
                    ((InvocationTargetException) inner).getTargetException();
            }
            return handleError(inner);
        } catch (Throwable exc) {
            return handleError(exc);
        } finally {
            objectMap = null;
        }
        return true;
    }

    /**
     * Handle the error
     *
     * @param exc The error
     *
     * @return Just return false
     */
    private boolean handleError(Throwable exc) {
        if ( !(exc instanceof IllegalStateException)
                && !(exc instanceof IllegalArgumentException)) {
            exc.printStackTrace();
        } else {
            exc.printStackTrace();
        }
        return error("An error occurred:" + exc);
    }

    /**
        }
     * Find the inner most non InvocationTargetException exception
     *
     * @param ite The exception
     *
     * @return First non InvocationTargetException exception
     */
    private Throwable getInnerException(InvocationTargetException ite) {
        Throwable inner = ite.getTargetException();
        while (inner instanceof InvocationTargetException) {
            inner = ((InvocationTargetException) inner).getTargetException();
        }

    /**
     * Process the node
     *
     * @param node The node
     * @return ok
     * @throws Throwable On badness
     */
    private boolean processNode(Element node) throws Throwable {
        String tagName = node.getTagName();
        //        System.err.println("tag:" + tagName);

        String methodName = "processTag"
                            + tagName.substring(0, 1).toUpperCase()
                            + tagName.substring(1).toLowerCase();

        //Look to see if this is a isl procedure
        Element procNode = (Element) procs.get(tagName);
        if (procNode != null) {
            return processTagCall(node, procNode);
        }


        Object tmp = methods.get(methodName);
        if (tmp == null) {
            try {
                tmp = getClass().getDeclaredMethod(methodName,
                        new Class[] { Element.class });
            } catch (Exception exc) {}
            if (tmp == null) {
                tmp = "no method";
            }
            methods.put(methodName, tmp);
        }

        if (tmp instanceof Method) {
            try {
                currentNode = node;
                Object result = ((Method) tmp).invoke(this,
                                    new Object[] { node });
                if (result.equals(Boolean.TRUE)) {
                    return true;
                }
            } catch (InvocationTargetException ite) {
                Throwable inner = getInnerException(ite);
                if (inner instanceof BadIslException) {
                    return error("Error handling ISL node:"
                                 + XmlUtil.toStringNoChildren(currentNode)
                                 + "\t" + inner.toString());
                }
                throw inner;
            }
            return false;
        }
        return error("Unknown tag:" + tagName);
    }


    /**
     * Recursively call processNode on each of the children elements
     *
     * @param node The parent node.
     *
     * @return Success.
     *
     * @throws Throwable On badness
     */
    private boolean processChildren(Element node) throws Throwable {
        NodeList elements = XmlUtil.getElements(node);
        for (int childIdx = 0; childIdx < elements.getLength(); childIdx++) {
            Element child = (Element) elements.item(childIdx);
            try {
                if ( !processNode(child)) {
                    return false;
                }
            } catch (Throwable thr) {
                String onerror = applyMacros(node, ATTR_ONERROR,
                                             (String) null);
                if (onerror == null) {
                    throw thr;
                }
                if (onerror.equals("ignore")) {}
                else {
                    System.err.println("Error occured");
                    thr.printStackTrace();
                }
        return true;
    }





    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagFtp(Element node) throws Throwable {
    }
        String file        = applyMacros(node, ATTR_FILE);
        String server      = applyMacros(node, ATTR_SERVER);
        String destination = applyMacros(node, ATTR_DESTINATION);
        String user        = applyMacros(node, ATTR_USER, "anonymous");
        String password = applyMacros(node, ATTR_PASSWORD,
                                      "idvuser@unidata.ucar.edu");
        byte[] bytes = IOUtil.readBytes(IOUtil.getInputStream(file));
        ftpPut(server, user, password, destination, bytes);
        return true;
    }


    /**
     * Process the export tag. This allows one to export data from a display. It requires
     * a display identifier
     *
     * @param node isl xml node
     *
     * @return everything is cool
     *
     * @throws Throwable On badness
     */
    protected boolean processTagExport(Element node) throws Throwable {
        DisplayControlImpl display = findDisplayControl(node);
        if (display == null) {
            throw new IllegalArgumentException("Could not find display:"
                    + XmlUtil.toString(node));
        }
        String what     = applyMacros(node, ATTR_WHAT, (String) null);

        String filename = XmlUtil.getAttribute(node, ATTR_FILE);
        display.doExport(what, filename);
        return true;
    }


    /**
     * Process the tag trace
     *
     * @param node  the node
     *
     * @return true if processed
     *
     * @throws Throwable  on badness
     */
    protected boolean processTagTrace(Element node) throws Throwable {
        String pattern = applyMacros(node, ATTR_PATTERN);
        debug("turning trace on for:" + pattern);
        Trace.startTrace();
        Trace.clearOnly();
        Trace.addOnly(pattern);
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagRemovedisplays(Element node)
            throws Throwable {

        if (XmlUtil.hasAttribute(node, ATTR_DISPLAY)) {
            debug("Removing display");
            DisplayControlImpl display = findDisplayControl(node);
            if (display == null) {
                return error("Could not find display:"
                             + XmlUtil.toString(node));
            }
            display.doRemove();
            return true;
        }
        debug("Removing all displays");
        getIdv().removeAllDisplays(true);
        return true;
    }






    /**
     * Process the print cache tag
     *
     * @param node  the XML node
     *
     * @return  true if successful
     *
     * @throws Throwable  on badness
     */
    protected boolean processTagPrintcache(Element node) throws Throwable {
        visad.data.DataCacheManager.getCacheManager().printStats();
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagRemoveall(Element node) throws Throwable {
        debug("Removing all displays and data");
        getIdv().removeAllDisplays(false);
        getIdv().removeAllDataSources();
        idToDataSource = new Hashtable();
        return true;
    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagSetfiles(Element node) throws Throwable {
        DataSource dataSource = findDataSource(node);
        if (dataSource == null) {
            return error("Could not find data source");
        }
        List files = new ArrayList();
        if (XmlUtil.hasAttribute(node, ATTR_FILE)) {
            files.add(applyMacros(node, ATTR_FILE));
        } else {
            List filesetFiles = findFiles(node);
            if (filesetFiles != null) {
                files.addAll(filesetFiles);
            }
        }
        if (files.size() == 0) {
            return error("Could not find files");
        }
        dataSource.setNewFiles(files);
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagExists(Element node) throws Throwable {
        List    files  = findFiles(node);
        boolean exists = ((files != null) && (files.size() > 0));
        putProperty(applyMacros(node, ATTR_PROPERTY), (exists
                ? "1"
                : "0"));
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagAsk(Element node) throws Throwable {
        String  property = applyMacros(node, ATTR_PROPERTY);
        boolean result;
        if (getIdv().getArgsManager().getIsOffScreen()) {
            if ( !XmlUtil.hasAttribute(node, ATTR_DEFAULT)) {
                throw new IllegalStateException(
                    "Running in offscreen mode and the 'ask' node does not have a 'default' attribute");
            }
            result = applyMacros(node, ATTR_DEFAULT, true);
        } else {
            result = GuiUtils.showYesNoDialog(null,
                    applyMacros(node, ATTR_MESSAGE), "");
        }
        putProperty(property, (result
                               ? "1"
                               : "0"));
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagEcho(Element node) throws Throwable {
        String message = applyMacros(node, ATTR_MESSAGE, (String) null);
        if (message == null) {
            message = applyMacros(XmlUtil.getChildText(node));
        }
        System.out.println(message);
        return true;


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagAsktocontinue(Element node) throws Throwable {
        String message = applyMacros(node, ATTR_MESSAGE, (String) null);
        if (message == null) {
            message = applyMacros(XmlUtil.getChildText(node));
        }
        return GuiUtils.askOkCancel("ISL", message);
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagGc(Element node) throws Throwable {
        Runtime.getRuntime().gc();
        return true;
    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagBreak(Element node) throws Throwable {
        throw new MyBreakException();
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagContinue(Element node) throws Throwable {
        throw new MyContinueException();
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagReturn(Element node) throws Throwable {
        throw new MyReturnException();
    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagProcedure(Element node) throws Throwable {
        procs.put(applyMacros(node, ATTR_NAME), node);
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
     * @param node Node to process
    protected boolean processTagMkdir(Element node) throws Throwable {
        IOUtil.makeDir(applyMacros(node, ATTR_FILE));
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagStop(Element node) throws Throwable {
        return false;
    }


    /**
     * Get the property value
     *
     * @param node the XML node
     *
     * @return  the values
     *
     * @throws Throwable on badness
     */
    protected String[] getPropertyValue(Element node) throws Throwable {
        String name  = (String) applyMacros(node, ATTR_NAME);
        String value = null;
        if (XmlUtil.hasAttribute(node, ATTR_VALUE)) {
            value = (String) applyMacros(node, ATTR_VALUE);
        } else if (XmlUtil.hasAttribute(node, ATTR_FROMFILE)) {
            String filename = applyMacros(node, ATTR_FROMFILE);
            value = applyMacros(IOUtil.readContents(filename));
        } else {
            value = XmlUtil.getChildText(node);
            if ((value == null) || (value.trim().length() == 0)) {
                throw new IllegalArgumentException(
                    "No value in property tag: " + XmlUtil.toString(node));
            }
            value = applyMacros(value);
        }
        return new String[] { name, value };
    }

    /**
     * Process IDV property tag
     *
     * @param node  the XML node
     *
     * @return  true if successful
     *
     * @throws Throwable  on badness
     */
    protected boolean processTagIdvproperty(Element node) throws Throwable {
        String[] tuple = getPropertyValue(node);
        debug("setting idv property: " + tuple[0] + " =" + tuple[1]);
        getIdv().getStateManager().putProperty(applyMacros(tuple[0]),
                applyMacros(tuple[1]));
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagProperty(Element node) throws Throwable {
        String[] tuple = getPropertyValue(node);
        putProperty(applyMacros(tuple[0]), tuple[1],
                    applyMacros(node, ATTR_GLOBAL, false));
        return true;
    }




    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagMove(Element node) throws Throwable {
        List files = findFiles(node);
        if (files != null) {
            File dir = new File(applyMacros(node, ATTR_DIR));
            debug("moving files to: " + dir + " files=" + files);
            for (int i = 0; i < files.size(); i++) {
                IOUtil.moveFile(new File(files.get(i).toString()), dir);
            }
        }
        return true;
    }

    /**
     * process the given node
     *
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagRename(Element node) throws Throwable {
        String from = applyMacros(node, ATTR_FROM);
        String to   = applyMacros(node, ATTR_TO);
        IOUtil.moveFile(new File(from), new File(to));
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagDelete(Element node) throws Throwable {
        List files = findFiles(node);
        if (files != null) {
            debug("deleting files:" + files);
            for (int i = 0; i < files.size(); i++) {
                ((File) files.get(i)).delete();
            }
        }
        return true;
    }

    /**
     * Handle the clear tag
     *
     * @param node node
     *
     * @return ok
     *
     * @throws Throwable On badness
     */
    protected boolean processTagClear(Element node) throws Throwable {
        String    name = applyMacros(node, ATTR_NAME);
        Hashtable ht   = (Hashtable) propertiesStack.get(0);
        ht.remove(name);
        return true;
    }

    /**
     * Handle the append tag
     *
     * @param node node
     *
     * @return ok
     *
     * @throws Throwable On badness
     */
    protected boolean processTagAppend(Element node) throws Throwable {
        String    name  = applyMacros(node, ATTR_NAME);
        Hashtable ht    = findTableFor(name);
        String    value = (String) ht.get(name);
        if (value == null) {
            value = "";
        }
        if (XmlUtil.hasAttribute(node, ATTR_VALUE)) {
            value = value + applyMacros(node, ATTR_VALUE);
        } else if (XmlUtil.hasAttribute(node, ATTR_FROMFILE)) {
            String filename = applyMacros(node, ATTR_FROMFILE);
            value = value + applyMacros(IOUtil.readContents(filename));
        } else {
            value = value + applyMacros(XmlUtil.getChildText(node)).trim();
        }
        ht.put(name, value);
        return true;
    }


    /**
     * Handle the append tag
     *
     * @param node node
     *
     * @return ok
     *
     * @throws Throwable On badness
     */
    protected boolean processTagIncrement(Element node) throws Throwable {
        String    name  = applyMacros(node, ATTR_NAME);
        Hashtable ht    = findTableFor(name);
        String    value = (String) ht.get(name);
        if (value == null) {
            value = "0";
        }
        String by = "1";
        if (XmlUtil.hasAttribute(node, ATTR_VALUE)) {
            by = applyMacros(node, ATTR_VALUE);
        }
        double num = new Double(value).doubleValue()
                     + new Double(by).doubleValue();
        ht.put(name, "" + num);
        return true;
    }

    /**
     * Handle the append tag
     *
     * @param node node
     *
     * @return ok
     *
     * @throws Throwable On badness
     */
    protected boolean processTagReplace(Element node) throws Throwable {
        String    name = applyMacros(node, ATTR_NAME);
        Hashtable ht   = findTableFor(name);
        ht.put(name, applyMacros(node, ATTR_VALUE));
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagCopy(Element node) throws Throwable {
        List files = findFiles(node);
        if (files != null) {
            File dir = new File(applyMacros(node, ATTR_DIR));
            IOUtil.makeDir(dir);
            if ( !dir.isDirectory()) {
                return error("Specified file:" + dir + " is not a directory");
            }
            debug("copying files to: " + dir + " files=" + files);
            for (int i = 0; i < files.size(); i++) {
                IOUtil.copyFile(new File(files.get(i).toString()), dir);
            }
        }
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagReload(Element node) throws Throwable {
        List dataSources = getIdv().getDataSources();
        for (int i = 0; i < dataSources.size(); i++) {
            DataSource dataSource = (DataSource) dataSources.get(i);
            dataSource.reloadData();
        }
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagExec(Element node) throws Throwable {
        String command = applyMacros(node, ATTR_COMMAND);
        debug("Calling exec:" + command);
        Process process = Runtime.getRuntime().exec(command);
        //This seems to hang?
        process.waitFor();
        if (process.exitValue() != 0) {
            String result = IOUtil.readContents(process.getInputStream());
            System.err.println("Exec:\n\t" + command + "\nreturned:"
                               + process.exitValue() + "\n" + result);
        }
        return true;

    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagJython(Element node) throws Throwable {
        String jythonFile = applyMacros(node, ATTR_FILE, (String) null);
        if (jythonFile != null) {
            InputStream is = IOUtil.getInputStream(jythonFile, getClass());
            if (is == null) {
                return error("Could not open jython file:" + jythonFile);
            } else {
                getInterpreter().execfile(is, jythonFile);
            }
        } else {
            String jython = applyMacros(node, ATTR_CODE, (String) null);
            if (jython == null) {
                jython = XmlUtil.getChildText(node);
            }
            if (jython != null) {
                getInterpreter().exec(jython);
            }
        }
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagFileset(Element node) throws Throwable {
        List files = findFiles(Misc.newList(node));
        pushProperties();
        for (int i = 0; i < files.size(); i++) {
            try {
                String filePath = files.get(i).toString();
                String tail     = IOUtil.getFileTail(filePath);
                putProperty(PROP_FILE, filePath);
                putProperty(PROP_FILEPREFIX, IOUtil.stripExtension(filePath));
                putProperty(PROP_FILENOSUFFIX,
                            IOUtil.stripExtension(filePath));
                putProperty(PROP_FILETAIL, tail);
                putProperty(PROP_FILETAILNOSUFFIX,
                            IOUtil.stripExtension(tail));
                if ( !processChildren(node)) {
                    return false;
                }
            } catch (MyBreakException be) {
                break;
            } catch (MyContinueException ce) {}
        }
        popProperties();
        return true;

    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagImport(Element node) throws Throwable {
        Element parent  = (Element) node.getParentNode();
        String  file    = applyMacros(node, ATTR_FILE);
        Element root    = XmlUtil.findRoot(node);
        Element newRoot = XmlUtil.getRoot(file, getClass());
        newRoot = (Element) root.getOwnerDocument().importNode(newRoot, true);
        parent.insertBefore(newRoot, node);
        parent.removeChild(node);
        if ( !processNode(newRoot)) {
            return false;
        }
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagDatasource(Element node) throws Throwable {
        debug("Creating data source");

        DataSource dataSource = null;
        String     id         = applyMacros(node, ATTR_ID, (String) null);
        if ((id != null) && (objectMap != null)) {
            dataSource = (DataSource) objectMap.get(id);
        }


        if (dataSource == null) {
            Object dataObject = applyMacros(node, ATTR_URL, (String) null);
            if (dataObject == null) {
                dataObject = StringUtil.toString(findFiles(node));
            }
            String bundle = applyMacros(node, ATTR_BUNDLE, (String) null);
            String type   = applyMacros(node, ATTR_TYPE, (String) null);
            if ((bundle == null) && (dataObject == null)) {
                return error(
                    "datasource tag requires either a url, fileset or a bundle");
            }

            if (dataObject != null) {
                dataSource = getIdv().makeOneDataSource(dataObject, type,
                        null);
                if (dataSource == null) {
                    return error("Failed to create data source:" + dataObject
                                 + " " + type);
                }
            } else {
                try {
                    String bundleXml = IOUtil.readContents(bundle);
                    Object obj =
                        getIdv().getEncoderForRead().toObject(bundleXml);
                    if ( !(obj instanceof DataSource)) {
                        return error("datasource bundle is not a DataSource:"
                                     + obj.getClass().getName());
                    }
                    dataSource = (DataSource) obj;
                } catch (Exception exc) {
                    return error("Error loading data source bundle: "
                                 + bundle, exc);
                }
            }
        }


        if (XmlUtil.hasAttribute(node, ATTR_TIMES)) {
            List timesList =
                StringUtil.parseIntegerListString(applyMacros(node,
                    ATTR_TIMES, (String) null));
            dataSource.setDateTimeSelection(timesList);
        }

        if (XmlUtil.hasAttribute(node, ATTR_ENSEMBLES)) {
            List ensList =
                StringUtil.parseIntegerListString(applyMacros(node,
                    ATTR_ENSEMBLES, (String) null));
            if (dataSource instanceof GridDataSource) {
                ((GridDataSource) dataSource).setEnsembleSelection(ensList);
            }
        }

        processGeoSelectionTags(node, dataSource.getDataSelection());


        Hashtable properties = getProperties(node);
        dataSource.setObjectProperties(properties);
        if (id != null) {
            idToDataSource.put(id, dataSource);
        }

        NodeList elements = XmlUtil.getElements(node);
        for (int childIdx = 0; childIdx < elements.getLength(); childIdx++) {
            Element child = (Element) elements.item(childIdx);
            if (child.getTagName().equals(TAG_DISPLAY)) {
                if ( !processDisplayNode(child, dataSource)) {
                    return false;
                }
            }
        }
        //        getIdv().getVMManager().setDisplayMastersActive();
        updateViewManagers();

        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagJoin(Element node) throws Throwable {
        List files = findFiles(node);
        if (files != null) {
            List images = new ArrayList();
            int  cols   = applyMacros(node, ATTR_COLUMNS, 0);
            int  rows   = applyMacros(node, ATTR_ROWS, 0);
            if ((cols == 0) && (rows == 0)) {
                cols = 1;
            }
            if ((cols != 0) && (rows != 0)) {
                cols = 0;
            }
            int colNum = 0;
            int rowNum = 0;
            for (int i = 0; i < files.size(); i++) {
                Image theImage =
                    ImageUtils.readImage(files.get(i).toString());
                if (theImage == null) {
                    continue;
                }
                images.add(theImage);
            }
            if (images.size() > 0) {
                if (cols == 0) {
                    cols = images.size() / rows;
                } else {
                    rows = images.size() / cols;
                }

                int maxWidth  = 0;
                int maxHeight = 0;
                int colCnt    = 0;
                for (int i = 0; i < images.size(); i++) {
                    Image theImage = (Image) images.get(i);
                    int   width    = theImage.getWidth(null);
                    int   height   = theImage.getHeight(null);
                }
            }
        }
        return true;
    }

    /**
     * Process the view tag
     *
     * @param node the element
     *
     * @return  true if it was processed
     *
     * @throws Throwable  problems
     */
    protected boolean processTagView(Element node) throws Throwable {
        List   vms    = getViewManagers(node);

        String width  = applyMacros(node, ATTR_WIDTH, (String) null);
        String height = applyMacros(node, ATTR_HEIGHT, (String) null);
        if ((width != null) && (height != null)) {
            getIdv().getStateManager().setViewSize(
                new Dimension(
                    new Integer(width).intValue(),
                    new Integer(height).intValue()));
        }

        if (vms.size() == 0) {
            StringBuffer properties = new StringBuffer();
            List         nodes      = XmlUtil.findChildren(node,
                                          TAG_PROPERTY);
            for (int childIdx = 0; childIdx < nodes.size(); childIdx++) {
                Element child = (Element) nodes.get(childIdx);
                properties.append(applyMacros(child, ATTR_NAME) + "="
                                  + applyMacros(child, ATTR_VALUE) + ";");
            }
            vms.add(getIdv().getViewManager(ViewDescriptor.LASTACTIVE, false,
                                            properties.toString()));
            return true;
        }

        for (int i = 0; i < vms.size(); i++) {
            ViewManager vm    = (ViewManager) vms.get(i);
            List        nodes = XmlUtil.findChildren(node, TAG_PROPERTY);
            for (int childIdx = 0; childIdx < nodes.size(); childIdx++) {
                Element child = (Element) nodes.get(childIdx);
                vm.setProperty(applyMacros(child, ATTR_NAME),
                               applyMacros(child, ATTR_VALUE), false);
            }
        }
        return true;
    }


    /**
     * Handle the animation tag. The index attribute can either be a number or be "end"
     *
     * @param node  the node
     *
     * @return  true if successful
     *
     * @throws Throwable  problems
     */
    protected boolean processTagAnimation(Element node) throws Throwable {
        String  indexString = applyMacros(node, ATTR_INDEX, "0");
        int     index       = -1;
        boolean end         = indexString.equals("end");
        boolean step        = indexString.equals("step");
        if ( !end && !step) {
            index = new Integer(indexString).intValue();
        }
        for (ViewManager viewManager : getViewManagers(node)) {
            AnimationWidget animationWidget =
                viewManager.getAnimationWidget();
            if (animationWidget == null) {
                continue;
            }
            if (end) {
                animationWidget.gotoEnd();
            } else if (step) {
                animationWidget.stepForward();
            } else {
                animationWidget.gotoIndex(index);
            }
        }
        return true;
    }



    /**
     * Process the viewpoint tag
     *
     * @param node  the node
     *
     * @return  true if successful
     *
     * @throws Throwable  problems
     */
    protected boolean processTagViewpoint(Element node) throws Throwable {

        List vms = getViewManagers(node);
        if (vms.size() == 0) {
            debug("Could not find view managers processing:"
                  + XmlUtil.toString(node));
        }
        ViewpointInfo viewpointInfo = null;


        for (int i = 0; i < vms.size(); i++) {
            ViewManager vm = (ViewManager) vms.get(i);
            if (XmlUtil.hasAttribute(node, ATTR_VIEWDIR)) {
                vm.setView(XmlUtil.getAttribute(node, ATTR_VIEWDIR));
            }

            if (XmlUtil.hasAttribute(node, ATTR_AZIMUTH)
                    || XmlUtil.hasAttribute(node, ATTR_TILT)) {
                viewpointInfo = new ViewpointInfo(toDouble(node,
                        ATTR_AZIMUTH, 0.0), toDouble(node, ATTR_TILT, 0.0));
                if ( !(vm instanceof MapViewManager)) {
                    continue;
                }
                MapViewManager mvm = (MapViewManager) vm;
                mvm.setViewpointInfo(viewpointInfo);
            }

            if (XmlUtil.hasAttribute(node, ATTR_ASPECTX)
                    || XmlUtil.hasAttribute(node, ATTR_ASPECTY)
                    || XmlUtil.hasAttribute(node, ATTR_ASPECTZ)) {
                double[] a = vm.getMaster().getDisplayAspect();
                a = new double[] { toDouble(node, ATTR_ASPECTX, a[0]),
                                   toDouble(node, ATTR_ASPECTY, a[1]),
                                   toDouble(node, ATTR_ASPECTZ, a[2]) };
                vm.getMaster().setDisplayAspect(a);
                vm.setAspectRatio(a);
            }

            if (XmlUtil.hasAttribute(node, ATTR_ROTX)
                    || XmlUtil.hasAttribute(node, ATTR_ROTY)
                    || XmlUtil.hasAttribute(node, ATTR_ROTZ)
                    || XmlUtil.hasAttribute(node, ATTR_TRANSX)
                    || XmlUtil.hasAttribute(node, ATTR_TRANSY)
                    || XmlUtil.hasAttribute(node, ATTR_TRANSZ)
                    || XmlUtil.hasAttribute(node, ATTR_SCALE)) {
                double[]      a = vm.getMaster().getDisplayAspect();
                double[]      currentMatrix = vm.getDisplayMatrix();
                double[]      trans         = { 0.0, 0.0, 0.0 };
                double[]      rot           = { 0.0, 0.0, 0.0 };
                double[]      scale         = { 0.0, 0.0, 0.0 };

                MouseBehavior mb = vm.getMaster().getMouseBehavior();
                mb.instance_unmake_matrix(rot, scale, trans, currentMatrix);
                double scaleValue = applyMacros(node, ATTR_SCALE, 0.0);
                if (scaleValue != 0) {
                    double scaleX = scaleValue * a[0];
                    double scaleY = scaleValue * a[1];
                    double scaleZ = scaleValue * a[2];
                    double[] scaleMatrix = mb.make_matrix(0.0, 0.0, 0.0,
                                               scaleX / scale[0],
                                               scaleY / scale[1],
                                               scaleZ / scale[2], 0.0, 0.0,
                                               0.0);
                    currentMatrix = mb.multiply_matrix(scaleMatrix,
                            currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }
                double[] tmp;
                double   dummy = 0.0;
                //TODO: the rotation maybe doesn't work
                if (XmlUtil.hasAttribute(node, ATTR_ROTX)) {
                    tmp = mb.make_matrix(applyMacros(node, ATTR_ROTX, dummy),
                                         0.0, 0.0, 1.0, 0.0, 0.0, 0.0);
                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }
                if (XmlUtil.hasAttribute(node, ATTR_ROTY)) {
                    tmp = mb.make_matrix(0.0,
                                         applyMacros(node, ATTR_ROTY, dummy)
                                         - rot[1], 0.0, 1.0, 0.0, 0.0, 0.0);

                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }
                if (XmlUtil.hasAttribute(node, ATTR_ROTZ)) {
                    tmp = mb.make_matrix(0.0, 0.0,
                                         applyMacros(node, ATTR_ROTZ, dummy)
        return true;
                                         - rot[2], 1.0, 0.0, 0.0, 0.0);

                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }

                if (XmlUtil.hasAttribute(node, ATTR_TRANSX)) {
                    tmp = mb.make_translate(applyMacros(node, ATTR_TRANSX,
                            dummy) - trans[0], 0.0, 0.0);
                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }
                if (XmlUtil.hasAttribute(node, ATTR_TRANSY)) {
                    tmp = mb.make_translate(0.0,
                                            applyMacros(node, ATTR_TRANSY,
                                                dummy) - trans[1], 0.0);
                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }

                if (XmlUtil.hasAttribute(node, ATTR_TRANSZ)) {
                    tmp = mb.make_translate(0.0, 0.0,
                                            applyMacros(node, ATTR_TRANSZ,
                                                dummy) - trans[2]);
                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }


                vm.setDisplayMatrix(currentMatrix);

            }
        }
        return true;

    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagCenter(Element node) throws Throwable {
        List vms = getViewManagers(node);
        if (XmlUtil.hasAttribute(node, ATTR_LAT)) {
            getVMManager().center(
                ucar.visad.Util.makeEarthLocation(
                    toDouble(node, ATTR_LAT), toDouble(node, ATTR_LON)), vms);
            return true;
        }


        if (XmlUtil.hasAttribute(node, ATTR_NORTH)) {
            ProjectionRect projRect = new ProjectionRect(toDouble(node,
                                          ATTR_WEST), toDouble(node,
                                              ATTR_NORTH), toDouble(node,
                                                  ATTR_EAST), toDouble(node,
                                                      ATTR_SOUTH));
            getVMManager().center(projRect, vms);
            return true;
        }

        if (XmlUtil.hasAttribute(node, ATTR_DISPLAY)) {
            DisplayControlImpl display = findDisplayControl(node);
            if (display == null) {
                throw new IllegalArgumentException("Could not find display:"
                        + XmlUtil.toString(node));
            }
            if (XmlUtil.getAttribute(node, ATTR_USEPROJECTION, false)) {
                MapProjection mp = display.getDataProjection();
                if (mp != null) {
                    getVMManager().center(mp, vms);
                }
            } else {
                LatLonPoint llp = display.getDisplayCenter();
                if (llp != null) {
                    getVMManager().center(
                        ucar.visad.Util.makeEarthLocation(llp), vms);
                }
            }
            return true;
        }

        getVMManager().center(vms);
    }


    /**
     * Find the data source that is identified by the given xml node
     *
     * @param node node
     *
     * @return The data source or null
     */
    private DataSource findDataSource(Element node) {
        String id = XmlUtil.getAttribute(node, ATTR_DATASOURCE);
        return findDataSource(id);
    }

    /**
     * Find the data source with the given id
     *
     * @param id the id we pass to datasource.identifiedByName
     *
     * @return The data source or null if none found
     */
    private DataSource findDataSource(String id) {
        List       dataSources = getIdv().getDataSources();
        DataSource dataSource  = (DataSource) idToDataSource.get(id);
        if (dataSource != null) {
            return dataSource;
        }
        for (int i = 0; i < dataSources.size(); i++) {
            dataSource = (DataSource) dataSources.get(i);
            if (dataSource.identifiedByName(id)) {
                return dataSource;
            }
        }
        return null;
    }




    /**
     * Find the display control that is identified by the given xml node
     *
     * @param node node
     *
     * @return The display control source or null
     */
    private DisplayControlImpl findDisplayControl(Element node) {
        String id = XmlUtil.getAttribute(node, ATTR_DISPLAY);
        return findDisplayControl(id);
    }


    /**
     * Find the display control identified by the given id
     *
     * @param id The id of the display control. This can be the id or it can be a 'class:class name'
     *
     * @return The display control or null
     */
    public DisplayControlImpl findDisplayControl(String id) {
        List controls = getIdv().getDisplayControls();
        return findDisplayControl(id, controls);
    }


    /**
     * Find the display control
     *
     * @param id  the control id
     * @param controls  the list of controls
     *
     * @return  the control or null
     */
    public DisplayControlImpl findDisplayControl(String id,
            List controls) {
        for (int i = 0; i < controls.size(); i++) {
            DisplayControlImpl control = (DisplayControlImpl) controls.get(i);

            if (id.startsWith("class:")) {
                if (StringUtil.stringMatch(control.getClass().getName(),
                                           id.substring(6), true, true)) {
                    return control;
                }
            }
            if (control.getId() != null) {
                if (StringUtil.stringMatch(control.getId(), id, true, true)) {
                    return control;
                }
            }
        }
        return null;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagBundle(Element node) throws Throwable {

        List timesList = null;
        List ensList   = null;
        if (XmlUtil.hasAttribute(node, ATTR_TIMES)) {
            timesList = StringUtil.parseIntegerListString(applyMacros(node,
                    ATTR_TIMES, (String) null));
        }
        if (XmlUtil.hasAttribute(node, ATTR_ENSEMBLES)) {
            ensList = StringUtil.parseIntegerListString(applyMacros(node,
                    ATTR_ENSEMBLES, (String) null));
        }


        List nodes    = XmlUtil.findChildren(node, TAG_SETFILES);
        List ids      = new ArrayList();
        List fileList = new ArrayList();
        getPersistenceManager().clearFileMapping();
        for (int i = 0; i < nodes.size(); i++) {
            Element child       = (Element) nodes.get(i);
            String dataSourceId = XmlUtil.getAttribute(child,
                                      ATTR_DATASOURCE);
            ids.add(dataSourceId);
            List files = new ArrayList();
            if (XmlUtil.hasAttribute(child, ATTR_FILE)) {
                String file = applyMacros(child, ATTR_FILE);
                debug("For data source: " + dataSourceId + " Using file: "
                      + file);
                files.add(file);
            } else {
                List filesetFiles = findFiles(child);
                if (filesetFiles != null) {
                    debug("For data source: " + dataSourceId
                          + " Using file: " + filesetFiles);
                    files.addAll(filesetFiles);
                } else {
                    debug("For data source: " + dataSourceId
                          + " Could not find any files");
                }
            }
            fileList.add(files);
            debug("Adding a file override id=" + dataSourceId + " files="
                  + files);
        }
        if (ids.size() > 0) {
            getPersistenceManager().setFileMapping(ids, fileList);
        }


        String width  = applyMacros(node, ATTR_WIDTH, (String) null);
        String height = applyMacros(node, ATTR_HEIGHT, (String) null);
        if ((width != null) && (height != null)) {
            getIdv().getStateManager().setViewSize(
                new Dimension(
                    new Integer(width).intValue(),
                    new Integer(height).intValue()));
        }
        String  bundleFile = applyMacros(node, ATTR_FILE, (String) null);
        boolean doRemove   = applyMacros(node, ATTR_CLEAR, true);
        if (doRemove) {
            //            try {
            cleanup();
            //            } catch(Exception exc) {
            //                System.err.println ("Error cleanup");
            //                System.exit(1);
            //            }
        }
        getIdv().getStateManager().setAlwaysLoadBundlesSynchronously(true);
        Hashtable bundleProperties = new Hashtable();
        if (timesList != null) {
            bundleProperties.put(IdvPersistenceManager.PROP_TIMESLIST,
                                 timesList);
        }
        if (ensList != null) {
            bundleProperties.put(IdvPersistenceManager.PROP_ENSLIST, ensList);
        }


        if (bundleFile != null) {
            debug("Loading bundle: " + bundleFile);
            if (bundleFile.endsWith(".jnlp")) {
                String xml =
                    getPersistenceManager().extractBundleFromJnlp(bundleFile);
                getPersistenceManager().decodeXml(xml, false, bundleFile,
                        null, false, true, bundleProperties, true, false);
            } else if (getArgsManager().isZidvFile(bundleFile)) {
                Hashtable properties = new Hashtable();
                boolean   ask        = getStore().get(PREF_ZIDV_ASK, true);
                getStore().put(PREF_ZIDV_ASK, false);
                getPersistenceManager().decodeXmlFile(bundleFile, "", false,
                        false, properties);
                getStore().put(PREF_ZIDV_ASK, ask);
            } else {
                String xml = IOUtil.readContents(bundleFile);
                xml = applyMacros(xml, null, false);
                getPersistenceManager().decodeXml(xml, false, bundleFile,
                        null, false, true, bundleProperties, true, false);
                //                getPersistenceManager().decodeXmlFile(bundleFile, false,
                //                        timesList);
            }
        } else {
            String b64Bundle = XmlUtil.getChildText(node).trim();
            if (b64Bundle.length() == 0) {
                return error("Could not bundle");
            }
            String xml = new String(XmlUtil.decodeBase64(b64Bundle));
            getPersistenceManager().decodeXml(xml, false, "", null, false,
                    true, bundleProperties, true, false);

        }

        if (applyMacros(node, ATTR_WAIT, true)) {
            getIdv().getIdvUIManager().waitUntilDisplaysAreDone(
                getIdv().getIdvUIManager());
        }
        getPersistenceManager().clearFileMapping();
        Color c            = applyMacros(node, ATTR_COLOR, (Color) null);
        List  viewManagers = getVMManager().getViewManagers();
        for (int i = 0; i < viewManagers.size(); i++) {
            ViewManager viewManager = (ViewManager) viewManagers.get(i);
            if (c != null) {
                viewManager.setColors(null, c);
            }
            viewManager.updateDisplayList();
        }

        //One more pause for the display lists
        updateViewManagers();
        getIdv().getIdvUIManager().waitUntilDisplaysAreDone(
            getIdv().getIdvUIManager());
        return true;
    }

    /**
     * remove data and displays, etc
     */
    private void cleanup() {
        getIdv().removeAllDisplays(false);
        getIdv().removeAllDataSources();
        idToDataSource = new Hashtable();
        ucar.unidata.util.CacheManager.clearCache();

        //        getIdv().getIdvUIManager().disposeAllWindows();
        if (getIdv().getArgsManager().getIsOffScreen()) {
            getIdv().getVMManager().removeAllViewManagers(true);
        }
        getIdv().getIdvUIManager().clearWaitCursor();

        double totalMemory   = (double) Runtime.getRuntime().maxMemory();
        double highWaterMark = (double) Runtime.getRuntime().totalMemory();
        double freeMemory    = (double) Runtime.getRuntime().freeMemory();
        double usedMemory    = (highWaterMark - freeMemory);
        totalMemory   = totalMemory / 1000000.0;
        usedMemory    = usedMemory / 1000000.0;
        highWaterMark = highWaterMark / 1000000.0;

        /*            System.err.println(
                      "MEM:" + ((int) usedMemory) + "/" + ((int) highWaterMark)
                      + " vms:" + getIdv().getVMManager().getViewManagers().size());
        */

    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagCall(Element node) throws Throwable {
        String  name     = applyMacros(node, ATTR_NAME);
        Element procNode = (Element) procs.get(name);
        return processTagCall(node, procNode);
    }

    /**
     * process the given node
     *
     * @param node Node to process
     * @param procNode The procedure node
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
        return true;
    protected boolean processTagCall(Element node, Element procNode)
            throws Throwable {
        if (procNode == null) {
            return error("Could not find procedure node for call:"
                         + XmlUtil.toString(node));
        }

        pushProperties();
        String cdata = XmlUtil.getChildText(node);
        if ((cdata != null) && (cdata.trim().length() > 0)) {
            putProperty("paramtext", cdata);
        } else {
            putProperty("paramtext", "");
        }

        NamedNodeMap procnnm = procNode.getAttributes();
        if (procnnm != null) {
            for (int i = 0; i < procnnm.getLength(); i++) {
                Attr attr = (Attr) procnnm.item(i);
                if ( !ATTR_NAME.equals(attr.getNodeName())) {
                    putProperty(attr.getNodeName(),
                                applyMacros(attr.getNodeValue()));
                }
            }
        }


        NamedNodeMap nnm = node.getAttributes();
        if (nnm != null) {
            for (int i = 0; i < nnm.getLength(); i++) {
                Attr attr = (Attr) nnm.item(i);
                if ( !ATTR_NAME.equals(attr.getNodeName())) {
                    putProperty(attr.getNodeName(),
                                applyMacros(attr.getNodeValue()));
                }
            }
        }
        try {
            if ( !processChildren(node)) {
                return false;
            }
            try {
                if ( !processChildren(procNode)) {
                    return false;
                }
            } catch (MyReturnException mre) {
                //noop
            }
        } catch (Throwable throwable) {
            popProperties();
            throw throwable;
        }
        popProperties();
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagIf(Element node) throws Throwable {
        String expr = applyMacros(node, ATTR_EXPR, (String) null);
        if (expr == null) {
            expr = applyMacros(XmlUtil.getChildText(node));
        }
        if ((expr == null) || (expr.trim().length() == 0)) {
            return error("Could not find if expression");
        }
        expr = expr.trim();
        boolean result = getInterpreter().eval(expr).toString().equals("1");

        Element thenNode      = XmlUtil.findChild(node, TAG_THEN);
        Element elseNode      = XmlUtil.findChild(node, TAG_ELSE);
        Element statementNode = (result
                                 ? thenNode
                                 : elseNode);
        if (statementNode == null) {
            if (result && (thenNode == null) && (elseNode == null)) {
                statementNode = node;
            } else {
                return true;
            }
        }
        if (statementNode != null) {
            //            pushProperties();
            try {
                if ( !processChildren(statementNode)) {
                    return false;
                }
            } catch (Throwable throwable) {
                //                popProperties();
                throw throwable;
            }
            //            popProperties();
        }
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagOutput(Element node) throws Throwable {
        if ( !XmlUtil.hasAttribute(node, ATTR_FILE)) {
            for (int i = 0; i < outputStack.size(); i++) {
                OutputInfo oi = (OutputInfo) outputStack.get(i);
                oi.process(node);
            }
            return true;
        }
        OutputInfo outputInfo = new OutputInfo(node);
        outputStack.add(outputInfo);
        pushProperties();
        try {
            if ( !processChildren(node)) {
                return false;
            }
        } catch (Throwable throwable) {
            popProperties();
            throw throwable;
        }
        popProperties();
        outputStack.remove(outputStack.size() - 1);
        outputInfo.write();
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagIsl(Element node) throws Throwable {
        debug = applyMacros(node, ATTR_DEBUG, false);
        boolean offScreen = applyMacros(node, ATTR_OFFSCREEN, true);
        //        System.err.println ("offscreen:" + offScreen);
        if ( !getIdv().getArgsManager().getIslInteractive()) {
            //            System.err.println ("setting offscreen:" + offScreen);
            getIdv().getArgsManager().setIsOffScreen(offScreen);
        }
        putProperty(PROP_OFFSCREEN,
                    (getIdv().getArgsManager().getIsOffScreen()
                     ? "1"
                     : "0"));

        //        System.err.println("setting offScreen " +         getIdv().getArgsManager().getIsOffScreen());

        return processTagGroup(node);
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagGroup(Element node) throws Throwable {
        pushProperties();
        int    loopTimes   = applyMacros(node, ATTR_LOOP, 1);
        String sleepString = applyMacros(node, ATTR_SLEEP, (String) null);
        long   sleepTime   = 0;
        if (sleepString != null) {
            sleepString = sleepString.trim();
            long   multiplier = 1000;
            String unit = StringUtil.findPattern(sleepString, "[0-9.]+(.*)$");

            if ((unit != null) && (unit.trim().length() > 0)) {
                sleepString = sleepString.substring(0,
                        sleepString.length() - unit.length());
                if (unit.equals("s")) {}
                else if (unit.equals("seconds")) {}
                else if (unit.equals("minutes")) {
                    multiplier = 60 * 1000;
                } else if (unit.equals("m")) {
                    multiplier = 60 * 1000;
                } else if (unit.equals("hours")) {
                    multiplier = 60 * 60 * 1000;
                } else if (unit.equals("h")) {
                    multiplier = 60 * 60 * 1000;
                } else {
                    return error("Unknown sleep time unit:" + unit);
                }
            }
     *
            sleepTime = (long) (multiplier
                                * new Double(sleepString).doubleValue());
        }
        for (int i = 0; i < loopTimes; i++) {
            currentLoopIndex = i;
            try {
                if ( !processChildren(node)) {
                    return false;
                }
            } catch (MyBreakException be) {
                break;
            } catch (MyContinueException ce) {}
            if ((loopTimes > 1) && (sleepTime > 0)) {
                Misc.sleep(sleepTime);
            }
        }
        popProperties();

        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagForeach(Element node) throws Throwable {
        pushProperties();
        List         allValues   = new ArrayList();
        int          numElements = 0;
        int          cnt         = 1;
        NamedNodeMap attrs       = node.getAttributes();
        if (attrs == null) {
            return error("No values in foreach tag");
        }

        for (int i = 0; i < attrs.getLength(); i++) {
            Attr   attr   = (Attr) attrs.item(i);
            String var    = attr.getNodeName();
            String values = applyMacros(attr.getNodeValue());
            List   tokens;
            //Check if it starts with file:, if so read the contents and split on new line
            if (values.startsWith("file:")) {
                String filename =
                    applyMacros(values.substring("file:".length()));
                values = IOUtil.readContents(filename, getClass()).trim();
                tokens = StringUtil.split(values, "\n");
            } else {
                tokens = StringUtil.split(values, ",");
            }

            if (allValues.size() == 0) {
                numElements = tokens.size();
            } else if (numElements != tokens.size()) {
                return error("Bad number of tokens (" + tokens.size()
                             + " should be:" + numElements
                             + ") in foreach argument:\n" + var + "="
                             + tokens);
            }
            allValues.add(new Object[] { var, tokens });
            cnt++;
        }
        for (int tokIdx = 0; tokIdx < numElements; tokIdx++) {
            for (int valueIdx = 0; valueIdx < allValues.size(); valueIdx++) {
                Object[] tuple = (Object[]) allValues.get(valueIdx);
                putProperty(tuple[0], ((List) tuple[1]).get(tokIdx));
            }
            try {
                if ( !processChildren(node)) {
                    return false;
                }
            } catch (MyBreakException be) {
                break;
            } catch (MyContinueException ce) {}
        }
        popProperties();
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagMovie(Element node) throws Throwable {
        pushProperties();
        captureMovie(null, node);
        popProperties();
        return true;
    }

    /**
     * process the given node
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagHtml(Element node) throws Throwable {

        String html = null;
        if (XmlUtil.hasAttribute(node, ATTR_FROMFILE)) {
            html = IOUtil.readContents(applyMacros(node, ATTR_FROMFILE));
        } else {
            html = XmlUtil.getChildText(node);
        }
        html = applyMacros(html);
        int   width = XmlUtil.getAttribute(node, ATTR_WIDTH, -1);
        Image image = ImageUtils.renderHtml(html, width, null, null);
        image = processImage(ImageUtils.toBufferedImage(image),
                             XmlUtil.getAttribute(node, ATTR_FILE), node,
                             getAllProperties(), null, new Hashtable());

        //        writeImageToFile(image, XmlUtil.getAttribute(node, ATTR_FILE));
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagPanel(Element node) throws Throwable {
        pushProperties();
        captureMovie(null, node);
        popProperties();
        return true;
    }


    /**
     * Parse the xml
     *
     * @param xml the xml
     *
     * @return the root
     *
     * @throws Exception On badness
     */
    private Element makeElement(String xml) throws Exception {
        return XmlUtil.getRoot(xml);
    }


    /**
     * Capture a movie and write it out. This is typically called by the jython scripting
     *
     * @param filename Movie filename
     * @param params xml parameters of the the form:  "task arg=val arg2=val; task2 arg3=val"
     *
     * @throws Exception On badness
     */
    public void writeMovie(String filename, String params) throws Exception {
        String isl = makeXmlFromString(params);

        String xml = XmlUtil.getHeader()+"\n"
                     + isl + "";
        captureMovie(applyMacros(filename), makeElement(xml));
    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagImage(Element node) throws Throwable {
        captureImage(XmlUtil.getAttribute(node, ATTR_FILE), node);
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagWait(Element node) throws Throwable {
        File f = null;
        if (XmlUtil.hasAttribute(node, ATTR_FILE)) {
            f = new File(applyMacros(node, ATTR_FILE));
        }
        double seconds = applyMacros(node, ATTR_SECONDS, 60.0);
        if ((f != null) && f.isDirectory()) {
            String patternStr = applyMacros(applyMacros(node, ATTR_PATTERN,
                                    (String) null));
            IOUtil.wait(f, patternStr, seconds);
        } else {
            if (f != null) {
                IOUtil.wait(Misc.newList(f), seconds);
            } else {
                IOUtil.wait(findFiles(node), seconds);
            }
        }
        return true;
    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagPause(Element node) throws Throwable {
        if (XmlUtil.hasAttribute(node, ATTR_EVERY)) {
            Misc.pauseEvery((int) (60 * toDouble(node, ATTR_EVERY)));
            return true;
        }
        if (XmlUtil.hasAttribute(node, ATTR_SECONDS)) {
            Misc.sleep((long) (1000 * toDouble(node, ATTR_SECONDS)));
        } else if (XmlUtil.hasAttribute(node, ATTR_MINUTES)) {
            Misc.sleep((long) (60 * 1000 * toDouble(node, ATTR_MINUTES)));
        } else if (XmlUtil.hasAttribute(node, ATTR_HOURS)) {
            Misc.sleep((long) (60 * 60 * 1000 * toDouble(node, ATTR_HOURS)));
        } else {
            updateViewManagers();
            getIdv().getIdvUIManager().waitUntilDisplaysAreDone(
                getIdv().getIdvUIManager());
        }
        return true;

    }

    /**
     * Update the view managers
     */
    protected void updateViewManagers() {
        try {
            List viewManagers = getVMManager().getViewManagers();
            for (int i = 0; i < viewManagers.size(); i++) {
                ViewManager viewManager = (ViewManager) viewManagers.get(i);
                viewManager.updateDisplayIfNeeded();
            }
        } catch (Exception exc) {
            logException("Updating view manager", exc);
        }
    }

    /**
     * Process tag display properties
     *
     * @param node  the node
     *
     * @return true if successful
     */
    protected boolean processTagDisplayproperties(Element node) {
        DisplayControlImpl display = findDisplayControl(node);
        if (display == null) {
            throw new IllegalArgumentException("Could not find display:"
                    + XmlUtil.toString(node));
        }
        Hashtable properties = getProperties(node);
        display.applyProperties(properties);
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     */

    protected boolean processTagDisplay(Element node) {
        if ( !processDisplayNode(node, null)) {
            return false;
        }
        if (applyMacros(node, ATTR_WAIT, true)) {
            pause();
        }
        updateViewManagers();
        return true;
    }



    /**
     * Process the display node. If data source is not null then use that
     * to find data choices. If null then use all loaded data sources to find
     * data choice.
     *
     * @param node Node to process
     * @param dataSource The data source. May be null.
     *
     * @return keep going
     */
    }
    private boolean processDisplayNode(Element node, DataSource dataSource) {

        //TODO:
        String     type       = applyMacros(node, ATTR_TYPE, (String) null);
        String     param      = applyMacros(node, ATTR_PARAM, (String) null);
        DataChoice dataChoice = null;
        debug("Creating display: " + type + " param:" + param);

        if ((dataSource == null)
                && XmlUtil.hasAttribute(node, ATTR_DATASOURCE)) {
            dataSource = findDataSource(node);
            if (dataSource == null) {
                return error("Failed to to find data source for display tag:"
                             + XmlUtil.toString(node));
            }
        }

        if (param != null) {
            if (dataSource != null) {
                dataChoice = dataSource.findDataChoice(param);
            } else {
                List dataSources = getIdv().getDataSources();
                for (int i = 0;
                        (i < dataSources.size()) && (dataChoice == null);
                        i++) {
                    dataSource = (DataSource) dataSources.get(i);
                    dataChoice = dataSource.findDataChoice(param);
                }
            }
            if (dataChoice == null) {
                return error("Failed to find parameter:" + param);
            }
        }
        List dataChoices = new ArrayList();
        if (dataChoice != null) {
            dataChoice = dataChoice.createClone();
            DataSelection dataSelection =
                new DataSelection(applyMacros(node, ATTR_LEVEL_FROM,
                    (String) null), applyMacros(node, ATTR_LEVEL_TO,
                        (String) null));


            processGeoSelectionTags(node, dataSelection);

            String timeString = applyMacros(node, ATTR_TIMES, (String) null);
            if (timeString != null) {
                List times = new ArrayList();
                for (String tok :
                        StringUtil.split(timeString, ",", true, true)) {
                    times.add(new Integer(tok));
                }
                dataSelection.setTimes(times);
            }
            if (XmlUtil.hasAttribute(node, ATTR_ENSEMBLES)) {
                List ensList =
                    StringUtil.parseIntegerListString(applyMacros(node,
                        ATTR_ENSEMBLES, (String) null));
                dataSelection.putProperty(
                    GridDataSource.PROP_ENSEMBLEMEMBERS, ensList);
            }
            dataChoice.setDataSelection(dataSelection);
            dataChoices.add(dataChoice);
        }

        if (type == null) {
            String bundleXml = null;
            if (XmlUtil.hasAttribute(node, ATTR_TEMPLATE)) {
                String filename = applyMacros(node, ATTR_TEMPLATE);
                try {
                    bundleXml = IOUtil.readContents(filename);
                } catch (IOException exc) {
                    return error("Could not find file: " + filename);
                }
            } else {
                Element templateNode = XmlUtil.findChild(node, TAG_TEMPLATE);
                if (templateNode != null) {
                    bundleXml = XmlUtil.getChildText(templateNode);
                }
            }
            if (bundleXml == null) {
                return error(
                    " tag does not contain type attribute or template attribute/tag");
            }
            try {
                Object obj = getIdv().getEncoderForRead().toObject(bundleXml);

                if ( !(obj instanceof DisplayControl)) {
                    return error("display template is not a DisplayControl:"
                                 + obj.getClass().getName());
                }
                DisplayControl displayControl = (DisplayControl) obj;
                displayControl.initAfterUnPersistence(getIdv(),
                        getProperties(node), dataChoices);
                getIdv().addDisplayControl(displayControl);
            } catch (Exception exc) {
                return error("Creating display", exc);
            }
        } else {
            ControlDescriptor cd = getIdv().getControlDescriptor(type);
            if (cd == null) {
                return error("Failed to find display control:" + type);
            }
            Trace.call1("ImageGenerator making display");
            getIdv().doMakeControl(dataChoices, cd, getProperties(node),
                                   null, false);
            Trace.call2("ImageGenerator making display");
        }
        return true;

    }

    /**
     * Process geo selection tags.
     *
     * @param node the node
     * @param dataSelection the data selection
     * @return true, if successful
     */
    private boolean processGeoSelectionTags(Element node,
                                            DataSelection dataSelection) {
        String strideString = applyMacros(node, ATTR_STRIDE, (String) null);
        if (strideString != null) {
            dataSelection.getGeoSelection(true).setXStride(applyMacros(node,
                    ATTR_STRIDE, 1));
            dataSelection.getGeoSelection(true).setYStride(applyMacros(node,
                    ATTR_STRIDE, 1));

        }


        String strideXString = applyMacros(node, ATTR_STRIDEX, (String) null);
        if (strideXString != null) {
            dataSelection.getGeoSelection(true).setXStride(applyMacros(node,
                    ATTR_STRIDEX, 1));

        }

        String strideYString = applyMacros(node, ATTR_STRIDEY, (String) null);
        if (strideYString != null) {
            dataSelection.getGeoSelection(true).setYStride(applyMacros(node,
                    ATTR_STRIDEY, 1));

        }
        String strideZString = applyMacros(node, ATTR_STRIDEZ, (String) null);
        if (strideZString != null) {
            dataSelection.getGeoSelection(true).setZStride(applyMacros(node,
                    ATTR_STRIDEX, 1));

        }

        String bboxString = applyMacros(node, ATTR_BBOX, (String) null);
        if (bboxString != null) {
            List toks = StringUtil.split(bboxString, ",", true, true);
            if (toks.size() != 4) {
                return error("Bad idv.data.geosubset property:" + bboxString);
            } else {
                GeoLocationInfo boundingBox =
                    new GeoLocationInfo(
                        Misc.decodeLatLon((String) toks.get(0)),
                        Misc.decodeLatLon((String) toks.get(1)),
                        Misc.decodeLatLon((String) toks.get(2)),
                        Misc.decodeLatLon((String) toks.get(3)));
                dataSelection.getGeoSelection(true).setBoundingBox(
                    boundingBox);
            }
        }
        return true;
    }


    /**
     * Process the property tag children of the given node
     *
     * @param node parent node that holds property tags
     *
     * @return properties
     */
    private Hashtable getProperties(Element node) {
        Hashtable properties = new Hashtable();
        List      nodes      = XmlUtil.findChildren(node, TAG_PROPERTY);
        for (int i = 0; i < nodes.size(); i++) {
            Element child = (Element) nodes.get(i);
            properties.put(applyMacros(child, ATTR_NAME),
                           applyMacros(child, ATTR_VALUE));
        }
        return properties;


    /**
     * Utility to print a message and return false.
     *
     * @param msg message
     *
     * @return false
     */
    protected boolean error(String msg) {
        if ( !getIdv().getArgsManager().getIsOffScreen()) {
            LogUtil.userErrorMessage(msg);
        } else {
            System.err.println(msg);
        }
        return false;
    }



    /**
     * Utility to print a message and return false.
     *
     * @param msg message
     * @param exc exception
     *
     * @return false
     */
    protected boolean error(String msg, Exception exc) {
        if ( !getIdv().getArgsManager().getIsOffScreen()) {
            LogUtil.logException(msg, exc);
        } else {
            exc.printStackTrace();
            System.err.println(msg);
        }
        return false;
    }


    /**
     * Find all of the files that are defined by contained fileset nodes.
     *
     * @param parentNode Node to process
     *
     * @return List of files
     */
    private List findFiles(Element parentNode) {
        List resultFiles = null;
        List filesets    = XmlUtil.findChildren(parentNode, TAG_FILESET);
        if (filesets.size() > 0) {
            if (resultFiles == null) {
                resultFiles = new ArrayList();
            }
            resultFiles.addAll(findFiles(filesets));
        }
        if (resultFiles == null) {
            return null;
        }
        return resultFiles;
    }


    /**
     * Find all of the files that are defined by any fileset nodes in the nodes list..
     *
     *
     * @param nodes List of nodes
     *
     * @return List of files
     */
    private List findFiles(List nodes) {
        List files = new ArrayList();
        for (int i = 0; i < nodes.size(); i++) {
            Element node = (Element) nodes.get(i);
            if (node.getTagName().equals(TAG_FILESET)) {
                String filename = applyMacros(node, ATTR_FILE, (String) null);
                if (filename != null) {
                    files.add(new File(filename));
                    continue;
                }
                File dir = new File(applyMacros(node, ATTR_DIR, "."));
                String pattern = applyMacros(applyMacros(node, ATTR_PATTERN,
                                     (String) null));
                File[] allFiles = ((pattern == null)
                                   ? dir.listFiles()
                                   : dir.listFiles(
                                       (java.io
                                           .FileFilter) new PatternFileFilter(
                                               pattern)));
                if (allFiles == null) {
                    continue;
                }
                List tmpFiles = new ArrayList();
                for (int fileIdx = 0; fileIdx < allFiles.length; fileIdx++) {
                    if ( !allFiles[fileIdx].isDirectory()) {
                        if ( !files.contains(allFiles[fileIdx])) {
                            tmpFiles.add(allFiles[fileIdx]);
                        }
                    }
                }

                String sort = applyMacros(node, ATTR_SORT, (String) null);
     * @return The value
                String sortDir = applyMacros(node, ATTR_SORTDIR,
                                             VALUE_ASCENDING);
                if (sort != null) {
                    if (sort.equals(VALUE_TIME)) {
                        if (sortDir.equals(VALUE_ASCENDING)) {
                            tmpFiles = Misc.toList(
                                IOUtil.sortFilesOnAge(
                                    IOUtil.toFiles(tmpFiles), false));
                        } else if (sortDir.equals(VALUE_DESCENDING)) {
                            tmpFiles = Misc.toList(
                                IOUtil.sortFilesOnAge(
                                    IOUtil.toFiles(tmpFiles), true));
                        } else {
                            System.err.println("unknown sort direction:"
                                    + sortDir);
                        }
                    } else {
                        System.err.println("unknown sort type:" + sort);
                    }
                }


                if (XmlUtil.hasAttribute(node, ATTR_FIRST)) {
                    int first = applyMacros(node, ATTR_FIRST, 0);
                    if (first < tmpFiles.size()) {
                        List tmp = new ArrayList();
                        for (int fileIdx = 0; fileIdx < first; fileIdx++) {
                            tmp.add(tmpFiles.get(fileIdx));
                        }
                        tmpFiles = tmp;

                    }
                } else if (XmlUtil.hasAttribute(node, ATTR_LAST)) {
                    int last = applyMacros(node, ATTR_LAST, 0);
                    if (last < tmpFiles.size()) {
                        List tmp = new ArrayList();
                        for (int fileIdx = tmpFiles.size() - 1; fileIdx >= 0;
                                fileIdx--) {
                            tmp.add(0, tmpFiles.get(fileIdx));
                            if (tmp.size() >= last) {
                                break;
                            }
                        }
                        tmpFiles = tmp;
                    }
                }
                files.addAll(tmpFiles);


            }
        }
        return files;
    }


    /**
     * Put the property in the current properties hashtable
     *
     * @param key key
     * @param value value
     */
    private void putProperty(Object key, Object value) {
        putProperty(key, value, false);
    }

    /**
     * Put the property in the current properties hashtable
     *
     * @param key key
     * @param value value
     * @param global If true put it in the base stack frame
     */
    private void putProperty(Object key, Object value, boolean global) {
        Hashtable properties = (global
                                ? (Hashtable) propertiesStack.get(0)
                                : getProperties());
        properties.put(key, value);
    }


    /**
     * Find the property table for the given key
     *
     * @param key The key
     *
     * @return The properties table. If none found then it returns the top of the stack
     */
    private Hashtable findTableFor(Object key) {
        for (int i = propertiesStack.size() - 1; i >= 0; i--) {
            Hashtable properties = (Hashtable) propertiesStack.get(i);
            if (properties.get(key) != null) {
                return properties;
            }
        }
        return getProperties();
    }

    /**
     * Find the table that contains the given property and replace it with the new value
     *
     * @param key key
     * @param value new value
     */
    private void replaceProperty(Object key, Object value) {
        findTableFor(key).put(key, value);
    }


    /**
     * Get the top most hashtable in the properties stack.
     *
     * @return Current properties hashtable.
     */
    private Hashtable getProperties() {
        if (propertiesStack.size() == 0) {
            return new Hashtable();
        }
        return (Hashtable) propertiesStack.get(propertiesStack.size() - 1);
    }


    /**
     * Add a Hashtable to the properties stack.
     *
     * @return The newly created hashtable.
     */
    private Hashtable pushProperties() {
        Hashtable properties = new Hashtable();
        propertiesStack.add(properties);
        return properties;
    }


    /**
     * Remove the top most hashtable in the properties stack
     */
    private void popProperties() {
        propertiesStack.remove(propertiesStack.size() - 1);
    }


    /**
     * utility to convert a string to a double. If the string ends with '%'
     * then return the percentage of the given base value
     *
     *
     * @param node Node to process
     * @param attr The attribute to look up
     * @param baseValue Used to handle '%'
     * @return The value
     */
    private double toDouble(Element node, String attr, double baseValue) {
        String s = applyMacros(node, attr);
        return toDouble(s, baseValue);
    }


    /**
     * utility to make a double. If the string begins with '%' then we take a percentage of the baseValue
     *
     * @param s string
     * @param baseValue Used if s is a percentage
     *
     * @return double value
     */
    private double toDouble(String s, double baseValue) {
        if (s.endsWith("%")) {
            double percent = Misc.toDouble(s.substring(0, s.length() - 1));
            return (percent / 100.0) * baseValue;
        }
        return new Double(s).doubleValue();
    }


    /**
     * Convert the attribute value of the given node to a double
     *
     * @param node Node to process
     * @param attr Attribute name
     *
     * @return double value
     */
    private double toDouble(Element node, String attr) {
        return Misc.toDouble(applyMacros(node, attr));
    }


    /**
     * Find the attribute  value of the given node. Apply the macros to it.
     *
     * @param node Node to process
     * @param attr Attribute name
     *
     * @return The value
     */
    public String applyMacros(Element node, String attr) {
        return applyMacros(XmlUtil.getAttribute(node, attr));
    }


    /**
     * If the attribute does not exist return the dflt. Else return the value.
     *
     * @param node Node to process
     * @param attr Attribute name
     * @param dflt The default value to use if the attribute does not exist
     *
     */
    public String applyMacros(Element node, String attr, String dflt) {
        return applyMacros(XmlUtil.getAttribute(node, attr, dflt));
    }


    /**
     * If the attribute does not exist return the dflt. Else return the value.
     *
     * @param node Node to process
     * @param attr Attribute name
     * @param dflt The default value to use if the attribute does not exist
     *
     * @return The value
     */
    public int applyMacros(Element node, String attr, int dflt) {
        String value = XmlUtil.getAttribute(node, attr, (String) null);
        if (value == null) {
            return dflt;
        }
        return (int) Misc.toDouble(applyMacros(value));
    }

    /**
     * If the attribute does not exist return the dflt. Else return the value.
     *
     * @param node Node to process
     * @param attr Attribute name
     * @param dflt The default value to use if the attribute does not exist
     *
     * @return The value
     */
    public boolean applyMacros(Element node, String attr, boolean dflt) {
        String value = XmlUtil.getAttribute(node, attr, (String) null);
        if (value == null) {
            return dflt;
        }
        return new Boolean(applyMacros(value)).booleanValue();
    }




    /**
     * If the attribute does not exist return the dflt. Else return the value.
     *
     * @param node Node to process
     * @param attr Attribute name
     * @param dflt The default value to use if the attribute does not exist
     *
     * @return The value
     */
    public Color applyMacros(Element node, String attr, Color dflt) {
        String value = XmlUtil.getAttribute(node, attr, (String) null);
        if (value == null) {
            return dflt;
        }
        String result = applyMacros(value);
        if (result.equals("none")) {
            return null;
        }
        return GuiUtils.decodeColor(result, dflt);
    }


    /**
     * If the attribute does not exist return the dflt. Else return the value.
     *
     * @param node Node to process
     * @param attr Attribute name
     * @param dflt The default value to use if the attribute does not exist
     *
     * @return The value
     */
    public double applyMacros(Element node, String attr, double dflt) {
        String value = XmlUtil.getAttribute(node, attr, (String) null);
        if (value == null) {
            return dflt;
        }
        return Misc.toDouble(applyMacros(value));
    }



    /**
     * Do the macro substitution
     *
     * @param s The string
     *
     * @return The expanded string
     */
    public String applyMacros(String s) {
            }
        return applyMacros(s, null);
    }

    /**
     * Merge all of the proeprties together
     *
     * @return The properties
     */
    private Hashtable getAllProperties() {
        Hashtable props = new Hashtable();
        for (int i = 0; i < propertiesStack.size(); i++) {
            Hashtable properties = (Hashtable) propertiesStack.get(i);
            props.putAll(properties);
        }
        return props;
    }


    /**
     * Do the macro substitution
     *
     * @param s The string
     * @param props Properties
     *
     * @return The expanded string
     */
    private String applyMacros(String s, Hashtable props) {
        return applyMacros(s, props, true);
    }

    /**
     * Do the macro substitution
     *
     * @param s The string
     * @param props Properties
     * @param doTime  process time macros
     *
     * @return The expanded string
     */
    private String applyMacros(String s, Hashtable props, boolean doTime) {
        if (s == null) {
            return null;
        }
        if (props == null) {
            props = new Hashtable();
        } else {
            Hashtable tmp = props;
            props = new Hashtable();
            props.putAll(tmp);
        }
        props.putAll(getAllProperties());

        putIndex(props, PROP_LOOPINDEX, currentLoopIndex);
        props.put(PROP_LOOPINDEX_PAD2,
                  StringUtil.padLeft("" + currentLoopIndex, 2, "0"));
        props.put(PROP_LOOPINDEX_PAD3,
                  StringUtil.padLeft("" + currentLoopIndex, 3, "0"));
        props.put(PROP_LOOPINDEX_PAD4,
                  StringUtil.padLeft("" + currentLoopIndex, 4, "0"));

        Date now = new Date(Misc.getCurrentTime());


        if (DATE_FORMATS == null) {
            TimeZone timeZone = TimeZone.getTimeZone("GMT");
            DATE_FORMATS = new ArrayList();
            for (int i = 0; i < DATE_PROPS.length; i++) {
                SimpleDateFormat sdf = new SimpleDateFormat(DATE_PROPS[i]);
                sdf.setTimeZone(timeZone);
                DATE_FORMATS.add(sdf);
            }
        }

        for (int i = 0; i < DATE_FORMATS.size(); i++) {
            SimpleDateFormat sdf = (SimpleDateFormat) DATE_FORMATS.get(i);
            props.put(DATE_PROPS[i], sdf.format(now));
        }


        props.put("memory", "" + Misc.usedMemory());


        /*
        if (s.indexOf("${anim:") >= 0) {
            now = getAnimationTime();
            if (now != null) {
                for (int i = 0; i < DATE_FORMATS.size(); i++) {
                    SimpleDateFormat sdf =
                        (SimpleDateFormat) DATE_FORMATS.get(i);
                    props.put("anim:" + DATE_PROPS[i], sdf.format(now));
                }

            }
            }*/


        TimeZone tz = getIdv().getPreferenceManager().getDefaultTimeZone();


        s = StringUtil.replaceDate(s, "now:", now, tz);

        if ((s.indexOf("anim:") >= 0) || (s.indexOf("time:") >= 0)) {
            Date animationTime = getAnimationTime();
            if (animationTime == null) {
                animationTime = now;
            if (doTime) {
                s = StringUtil.replaceDate(s, "anim:", animationTime, tz);
                s = StringUtil.replaceDate(s, "time:", animationTime, tz);
                s = StringUtil.replaceDate(s, "now:", now, tz);
            }
        }
        s = StringUtil.applyMacros(s, props, false);
        //Now use the idv properties
        s = StringUtil.applyMacros(s, getStateManager().getProperties(),
                                   false);
        if (s.indexOf("${") >= 0) {
            throw new BadIslException("Undefined macro in: " + s);
        }

        if (s.startsWith("jython:")) {
            Object result = getInterpreter().eval(s.substring(7));
            s = result.toString();
        }

        if (s.startsWith("interp.")) {
            Object result = getInterpreter().eval(s);
            s = result.toString();
        }
        if (s.startsWith("islInterpreter.")) {
            Object result = getInterpreter().eval(s);
            s = result.toString();
        }
        s = s.replace("\\n", "\n");
        return s;
    }




    /**
     * Capture an image from the first active view managers
     *
     * @param filename The image filename
     */
    public void captureImage(String filename) {
        try {
            captureImage(filename, null);
        } catch (Throwable exc) {
            logException("Capturing image", exc);
        }
    }


    /**
     * Put the index
     *
     * @param props  the properties
     * @param name the name
     * @param v  the index
     */
    public void putIndex(Hashtable props, String name, int v) {
        props.put(name, new Integer(v));
        props.put(name + "_alpha", getLetter(v).toLowerCase());
        props.put(name + "_ALPHA", getLetter(v).toUpperCase());
        
        props.put(name + "_ROMAN", getRoman(v).toUpperCase());
        props.put(name + "_roman", getRoman(v).toLowerCase());


    }


    /**
     * Find all view managers that are identified by the given xml node. If the node
     * does not have a "view" attribute return all view managers. Else use the view
     * attribute to find the vms. The view can be class:class name or just a name. If a name can
     * also be a regular expression
     *
     * @param node node
     *
     * @return List of view managers
     */
    private List getViewManagers(Element node) {
        List viewManagers =
            (List) getVMManager().getViewManagers();
        if ((node == null) || !XmlUtil.hasAttribute(node, ATTR_VIEW)) {
            return viewManagers;
        }

        List   goodOnes = new ArrayList();
        String viewId   = applyMacros(node, ATTR_VIEW);
        //        System.err.println ("viewManagers:" + viewManagers);
        if (viewId.startsWith("name:")) {
            viewId = viewId.substring(5);
        }

        for (int i = 0; i < viewManagers.size(); i++) {
            ViewManager viewManager = (ViewManager) viewManagers.get(i);
            if (viewId.startsWith("#")) {
                int viewIndex = new Integer(viewId.substring(1)).intValue();
                if (viewIndex == i) {
                    goodOnes.add(viewManager);
                    //                    System.err.println("\tskipping index");
                }
                continue;
            }
            if (viewId.startsWith("class:")) {
                if (StringUtil.stringMatch(viewManager.getClass().getName(),
                                           viewId.substring(6), true, true)) {
                    goodOnes.add(viewManager);
                }
                continue;
            }
            String name = viewManager.getName();
            if (name == null) {
                name = "";
            }
    }
            if (StringUtil.stringMatch(name, viewId, true, true)) {
                goodOnes.add(viewManager);
            }
        }
        if (goodOnes.size() == 0) {
            warning("Unable to find any views with id:" + viewId);
        } else {
            //            System.err.println(viewId + " " + goodOnes);
        }
        return goodOnes;
    }


    /**
     * Wait until all displays are built
     */
    public void pause() {
        getIdv().waitUntilDisplaysAreDone();
    }


    /**
     * Toggle debug
     *
     * @param v debug
     */
    public void setDebug(boolean v) {
        debug = v;
    }


    /**
     * Evaluate the given isl
     *
     * @param isl The isl
     *
     * @return success
     *
     * @throws Throwable On badness
     */
    public boolean evaluateIsl(String isl) throws Throwable {
        isl = XmlUtil.tag(TAG_GROUP, "", isl);
        return processNode(XmlUtil.getRoot(isl));
    }



    /**
     * Load the given bundle file
     *
     * @param bundleFile The bundle
     * @param setFiles This is a list, which may be null, of datasource patterns and file names to change
     *
     * @throws Throwable     On badness
     */
    public void loadBundle(String bundleFile, List setFiles)
            throws Throwable {
        loadBundle(bundleFile, setFiles, -1, -1, "", true);
    }

    /**
     * Load the given bundle file, list of datasets, width and height
     *
     * @param bundleFile The bundle
     * @param setFiles This is a list, which may be null, of datasource patterns and file names to change
     * @param width The width of the display area to use
     * @param height The height of the display are to use
     *
     * @throws Throwable  an exception
     */
    public void loadBundle(String bundleFile, List setFiles, int width,
                           int height)
            throws Throwable {
        loadBundle(bundleFile, setFiles, width, height, "", true);
    }


    /**
     * Load the given bundle file, list of datasets, width and height
     *
     * @param bundleFile The bundle
     * @param setFiles This is a list, which may be null, of datasource patterns and file names to change
     * @param width The width of the display area to use
     * @param height The height of the display are to use
     * @param times A string of times to use from the bundle file
     * @param clear If false then do not clear out the data sources and displays (which is otherwise the default)
     *
     * @throws Throwable  an exception
     */
    public void loadBundle(String bundleFile, List setFiles, int width,
                           int height, String times, boolean clear)
            throws Throwable {

        StringBuffer extra = new StringBuffer();
        if (setFiles != null) {
            for (int i = 0; i < setFiles.size(); i += 2) {
                String datasource = (String) setFiles.get(i);
                String files      = (String) setFiles.get(i + 1);
                if ((datasource != null) && (files != null)) {
                    extra.append(XmlUtil.tag(TAG_SETFILES,
                                             XmlUtil.attrs("datasource",
                                                 datasource, "file", files)));
                }
                extra.append("\n");
            }
        }
        StringBuffer attrs = new StringBuffer();
        attrs.append(" ");
        attrs.append(ATTR_FILE + "=" + quote(bundleFile));
        attrs.append(" ");
        if ((width > 0) && (height > 0)) {
            attrs.append(" ");
            attrs.append(ATTR_WIDTH + "=" + quote("" + width));
            attrs.append(" ");
            attrs.append(ATTR_HEIGHT + "=" + quote("" + height));
            attrs.append(" ");
        }
        if ((times != null) && !times.isEmpty()) {
            attrs.append(" ");
            attrs.append(ATTR_TIMES + "=" + quote("" + times));
        }
        if ( !clear) {
            attrs.append(" ");
            attrs.append(ATTR_CLEAR + "=" + quote("false"));
        }

        String xml = "" + extra + "";
        System.err.println(xml);
        processTagBundle(makeElement(xml));
    }

    /**
     * Write an Image to the specified file
     *
     * @param image Image to be written
     * @param file Name of output file (may use macros)
     *
     * @throws Exception On badness
     */
    public void writeImageToFile(Image image, String file) throws Exception {
        ImageUtils.writeImageToFile(image,
                                    applyMacros(getImageFileName(file)));
    }


    /**
     * Create XML from the input String
     *
     * @param s in the form:  "task arg=val arg2=val; task2 arg3=val"
     *
     * @return  
     *
     */
    protected static String makeXmlFromString(String s) {
        if ((s == null) || (s.length() == 0)) {
            return "";
        }
        StringTokenizer st = new StringTokenizer(s, ";");
        StringBuffer    sb = new StringBuffer();

        while (st.hasMoreTokens()) {
            String          so  = st.nextToken();
            StringTokenizer sot = new StringTokenizer(so, "=");
            int             k   = sot.countTokens();

            for (int i = 0; i < k; i++) {

                StringTokenizer sbt =
                    new StringTokenizer(sot.nextToken().trim(), " ");

                if (i == 0) {
                    sb.append("<" + sbt.nextToken().trim());
                }

                int     n      = sbt.countTokens();
                boolean gotone = false;
                while (n > 1) {
                    if (gotone) {
                        sb.append(" ");
                    }
                    sb.append(sbt.nextToken().trim());
                    n      = n - 1;
                    gotone = true;
                }

                // now deal with the last value
                if (sbt.hasMoreTokens()) {
                    if (i != k - 1) {
                        if (gotone) {
                            sb.append("\" " + sbt.nextToken().trim() + "=\"");
                        } else {
                            sb.append(" " + sbt.nextToken().trim() + "=\"");
                        }
                    } else {
                        if (gotone) {
                            sb.append(" " + sbt.nextToken().trim() + "\"");
                        } else {
                            sb.append(sbt.nextToken().trim() + "\"");
                        }
                    }
                }
            }


            sb.append(" />");
        }

        return sb.toString();
    }

    /**
     * Quote a string
     *
     * @param s  the string
     *
     * @return  the quotated string
     */
    private static String quote(String s) {
        return "\"" + s + "\"";
    }

    /**
     * Get the image of the current display and write to file. Image
     * may be modified by the params given in the form:
     *    tag1 arg=val arg2=val2; tag2 arg=val
     * where 'tag' are ISL tags.
     *
     * @param filename Output filename (may be modified by macros)
     * @param params String of parameters
     * @param qual Quality (def=1.0)
     *
     *
     * @throws Exception On badness
     * @throws Throwable On badness
     */
    public void writeImage(String filename, String params, float qual)
            throws Exception, Throwable {
        String isl = makeXmlFromString(params);
        String xml = XmlUtil.getHeader()+"\n" + isl + "";
        captureImage(applyMacros(filename), makeElement(xml));
    }


    /**
     * Get the Image of the current display
     *
     * @return the Image
     *
     * @throws Exception On badness
     */
    public Image getImage() throws Exception {
        //        updateViewManagers();
        List viewManagers = getVMManager().getViewManagers();
        for (int i = 0; i < viewManagers.size(); i++) {
            ViewManager viewManager = (ViewManager) viewManagers.get(i);
            if ( !getIdv().getArgsManager().getIsOffScreen()) {
                IdvWindow window = viewManager.getDisplayWindow();
                if (window != null) {
                    window.setLocation(50, 50);
                    viewManager.toFront();
                }
            }
            return viewManager.getMaster().getImage(false);
        }
        return null;
    }


    /**
     * Capture the image
     *
     * @param filename file
     * @param scriptingNode THe node from the isl. Possibly null.
     *
     * @throws Throwable On badness
     */
    private void captureImage(String filename, Element scriptingNode)
            throws Throwable {

        Hashtable imageProperties = new Hashtable();

        //See if we're in test mode
        if ((scriptingNode != null)
                && XmlUtil.hasAttribute(scriptingNode, "test")) {
            BufferedImage tmpImage =
                new BufferedImage(applyMacros(scriptingNode, ATTR_WIDTH,
                    300), applyMacros(scriptingNode, ATTR_HEIGHT, 300),
                          BufferedImage.TYPE_INT_RGB);
            String loopFilename = applyMacros(filename);
            lastImage = processImage((BufferedImage) tmpImage, loopFilename,
                                     scriptingNode, getAllProperties(), null,
                                     imageProperties);
            return;
        }


        List viewManagers = null;
        if ((scriptingNode != null)
                && XmlUtil.hasAttribute(scriptingNode, ATTR_DISPLAY)) {
            DisplayControlImpl display = findDisplayControl(scriptingNode);
            if (display == null) {
                throw new IllegalArgumentException("Could not find display:"
                        + XmlUtil.toString(scriptingNode));
            }
            String loopFilename = applyMacros(filename);
            String what = applyMacros(scriptingNode, ATTR_WHAT,
                                      (String) null);

            ViewManager viewManager = display.getViewManagerForCapture(what);
            if (viewManager != null) {
                viewManager.updateDisplayIfNeeded();
                viewManagers = (List) Misc.newList(viewManager);
            } else {
                lastImage = display.getImage(what);
                lastImage = processImage((BufferedImage) lastImage,
                                         loopFilename, scriptingNode,
                                         getAllProperties(), null,
                                         imageProperties);
                return;
            }
        }

        if (viewManagers == null) {
            viewManagers = (List) getViewManagers(scriptingNode);
        }

        if (viewManagers.size() == 0) {
            debug("No views to capture");
        }
        pushProperties();

        List indices = StringUtil.parseIntegerListString(
                                    XmlUtil.getAttribute(
                                        scriptingNode, ATTR_ANIMATION_INDEX,
                                        "1"));

        int idx = 0;
        for (int j = 0; j < indices.size(); j++) {
            List images = new ArrayList();
            String      fname  = (indices.size() > 1)
                                 ? fixFileName(filename, indices.get(j))
                                 : filename;
            for (int i = 0; i < viewManagers.size(); i++) {
                ViewManager viewManager = (ViewManager) viewManagers.get(i);
                viewManager.getAnimation().setCurrent(indices.get(j));
                putIndex(getProperties(), PROP_VIEWINDEX, idx);
                String name = viewManager.getName();
                if (name == null) {
                    name = "view" + idx;
                }
                getProperties().put(PROP_VIEWNAME, name);
                if ( !getIdv().getArgsManager().getIsOffScreen()) {
                    IdvWindow window = viewManager.getDisplayWindow();
                    if (window != null) {
                        window.setLocation(50, 50);
                        viewManager.toFront();
                        //                    Misc.sleep(100);
                    }
                }
                String loopFilename = applyMacros(fname);
                if (scriptingNode == null) {
                    File imageFile = null;
                    if (loopFilename != null) {
                        imageFile = new File(getImageFileName(loopFilename));
                    }
                    viewManager.writeImage(imageFile, true, false);
                } else if ((loopFilename != null)
                           && ViewManager.isVectorGraphicsFile(
                               loopFilename)) {
                    VectorGraphicsRenderer vectorRenderer =
                        new VectorGraphicsRenderer(viewManager);
                    vectorRenderer.renderTo(loopFilename);
                } else {
                    getIdv().getIdvUIManager().waitUntilDisplaysAreDone(
                        getIdv().getIdvUIManager(), 0);
                    lastImage       = viewManager.getMaster().getImage(false);
                    imageProperties = new Hashtable();
                    lastImage = processImage((BufferedImage) lastImage,
                                             loopFilename, scriptingNode,
                                             getAllProperties(), viewManager,
                                             imageProperties);
                }
                images.add(lastImage);
                idx++;
            }

            boolean combine = XmlUtil.getAttribute(scriptingNode,
                                  ATTR_COMBINE, false);
            if (combine) {
                String          combineFilename = applyMacros(filename);
                List components      = new LinkedList();
                for (ViewManager vm : viewManagers) {
                    components.add(vm.getComponent());
                }
                int cols = 2;
                if ( !getIdv().getArgsManager().getIsOffScreen()) {
                    cols = ImageUtils.getColumnCountFromComps(components);
                } else {
                    cols = XmlUtil.getAttribute(scriptingNode, ATTR_COLUMNS,
                            cols);
                }

                if (ViewManager.isVectorGraphicsFile(combineFilename)) {
                    VectorGraphicsRenderer vectorRenderer =
                        new VectorGraphicsRenderer(viewManagers, cols);
                    vectorRenderer.renderTo(combineFilename);
                } else {
                    Image i = ImageUtils.gridImages2(images, 0, Color.GRAY,
                                  cols);
                    ImageUtils.writeImageToFile(i, combineFilename);
                }
            }
        }

        popProperties();
    }

    /**
     * Fixing file name for animation indices.
     *
     * @param filename
     * @param integer
     *
     * @return The file name with an appended index.
     */
    private String fixFileName(String filename, Integer integer) {
        String[] tokens = filename.split("\\.(?=[^\\.]+$)");
        return tokens[0] + integer + "." + tokens[1];
    }

    /**
     * Resize the image
     *
     * @param image The image
     * @param node Node to process. This may contain a width or a height attribute.
     *
     * @return The resized image
     */
    protected Image resize(Image image, Element node) {
        int imageWidth  = image.getWidth(null);
        int imageHeight = image.getHeight(null);
        int width       = -1;
        int height      = -1;
        if (XmlUtil.hasAttribute(node, ATTR_WIDTH)) {
            width = (int) toDouble(node, ATTR_WIDTH, imageWidth);
        }
        if (XmlUtil.hasAttribute(node, ATTR_HEIGHT)) {
            height = (int) toDouble(node, ATTR_HEIGHT, imageWidth);
        }
        if ((width == -1) && (height == -1)) {
            return image;
        }

        return image.getScaledInstance(width, height,
                                       Image.SCALE_AREA_AVERAGING);
    }




    /**
     * Resize the image
     *
     * @param image The image
     * @param widthStr width of desired image (pixels)
     * @param heightStr height of desired image (pixels)
     *
     * @return The resized image
     */
    public BufferedImage resizeImage(BufferedImage image, String widthStr,
                                     String heightStr) {
        int imageWidth  = image.getWidth(null);
        int imageHeight = image.getHeight(null);
        int width       = -1;
        int height      = -1;
        if ( !widthStr.equals("-1")) {
            width = (int) toDouble(widthStr, imageWidth);
        }
        if ( !heightStr.equals("-1")) {
            height = (int) toDouble(heightStr, imageHeight);
        }
        if ((width == -1) && (height == -1)) {
            return image;
        }

        BufferedImage resizedImage =
            ImageUtils.toBufferedImage(image.getScaledInstance(width, height,
                Image.SCALE_AREA_AVERAGING), BufferedImage.TYPE_INT_RGB);
        return resizedImage;



    /**
     * Matte the image
     *
     * @param image The image
     * @param bgString color for the matte ("red", "green", etc)
     * @param top number of lines for the top (north) matte
     * @param left number of pixels for the left (west) matte
     * @param bottom number of lines for the bottom (south) matte
     * @param right number of pixels for the right (east) matte
     *
     * @return The matte'd image
     */
    public BufferedImage matteImage(BufferedImage image, String bgString,
                                    int top, int left, int bottom,
                                    int right) {
        Color bg = GuiUtils.decodeColor(bgString, (Color) null);
        return ImageUtils.matte(image, top, bottom, left, right, bg);
    }



    /**
     * Process the image
     *
     * @param image The image
     * @param filename File to write the image to
     * @param node Node to process
     * @param props Extra properties
     * @param viewManager The viewmanager this image came from
     * @param imageProps  the image properties
     *
     *
     * @return The processed image
     * @throws Throwable On badness
     */
    protected BufferedImage processImage(BufferedImage image,
                                         String filename, Element node,
                                         Hashtable props,
                                         ViewManager viewManager,
                                         Hashtable imageProps)
            throws Throwable {

        if (node == null) {
            return image;
        }

        if (props == null) {
            props = new Hashtable();
        }
        if (viewManager != null) {
            Animation animation = viewManager.getAnimation();
            props.put(PROP_ANIMATIONTIME, "");
            if (animation != null) {
                if (animation.getAniValue() != null) {
                    props.put(PROP_ANIMATIONTIME, animation.getAniValue());
                }
            }
        }
        getProperties().putAll(props);

        NodeList  elements       = XmlUtil.getElements(node);
        Hashtable seenColorTable = new Hashtable();
        for (int childIdx = 0; childIdx < elements.getLength(); childIdx++) {
            boolean       shouldIterateChildren = true;
            BufferedImage newImage              = null;
            int           imageWidth            = image.getWidth(null);
            int           imageHeight           = image.getHeight(null);
            Element       child = (Element) elements.item(childIdx);
            String        tagName               = child.getTagName();

            if (tagName.equals(TAG_RESIZE)) {
                newImage = ImageUtils.toBufferedImage(resize(image, child));
            } else if (tagName.equals(TAG_FILESET)) {
                //ignore
            } else if (tagName.equals(TAG_OUTPUT)) {
                processTagOutput(child);
            } else if (tagName.equals(TAG_DISPLAYLIST)) {
                if (viewManager != null) {
                    newImage = ImageUtils.toBufferedImage(image, true);
                    Graphics g = newImage.getGraphics();
                    String valign = applyMacros(child, ATTR_VALIGN,
                                        VALUE_BOTTOM);
                    Font font = getFont(child);
                    if (XmlUtil.hasAttribute(child, ATTR_MATTEBG)) {
                        int height =
                            viewManager.paintDisplayList((Graphics2D) g,
                                null, imageWidth, imageHeight,
                                valign.equals(VALUE_BOTTOM), null, font);

                        int top    = (valign.equals(VALUE_TOP)
                    }
                                      ? height
                                      : 0);
                        int bottom = (valign.equals(VALUE_BOTTOM)
                                      ? height
                                      : 0);
                        newImage = ImageUtils.matte(image, top, bottom, 0, 0,
                                applyMacros(child, ATTR_MATTEBG,
                                            Color.white));
                        g           = newImage.getGraphics();
                        imageHeight += height;
                    }

                    Color c = applyMacros(child, ATTR_COLOR, (Color) null);
                    viewManager.paintDisplayList((Graphics2D) g, null,
                            imageWidth, imageHeight,
                            valign.equals(VALUE_BOTTOM), c, font);
                }
            } else if (tagName.equals(TAG_COLORBAR)
                       || tagName.equals(TAG_KML_COLORBAR)) {
                // only do one colorbar if we are writing to kml
                Integer index = (Integer) props.get(PROP_IMAGEINDEX);
                if ((index != null) && (index.intValue() > 0)
                        && tagName.equals(TAG_KML_COLORBAR)) {
                    continue;
                }

                boolean showLines = applyMacros(child, ATTR_SHOWLINES, false);

                List controls =
                    (List) ((viewManager != null)
                        ? viewManager.getControls()
                        : new ArrayList());

                if (XmlUtil.hasAttribute(child, ATTR_DISPLAY)) {
                    DisplayControlImpl display = ((controls.size() > 0)
                            ? findDisplayControl(XmlUtil.getAttribute(child,
                                ATTR_DISPLAY), controls)
                            : findDisplayControl(child));
                    if (display == null) {
                        error("Could not find display:"
                              + XmlUtil.toString(node));
                        return null;
                    }
                    controls = Misc.newList(display);
                }

                int    width    = applyMacros(child, ATTR_WIDTH, 150);
                int    height   = applyMacros(child, ATTR_HEIGHT, 20);
                int    ticks    = applyMacros(child, ATTR_TICKMARKS, 0);
                double interval = applyMacros(child, ATTR_INTERVAL, -1.0);
                String valuesStr = applyMacros(child, ATTR_VALUES,
                                       (String) null);
                Color c = applyMacros(child, ATTR_COLOR, Color.black);

                Color lineColor = applyMacros(child, ATTR_LINECOLOR, c);

                Rectangle imageRect = new Rectangle(0, 0, imageWidth,
                                          imageHeight);

                Point pp = ImageUtils.parsePoint(applyMacros(child,
                               ATTR_PLACE, "ll,10,-10"), imageRect);
                Point ap = ImageUtils.parsePoint(applyMacros(child,
                               ATTR_ANCHOR, "ll"), new Rectangle(0, 0, width,
                                   height));

                String orientation = applyMacros(child, ATTR_ORIENTATION,
                                         VALUE_BOTTOM);
                boolean vertical = orientation.equals(VALUE_RIGHT)
                                   || orientation.equals(VALUE_LEFT);
                int     baseY       = pp.y - ap.y + (vertical
                        ? 0
                        : height);

                int     baseX       = pp.x - ap.x;

                List    colorTables = new ArrayList();
                List    ranges      = new ArrayList();
                List    units       = new ArrayList();

                boolean forKml      = tagName.equals(TAG_KML_COLORBAR);

                for (int i = 0; i < controls.size(); i++) {
                    DisplayControlImpl control =
                        (DisplayControlImpl) controls.get(i);
    /**
                    ColorTable colorTable = control.getColorTable();
                    if (colorTable == null) {
                        continue;
                    }
                    Range range = control.getRangeForColorTable();
                    //only do unique color tables
                    Object[] key = { colorTable, range };
                    if (seenColorTable.get(key) != null) {
                        continue;
                    }
                    seenColorTable.put(key, key);
                    colorTables.add(colorTable);
                    ranges.add(range);
                    units.add(control.getDisplayUnit());
                }

                for (int i = 0; i < colorTables.size(); i++) {
                    ColorTable colorTable = (ColorTable) colorTables.get(i);
                    Range      range      = (Range) ranges.get(i);
                    Unit       unit       = (Unit) units.get(i);
                    Image      imageToDrawIn;
                    if (forKml) {
                        if (vertical) {
                            baseX = 0;
                            baseY = 0;
                        } else {
                            baseX = 0;
                            baseY = height;
                        }
                        int space = applyMacros(child, ATTR_SPACE, (vertical
                                ? width
                                : height));
                        imageToDrawIn = new BufferedImage(width + (vertical
                                ? space
                                : 0), height + (vertical
                                ? 0
                                : space), BufferedImage.TYPE_INT_RGB);
                    } else {
                        imageToDrawIn = newImage =
                            ImageUtils.toBufferedImage(image);
                    }
                    Graphics g = imageToDrawIn.getGraphics();
                    if (forKml) {
                        Color bgColor = applyMacros(child, ATTR_BACKGROUND,
                                            Color.white);
                        g.setColor(bgColor);
                        g.fillRect(0, 0, imageToDrawIn.getWidth(null),
                                   imageToDrawIn.getHeight(null));
                    }
                    ColorPreview preview =
                        new ColorPreview(
                            new BaseRGBMap(colorTable.getNonAlphaTable()),
                            (vertical
                             ? width
                             : height));
                    if (vertical) {
                        preview.setSize(new Dimension(height, width));
                    } else {
                        preview.setSize(new Dimension(width, height));
                    }
                    Image previewImage = GuiUtils.getImage(preview);
                    boolean includeAlpha = applyMacros(child, ATTR_TRANSPARENCY,
                                               true);
                    previewImage = ColorTableCanvas.getImage(colorTable,
                            (vertical
                             ? height
                             : width), (vertical
                                        ? width
                                        : height), includeAlpha);


                    if (vertical) {
                        BufferedImage tmpImage =
                            new BufferedImage(width, height,
                                BufferedImage.TYPE_INT_RGB);
                        BufferedImage tmpImagexxx =
                            new BufferedImage(500, 500,
                                BufferedImage.TYPE_INT_RGB);
                        Graphics2D tmpG = (Graphics2D) tmpImage.getGraphics();
                        tmpG.setColor(Color.red);
                        tmpG.fillRect(0, 0, 1000, 1000);
                        tmpG.rotate(Math.toRadians(90.0));
                        tmpG.drawImage(previewImage, 0, 0 - width, null);
                        previewImage = tmpImage;
                    if (forKml) {
                        g.drawImage(previewImage, 0, 0, null);
                    } else {
                        g.drawImage(previewImage, baseX, (vertical
                                ? baseY
                                : baseY - height), null);
                    }
                    if (showLines) {
                        g.setColor(lineColor);
                        g.drawRect(baseX, (vertical
                                           ? baseY
                                           : baseY - height), width - 1,
                                           height - (vertical
                                ? 1
                                : 0));
                    }
                    setFont(g, child);
                    FontMetrics fm     = g.getFontMetrics();
                    List        values = new ArrayList();
                    String suffixFrequency = XmlUtil.getAttribute(child,
                                                 ATTR_SUFFIXFREQUENCY,
                                                 XmlUtil.getAttribute(child,
                                                     ATTR_SHOWUNIT,
                                                     "false")).toLowerCase();
                    String unitDefault = ( !suffixFrequency.equals("false"))
                                         ? " %unit%"
                                         : "";
                    String labelSuffix = applyMacros(child, ATTR_SUFFIX,
                                             unitDefault);
                    if (unit != null) {
                        labelSuffix = labelSuffix.replace("%unit%",
                                "" + unit);
                    } else {
                        labelSuffix = labelSuffix.replace("%unit%", "");
                    }
                    if (valuesStr != null) {
                        double[] valueArray = Misc.parseDoubles(valuesStr,
                                                  ",");
                        for (int valueIdx = 0; valueIdx < valueArray.length;
                                valueIdx++) {
                            values.add(new Double(valueArray[valueIdx]));
                        }
                    } else if (ticks > 0) {
                        int spacing = ((ticks == 1)
                                       ? 0
                                       : (vertical
                                          ? height
                                          : width) / (ticks - 1));
                        for (int tickIdx = 0; tickIdx < ticks; tickIdx++) {
                            double percent = ((ticks > 1)
                                    ? (double) tickIdx / (double) (ticks - 1)
                                    : 0.0);
                            values.add(
                                new Double(range.getValueOfPercent(percent)));
                        }
                    } else if (interval > 0) {
                        double value = range.getMin();
                        double max   = range.getMax();
                        while (value <= max) {
                            values.add(new Double(value));
                            value += interval;
                        }
                    }
                    for (int valueIdx = 0; valueIdx < values.size();
                            valueIdx++) {
                        double value =
                            ((Double) values.get(valueIdx)).doubleValue();
                        int x;
                        int y;
                        if (vertical) {
                            if (orientation.equals(VALUE_RIGHT)) {
            try {
                                x = baseX + width;
                            } else {
                                x = baseX;
                            }
                            y = baseY
                                + (int) (range.getPercent(value) * height);
                            if (y > baseY + height) {
                                break;
                            }
                        } else {
                            if (orientation.equals(VALUE_BOTTOM)) {
                                y = baseY;
                            } else {
                                y = baseY - height;
                            }

                            if (range != null) {
                                x = baseX
                                    + (int) (range.getPercent(value) * width);
                            } else {
                                x = baseX;
                            }

                            if (x > baseX + width) {
                                break;
                            }
                        }
                        String tickLabel =
                            getIdv().getDisplayConventions().format(value);
                        if (suffixFrequency.equals(VALUE_LAST)
                                && (valueIdx == values.size() - 1)) {
                            tickLabel += labelSuffix;
                        } else if (suffixFrequency.equals(VALUE_FIRST)
                                   && (valueIdx == 0)) {
                            tickLabel += labelSuffix;
                        } else if (suffixFrequency.equals(VALUE_ALL)
                                   || suffixFrequency.equals("true")) {
                            tickLabel += labelSuffix;
                        }


                        Rectangle2D rect = fm.getStringBounds(tickLabel, g);
                        g.setColor(lineColor);
                        if (orientation.equals(VALUE_RIGHT)) {
                            g.drawLine(x + 1, y, x, y);
                            if (showLines) {
                                g.drawLine(x, y, x - width, y);
                            }
                        } else if (orientation.equals(VALUE_LEFT)) {
                            g.drawLine(x - 1, y, x, y);
                            if (showLines) {
                                g.drawLine(x, y, x + width, y);
                            }
                        } else if (orientation.equals(VALUE_BOTTOM)) {
                            g.drawLine(x, y + 1, x, y);
                            if (showLines) {
                                g.drawLine(x, y, x, y - height);
                            }
                        } else {
                            g.drawLine(x, y - 1, x, y);
                            if (showLines) {
                                g.drawLine(x, y, x, y + height);
                            }
                        }
                        g.setColor(c);
                        if (orientation.equals(VALUE_RIGHT)) {
                            int yLoc = y + (int) (rect.getHeight() / 2) - 2;
                            if (forKml) {
                                if (valueIdx == 0) {
                                    yLoc = y + (int) (rect.getHeight()) - 2;
                                } else if (valueIdx == values.size() - 1) {
                                    yLoc = y - (int) (rect.getHeight()) + 6;
                                }
                            }
                            g.drawString(tickLabel, x + 2, yLoc);
                        } else if (orientation.equals(VALUE_LEFT)) {
                            int xLoc = x - 2 - (int) rect.getWidth();
                            g.drawString(tickLabel, xLoc,
                                         y + (int) (rect.getHeight() / 2)
                                         - 2);
                        } else if (orientation.equals(VALUE_BOTTOM)) {
                            int xLoc = x - (int) (rect.getWidth() / 2);
                            if (forKml) {
                                if (valueIdx == 0) {
                                    xLoc = x + 2;
                                } else if (valueIdx == values.size() - 1) {
                                    xLoc = x - (int) rect.getWidth() + 2;
                                }
                            }
                            g.drawString(tickLabel, xLoc,
                                         y + (int) rect.getHeight() + 2);
                        } else {
                            g.drawString(tickLabel,
                                         x - (int) (rect.getWidth() / 2),
                                         y - 2);
                        }
                    }
                    if (vertical) {
                        baseX += width + 30;
                    } else {
                        baseY += height + 30;
                    }
                    if (forKml) {
                        String tmpImageFile =
                            applyMacros(
                                child, ATTR_FILE,
                                getIdv().getStore().getTmpFile(
                                    "testcolorbar${viewindex}.png"));
                        String template =
                            "${kml.name}${icon}\n"
                            + "\n"
                            + "\n"
                            + "\n"
                            + "\n";
                        String[] macros = {
                            "kml.name", "kml.overlayXY.x", "kml.overlayXY.y",
                            "kml.overlayXY.xunits", "kml.overlayXY.yunits",
                            "kml.screenXY.x", "kml.screenXY.y",
                            "kml.screenXY.xunits", "kml.screenXY.yunits",
                            "kml.size.x", "kml.size.y", "kml.size.xunits",
                            "kml.size.yunits"
                        };
                        String[] macroValues = {
                            "", "0", "1", "fraction", "fraction", "0", "1",
                            "fraction", "fraction", "-1", "-1", "pixels",
                            "pixels"
                        };

                        for (int macroIdx = 0; macroIdx < macros.length;
                                macroIdx++) {
                            template =
                                template.replace("${" + macros[macroIdx]
                                    + "}", applyMacros(child,
                                        macros[macroIdx],
                                        macroValues[macroIdx]));
                        }
                        template = template.replace("${icon}",
                                IOUtil.getFileTail(tmpImageFile));
                        imageProps.put("kml", template);
                        List kmlFiles = (List) imageProps.get("kmlfiles");
                        //TODO: Only do the first one for now
                        if (kmlFiles == null) {
                            kmlFiles = new ArrayList();
                            imageProps.put("kmlfiles", kmlFiles);
                        }
                        kmlFiles.add(tmpImageFile);

                        //                        System.out.println(template);
                        ImageUtils.writeImageToFile(imageToDrawIn,
                                tmpImageFile);
                    }
                }


            } else if (tagName.equals(TAG_TRANSPARENT)
                       || tagName.equals(TAG_BGTRANSPARENT)) {
                Color c = null;
                if (tagName.equals(TAG_BGTRANSPARENT)) {
                    c = viewManager.getBackground();
                } else {
                    c = applyMacros(child, ATTR_COLOR, (Color) null);
                }
                //                System.err.println ("c:" + c);
                int[] redRange   = { 0, 0 };
                int[] greenRange = { 0, 0 };
                int[] blueRange  = { 0, 0 };
                if (c != null) {
                    //                    System.err.println("got color");
                    redRange[0]   = redRange[1] = c.getRed();
                    greenRange[0] = greenRange[1] = c.getGreen();
                    blueRange[0]  = blueRange[1] = c.getBlue();
                } else {}
                newImage = ImageUtils.makeColorTransparent(image, redRange,
                        greenRange, blueRange);
            } else if (tagName.equals(TAG_SHOW)) {
                JComponent contents = new JLabel(new ImageIcon(image));
                String message = applyMacros(child, ATTR_MESSAGE,
                                             (String) null);
                if (message != null) {
                    contents = GuiUtils.topCenter(new JLabel(message),
                            contents);
                }
                if ( !GuiUtils.askOkCancel("Continue?", contents)) {
                    throw new MyQuitException();
                }
            } else if (tagName.equals(TAG_MATTE)) {
                newImage = doMatte(image, child, 0);
            } else if (tagName.equals(TAG_LATLONLABELS)) {
                newImage = doLatLonLabels(child, viewManager, image,
                                          imageProps);
            } else if (tagName.equals(TAG_WRITE)) {
                ImageUtils.writeImageToFile(
                    image, getImageFileName(applyMacros(child, ATTR_FILE)));

            } else if (tagName.equals(TAG_PUBLISH)) {
                getIdv().getPublishManager().publishIslImage(this, node,
                        image);
            } else if (tagName.equals(TAG_CLIP)) {
                int[] ul;
                int[] lr;
                if (XmlUtil.hasAttribute(child, ATTR_DISPLAY)) {
                    //                    System.err.println("Clipping from display");
                    DisplayControlImpl dc = findDisplayControl(child);
                    if (dc == null) {
                        throw new IllegalArgumentException(
                            "Could not find display:"
                            + XmlUtil.toString(node));
                    }
                    NavigatedDisplay display =
                        (NavigatedDisplay) viewManager.getMaster();
                    MapProjection mapProjection = dc.getDataProjection();
                    java.awt.geom.Rectangle2D rect =
                        mapProjection.getDefaultMapArea();
                    LatLonPoint llplr =
                        mapProjection.getLatLon(new double[][] {
                        { rect.getX() + rect.getWidth() },
                        { rect.getY() + rect.getHeight() }
                    });
                    LatLonPoint llpul =
                        mapProjection.getLatLon(new double[][] {
                        { rect.getX() }, { rect.getY() }
                    });
                    EarthLocation ulEl = new EarthLocationTuple(llpul,
                                             new Real(RealType.Altitude, 0));
                    EarthLocation lrEl = new EarthLocationTuple(llplr,
                                             new Real(RealType.Altitude, 0));
                    ul = display.getScreenCoordinates(
                        display.getSpatialCoordinates(ulEl, null));
                    lr = display.getScreenCoordinates(
                        display.getSpatialCoordinates(lrEl, null));
                    //System.err.println("ul:" + ulEl + " lr:" + lrEl);
                    if (ul[0] > lr[0]) {
                        int tmp = ul[0];
                        ul[0] = lr[0];
                        lr[0] = tmp;
                    }
                    if (ul[1] > lr[1]) {
                        int tmp = ul[1];
                        ul[1] = lr[1];
                        lr[1] = tmp;
                    }
                    imageProps.put(ATTR_NORTH,
                                   new Double(ulEl.getLatitude().getValue()));
                    imageProps.put(
                        ATTR_WEST,
                        new Double(ulEl.getLongitude().getValue()));
                    imageProps.put(ATTR_SOUTH,
                                   new Double(lrEl.getLatitude().getValue()));
                    imageProps.put(
                        ATTR_EAST,
                        new Double(lrEl.getLongitude().getValue()));
                } else if ((viewManager != null)
                           && XmlUtil.hasAttribute(child, ATTR_NORTH)) {
                    NavigatedDisplay display =
                        (NavigatedDisplay) viewManager.getMaster();
                    EarthLocation el1 =
                        DisplayControlImpl.makeEarthLocation(toDouble(child,
                            ATTR_NORTH), toDouble(child, ATTR_WEST), 0);
                    EarthLocation el2 =
                        DisplayControlImpl.makeEarthLocation(toDouble(child,
                            ATTR_SOUTH), toDouble(child, ATTR_EAST), 0);
                    ul = display.getScreenCoordinates(
                        display.getSpatialCoordinates(el1, null));
                    lr = display.getScreenCoordinates(
                        display.getSpatialCoordinates(el2, null));
                    imageProps.put(ATTR_NORTH,
                                   new Double(el1.getLatitude().getValue()));
                    imageProps.put(ATTR_WEST,
                                   new Double(el1.getLongitude().getValue()));
                    imageProps.put(ATTR_SOUTH,
                                   new Double(el2.getLatitude().getValue()));
                    imageProps.put(ATTR_EAST,
                                   new Double(el2.getLongitude().getValue()));
                } else if (XmlUtil.hasAttribute(child, ATTR_LEFT)) {
                    ul = new int[] {
                        (int) toDouble(child, ATTR_LEFT, imageWidth),
                        (int) toDouble(child, ATTR_TOP, imageHeight) };
                    lr = new int[] {
                        (int) toDouble(child, ATTR_RIGHT, imageWidth),
                        (int) toDouble(child, ATTR_BOTTOM, imageHeight) };
                } else if (viewManager != null) {
                    //TODO: Clip on visad coordinates
                    NavigatedDisplay display =
                        (NavigatedDisplay) viewManager.getMaster();
                    ul = display.getScreenCoordinates(new double[] { -1, 1,
                            0 });
                    lr = display.getScreenCoordinates(new double[] { 1, -1,
                            0 });
                    int space  = applyMacros(child, ATTR_SPACE, 0);
                    int hspace = applyMacros(child, ATTR_HSPACE, space);
                    int vspace = applyMacros(child, ATTR_VSPACE, space);
                    ul[0] -= applyMacros(child, ATTR_SPACE_LEFT, hspace);
                    ul[1] -= applyMacros(child, ATTR_SPACE_TOP, vspace);
                    lr[0] += applyMacros(child, ATTR_SPACE_RIGHT, hspace);
                    lr[1] += applyMacros(child, ATTR_SPACE_BOTTOM, vspace);
                } else {
                    continue;
                }


                for (String attr :
                        (List) Misc.newList(ATTR_NORTH, ATTR_SOUTH,
                            ATTR_EAST, ATTR_WEST)) {
                    String kmlAttr = "kml." + attr;
                    if (XmlUtil.hasAttribute(child, kmlAttr)) {
                        imageProps.put(attr,
                                       new Double(applyMacros(child, kmlAttr,
                                           0.0)));
                    }
                }



                ul[0]    = Math.max(0, ul[0]);
                ul[1]    = Math.max(0, ul[1]);

                lr[0]    = Math.min(lr[0], imageWidth);
                lr[1]    = Math.min(lr[1], imageHeight);


                newImage = ImageUtils.clip(image, ul, lr);
            } else if (tagName.equals(TAG_SPLIT)) {
                shouldIterateChildren = false;
                int    width  = image.getWidth(null);
                int    height = image.getHeight(null);
                int    cols   = applyMacros(child, ATTR_COLUMNS, 2);
                int    rows   = applyMacros(child, ATTR_ROWS, 2);
                String file   = applyMacros(child, ATTR_FILE);
                int    cnt    = 0;
                int    hSpace = width / cols;
                int    vSpace = height / rows;
                for (int row = 0; row < rows; row++) {
                    for (int col = 0; col < cols; col++) {
                        pushProperties();
                        Hashtable myprops = new Hashtable();
                        putProperty("row", new Integer(row));
                        putProperty("column", new Integer(col));
                        putProperty("count", new Integer(++cnt));
                        String realFile = applyMacros(file, myprops);
                        Image splitImage = image.getSubimage(hSpace * col,
                                               vSpace * row, hSpace, vSpace);
                        processImage(ImageUtils.toBufferedImage(splitImage),
                                     realFile, child, myprops, viewManager,
                                     new Hashtable());
                        popProperties();
                    }
                }
            } else if (tagName.equals(TAG_THUMBNAIL)) {
                shouldIterateChildren = false;
                BufferedImage thumbImage =
                    ImageUtils.toBufferedImage(resize(image, child));
                String thumbFile = applyMacros(child, ATTR_FILE,
                                       (String) null);
                if (thumbFile == null) {
                    thumbFile = IOUtil.stripExtension(filename) + "_thumb"
                                + IOUtil.getFileExtension(filename);
                }
                processImage(thumbImage, thumbFile, child, null, viewManager,
                             new Hashtable());
            } else if (tagName.equals(TAG_KML)) {
                //NOOP
            } else if (tagName.equals(TAG_KMZFILE)) {
                //NOOP
            } else if (tagName.equals(TAG_OVERLAY)) {
                double transparency = applyMacros(child, ATTR_TRANSPARENCY,
                                          0.0);
                Graphics2D g = (Graphics2D) image.getGraphics();
                String imagePath = applyMacros(child, ATTR_IMAGE,
                                       (String) null);

                Rectangle imageRect = new Rectangle(0, 0, imageWidth,
                                          imageHeight);
                Point pp = ImageUtils.parsePoint(applyMacros(child,
                               ATTR_PLACE, "lr,-10,-10"), imageRect);
                String text = applyMacros(child, ATTR_TEXT, (String) null);
                Color  bg = applyMacros(child, ATTR_BACKGROUND, (Color) null);
                if (text != null) {
                    double angle = Math.toRadians(applyMacros(child,
                                       ATTR_ANGLE, 0.0));
                    text = applyMacros(text);
                    Color c = applyMacros(child, ATTR_COLOR, Color.white);
                    if ((c != null) && (transparency > 0)) {
                        c = new Color(c.getRed(), c.getGreen(), c.getBlue(),
                                      ImageUtils.toAlpha(transparency));
                    }
                    //Color bg = applyMacros(child, ATTR_BACKGROUND,
                    //                       (Color) null);
                    if ((bg != null) && (transparency > 0)) {
                        bg = new Color(bg.getRed(), bg.getGreen(),
                                       bg.getBlue(),
                                       ImageUtils.toAlpha(transparency));
                    }
                    setFont(g, child);
                    FontMetrics fm     = g.getFontMetrics();
                    Rectangle2D rect   = fm.getStringBounds(text, g);
                    int         width  = (int) rect.getWidth();
                    int         height = (int) (rect.getHeight());

                    Point ap = ImageUtils.parsePoint(applyMacros(child,
                                   ATTR_ANCHOR,
                                   "lr,-10,-10"), new Rectangle(0, 0, width,
                                       height));

                    g.rotate(angle);

                    if (bg != null) {
                        g.setColor(bg);
                        g.fillRect(pp.x - ap.x - 1, pp.y - ap.y - 1,
                wait();
                                   (int) width + 2, (int) height + 2);
                    }
                    g.setColor(c);
                    g.drawString(text, pp.x - ap.x, pp.y - ap.y + height);
                }

                if (imagePath != null) {
                    Image overlay = ImageUtils.readImage(imagePath);
                    if (overlay != null) {
                        if (transparency > 0) {
                            overlay = ImageUtils.setAlpha(overlay,
                                    transparency);
                        }
                        int width  = overlay.getWidth(null);
                        int height = overlay.getHeight(null);
                        Point ap = ImageUtils.parsePoint(applyMacros(child,
                                       ATTR_ANCHOR,
                                       "lr,-10,-10"), new Rectangle(0, 0,
                                           width, height));
                        g.drawImage(overlay, pp.x - ap.x, pp.y - ap.y, bg,
                                    null);
                    }
                }
            } else {
                error("Unknown tag:" + tagName);
            }
            if (newImage != null) {
                String newFileName = applyMacros(child, ATTR_FILE,
                                         (String) null);
                if (shouldIterateChildren) {
                    newImage = processImage(newImage, newFileName, child,
                                            null, viewManager,
                                            new Hashtable());
                }
                if (newFileName != null) {
                    ImageUtils.writeImageToFile(newImage,
                            getImageFileName(newFileName));
                    debug("Writing image:" + newFileName);
                }
                if ( !applyMacros(child, ATTR_COPY, false)) {
                    image = newImage;
                }
            }
        }


        if (filename != null) {
            float quality = (float) applyMacros(node, ATTR_QUALITY, 1.0);
            List fileToks = (List) StringUtil.split(filename,
                                        ",", true, true);
            for (String file : fileToks) {
                file = getImageFileName(file);
                debug("Writing image:" + file);
                if (file.endsWith(FileManager.SUFFIX_KMZ)) {
                    GeoLocationInfo bounds = null;
                    if (viewManager != null) {
                        bounds = viewManager.getVisibleGeoBounds();
                        ImageSequenceGrabber.subsetBounds(bounds, imageProps);
                        String tail = IOUtil.getFileTail(file);
                        String tmpImageFile =
                            getIdv().getStore().getTmpFile(tail + ".png");
                        ImageUtils.writeImageToFile(image, tmpImageFile,
                                quality);
                        ImageWrapper imageWrapper =
                            new ImageWrapper(tmpImageFile, null, bounds,
                                             null);
                        imageWrapper.setProperties(imageProps);
                        new ImageSequenceGrabber(
                            file, getIdv(), this, node,
                            (List) Misc.newList(imageWrapper),
                            null, 1);
                    }
                } else {
                    ImageUtils.writeImageToFile(image, file, quality);
                }
            }
        }
        return image;
    }


    /**
     * Get the insets
     *
     * @param child  the element
     * @param dflt   the default value
     *
     * @return the Insets
     */
    public Insets getInsets(Element child, int dflt) {
        int space  = applyMacros(child, ATTR_SPACE, dflt);
        int hspace = applyMacros(child, ATTR_HSPACE, space);
        int vspace = applyMacros(child, ATTR_VSPACE, space);
        int top    = applyMacros(child, ATTR_TOP, vspace);
        int bottom = applyMacros(child, ATTR_BOTTOM, vspace);
        int left   = applyMacros(child, ATTR_LEFT, hspace);
        int right  = applyMacros(child, ATTR_RIGHT, hspace);
        return new Insets(top, left, bottom, right);
    }


     * Process the lat/lon labels tag
     *
     * @param child  the XML
     * @param viewManager  the associated view manager
     * @param image  the image to draw on
     * @param imageProps  the image properties
     *
     * @return  a new image
     *
     * @throws Exception  on badness
     */
    public BufferedImage doLatLonLabels(Element child,
                                        ViewManager viewManager,
                                        BufferedImage image,
                                        Hashtable imageProps)
            throws Exception {

        if (viewManager == null) {
            throw new IllegalArgumentException("Tag " + TAG_LATLONLABELS
                    + " requires a view");
        }
        if ( !(viewManager instanceof MapViewManager)) {
            throw new IllegalArgumentException("Tag " + TAG_LATLONLABELS
                    + " requires a map view");
        }
        MapViewManager   mvm     = (MapViewManager) viewManager;
        NavigatedDisplay display = (NavigatedDisplay) viewManager.getMaster();
        DecimalFormat format = new DecimalFormat(applyMacros(child,
                                   ATTR_FORMAT, "##0.0"));
        Color color     = applyMacros(child, ATTR_COLOR, Color.red);
        Color lineColor = applyMacros(child, ATTR_LINECOLOR, color);
        Color bg = applyMacros(child, ATTR_LABELBACKGROUND, (Color) null);

        double[] latValues = Misc.parseDoubles(applyMacros(child,
                                 ATTR_LAT_VALUES, ""));
        List latLabels = StringUtil.split(applyMacros(child,
                                     ATTR_LAT_LABELS, ""), ",", true, true);
        double[] lonValues = Misc.parseDoubles(applyMacros(child,
                                 ATTR_LON_VALUES, ""));
        List lonLabels = StringUtil.split(applyMacros(child,
                                     ATTR_LON_LABELS, ""), ",", true, true);

        boolean drawLonLines = applyMacros(child, ATTR_DRAWLONLINES, false);
        boolean drawLatLines = applyMacros(child, ATTR_DRAWLATLINES, false);
        boolean       showTop    = applyMacros(child, ATTR_SHOWTOP, false);
        boolean       showBottom = applyMacros(child, ATTR_SHOWBOTTOM, true);
        boolean       showLeft   = applyMacros(child, ATTR_SHOWLEFT, true);
        boolean       showRight  = applyMacros(child, ATTR_SHOWRIGHT, false);

        int           width      = image.getWidth(null);
        int           height     = image.getHeight(null);
        int           centerX    = width / 2;
        int           centerY    = height / 2;
        EarthLocation nw, ne, se, sw;

        //don: this  what I added
        Double north = (Double) imageProps.get(ATTR_NORTH);
        Double south = (Double) imageProps.get(ATTR_SOUTH);
        Double east  = (Double) imageProps.get(ATTR_EAST);
        Double west  = (Double) imageProps.get(ATTR_WEST);
        //Assume if we have one we have them all
        if (north != null) {
            nw = DisplayControlImpl.makeEarthLocation(north.doubleValue(),
                    west.doubleValue(), 0);

            ne = DisplayControlImpl.makeEarthLocation(north.doubleValue(),
                    east.doubleValue(), 0);
            sw = DisplayControlImpl.makeEarthLocation(south.doubleValue(),
                    west.doubleValue(), 0);
            se = DisplayControlImpl.makeEarthLocation(south.doubleValue(),
                    east.doubleValue(), 0);

        } else {
        } else {
            nw = display.screenToEarthLocation(0, 0);
            ne = display.screenToEarthLocation(width, 0);
            se = display.screenToEarthLocation(0, height);
            sw = display.screenToEarthLocation(width, height);
        }


        double widthDegrees = ne.getLongitude().getValue()
                              - nw.getLongitude().getValue();
        double heightDegrees = ne.getLatitude().getValue()
                               - se.getLatitude().getValue();

        Insets insets = getInsets(child, 0);
        int    delta  = 2;
        int    bgPad  = 1;

        image = doMatte(image, child, 0);

        Graphics2D g = (Graphics2D) image.getGraphics();
        g.setFont(getFont(child));
        FontMetrics fm            = g.getFontMetrics();

        int lineOffsetRight = applyMacros(child, ATTR_LINEOFFSET_RIGHT, 0);
        int lineOffsetLeft = applyMacros(child, ATTR_LINEOFFSET_LEFT, 0);
        int         lineOffsetTop = applyMacros(child, ATTR_LINEOFFSET_TOP,
                                        0);
        int lineOffsetBottom = applyMacros(child, ATTR_LINEOFFSET_BOTTOM, 0);



        Stroke      lineStroke;
        if (XmlUtil.hasAttribute(child, ATTR_DASHES)) {
            lineStroke = new BasicStroke((float) applyMacros(child,
                    ATTR_LINEWIDTH, 1.0), BasicStroke.CAP_BUTT,
                                          BasicStroke.JOIN_BEVEL, 1.0f,
                                          Misc.parseFloats(applyMacros(child,
                                              ATTR_DASHES, "8")), 0.0f);
        } else {
            lineStroke = new BasicStroke((float) applyMacros(child,
                    ATTR_LINEWIDTH, 1.0));
        }

        g.setStroke(lineStroke);
        double leftLon  = nw.getLongitude().getValue(CommonUnit.degree);
        double rightLon = ne.getLongitude().getValue(CommonUnit.degree);
        Range  lonRange = new Range(leftLon, rightLon);

        for (int i = 0; i < lonValues.length; i++) {
            double lon = GeoUtils.normalizeLongitude(lonRange, lonValues[i]);
            double percent = (lon - nw.getLongitude().getValue())
                             / widthDegrees;
            //            if(percent<0 || percent>1) continue;
            String label;
            if (i < lonLabels.size()) {
                label = lonLabels.get(i);
            } else {
                label = format.format(lonValues[i]);
            }
            Rectangle2D rect  = fm.getStringBounds(label, g);
            int         baseX = insets.left + (int) (percent * width);
            int         x     = baseX - (int) rect.getWidth() / 2;
            int         topY;
            if (insets.top == 0) {
                topY = (int) rect.getHeight() + delta;
            } else {
                topY = insets.top - delta;
            }
            if (drawLonLines) {
                g.setColor(lineColor);
                g.drawLine(baseX, insets.top + lineOffsetTop, baseX,
                           insets.top + height - lineOffsetBottom);
            }

            if (showTop) {
                if (bg != null) {
                    g.setColor(bg);
                    g.fillRect(x - bgPad,
                               topY - (int) rect.getHeight() - bgPad,
                               (int) rect.getWidth() + bgPad * 2,
                               (int) rect.getHeight() + bgPad * 2);
                }
                g.setColor(color);
                g.drawString(label, x, topY);
            }
            int bottomY;
            if (insets.bottom == 0) {
                bottomY = insets.top + height - delta;
            } else {
                bottomY = insets.top + height + (int) rect.getHeight()
                          + delta;
            }
            if (showBottom) {
                if (bg != null) {
                    g.setColor(bg);
                    g.fillRect(x - bgPad,
                               bottomY - (int) rect.getHeight() - bgPad,
                               (int) rect.getWidth() + bgPad * 2,
                               (int) rect.getHeight() + bgPad * 2);
                }
                g.setColor(color);
                g.drawString(label, x, bottomY);
            }
        }


        for (int i = 0; i < latValues.length; i++) {
            double lat = latValues[i];
            double percent = 1.0
                             - (lat - se.getLatitude().getValue())
                               / heightDegrees;
            int baseY = insets.top + (int) (percent * height);
            //            if(percent<0 || percent>1) continue;
            String label;
            if (i < latLabels.size()) {
                label = latLabels.get(i);
            } else {
                label = format.format(lat);
            }
            Rectangle2D rect = fm.getStringBounds(label, g);
            int         y    = baseY + (int) rect.getHeight() / 2;
            int         leftX;
            if (insets.left == 0) {
                leftX = 0 + delta;
            } else {
                leftX = insets.left - (int) rect.getWidth() - delta;
            }
            if (drawLonLines) {
                g.setColor(lineColor);
                g.drawLine(insets.left + lineOffsetRight, baseY,
                           insets.left + width - lineOffsetRight, baseY);
            }

            if (showLeft) {
                if (bg != null) {
                    g.setColor(bg);
                    g.fillRect(leftX - bgPad,
                               y - (int) rect.getHeight() - bgPad,
                               (int) rect.getWidth() + bgPad * 2,
                               (int) rect.getHeight() + bgPad * 2);
                }
                g.setColor(color);
                g.drawString(label, leftX, y);
            }

            if (insets.right == 0) {
                leftX = insets.left + width - (int) rect.getWidth() - delta;
            } else {
                leftX = insets.left + width + delta;
            }
            if (showRight) {
                if (bg != null) {
                    g.setColor(bg);
                    g.fillRect(leftX - bgPad,
                               y - (int) rect.getHeight() - bgPad,
                               (int) rect.getWidth() + bgPad * 2,
                               (int) rect.getHeight() + bgPad * 2);
                }
                g.setColor(color);
                g.drawString(label, leftX, y);
            }
        }

        return image;

    }

    /**
     * Matte the image
     *
     * @param image  the image
     * @param child  the XML defining the matting
     * @param dfltSpace  default spacing
     *
     * @return a new image
     */
    public BufferedImage doMatte(BufferedImage image, Element child,
                                 int dfltSpace) {
        return doMatte(image, child, getInsets(child, dfltSpace));
    }


    /**
     * Matte the image
     *
     * @param image  the image
     * @param child  the matte specs
     * @param insets the insets
     *
     * @return  a new image
     */
    public BufferedImage doMatte(BufferedImage image, Element child,
                                 Insets insets) {
        Color bg = applyMacros(child, ATTR_BACKGROUND, Color.white);
        return ImageUtils.matte(image, insets.top, insets.bottom,
                                insets.left, insets.right, bg);
    }



    /*
      public void setDataSourceFiles(String[] datasource, String[] filenames) {

      List dataSources = getIdv().getDataSources();

        for (int k = 0; k < datasource.size(); k++) {
          for (int i = 0; i < dataSources.size(); i++) {
            DataSource theDataSource = (DataSource) dataSources.get(i);
            if (theDataSource.identifiedByName(datasource[k])) {
              theDataSource.setNewFiles(new ArrayList().add(filenames[k]));
            }
          }
        }
      }

     */


    /**
     * Get the file name to write images to. If we are in test mode then prepend the test directory
     *
     * @param filename image file name
     *
     * @return filename to use
     */
    private String getImageFileName(String filename) {
        if (LogUtil.getTestMode()) {
            if (getIdv().getArgsManager().testDir != null) {
                filename = IOUtil.joinDir(getIdv().getArgsManager().testDir,
                                          filename);
            }
        }
        return filename;
    }


    /**
     * Set the font on the graphics from the font defined on the node.
     *
     * @param g The graphics
     * @param node Node to get font info from
     */
    private void setFont(Graphics g, Element node) {
        int fontSize = applyMacros(node, ATTR_FONTSIZE, 12);
        Font f = new Font(applyMacros(node, ATTR_FONTFACE, "dialog"),
                          Font.PLAIN, fontSize);
        g.setFont(f);
    }


    /**
     * Get the font from the XML
     *
     * @param node  the XML
     *
     * @return  the font or null
     */
    private Font getFont(Element node) {
        if (XmlUtil.hasAttribute(node, ATTR_FONTSIZE)
                || XmlUtil.hasAttribute(node, ATTR_FONTFACE)) {
            int fontSize = applyMacros(node, ATTR_FONTSIZE, 12);
            return new Font(applyMacros(node, ATTR_FONTFACE, "dialog"),
                            Font.PLAIN, fontSize);
        }
        return null;
    }



    /**
     * Called to notify this object that the movie capture is done
     */
    public synchronized void doneCapturingMovie() {
        this.notify();
    }


    /**
     * Capture a movie from the first view manager
     *
     * @param filename The movie  filename
     */
    public synchronized void captureMovie(String filename) {
        captureMovie(filename, null);
    }


    /**
     * Capture the movie
     *
     * @param filename The file
     * @param scriptingNode Node form isl.
     */
    public synchronized void captureMovie(String filename,
                                          Element scriptingNode) {

        if ((filename == null) && (scriptingNode != null)) {
            filename = XmlUtil.getAttribute(scriptingNode, ATTR_FILE);
        }

        if (scriptingNode != null) {
            List files = findFiles(scriptingNode);
            if (files != null) {
                debug("Making movie from existing images " + filename);
                filename = applyMacros(filename);
                Dimension size = new Dimension(applyMacros(scriptingNode,
                                     ATTR_WIDTH,
                                     400), applyMacros(scriptingNode,
                                         ATTR_HEIGHT, 300));
    /**
                ImageSequenceGrabber isg = new ImageSequenceGrabber(
                                               filename, getIdv(), this,
                                               scriptingNode, files, size,
                                               applyMacros(
                                                   scriptingNode,
                                                   ATTR_FRAMERATE,
                                                   2), applyMacros(
                                                       scriptingNode,
                                                       ATTR_ENDFRAMEPAUSE,
                                                       -1));
                return;
            }
        }

        List viewManagers = null;
        if ((scriptingNode != null)
                && XmlUtil.hasAttribute(scriptingNode, ATTR_DISPLAY)) {
            DisplayControlImpl display = findDisplayControl(scriptingNode);
            if (display == null) {
                throw new IllegalArgumentException("Could not find display:"
                        + XmlUtil.toString(scriptingNode));
            }
            String what = applyMacros(scriptingNode, ATTR_WHAT,
                                      (String) null);

            ViewManager viewManager = null;
            try {
                viewManager = display.getViewManagerForCapture(what);
                if (viewManager != null) {
                    viewManager.updateDisplayIfNeeded();
                }
            } catch (Exception exc) {
                throw new RuntimeException(exc);
            }

            if (viewManager != null) {
                viewManagers = (List) Misc.newList(viewManager);
            } else {
                throw new IllegalArgumentException(
                    "Cannot capture a movie with display:"
                    + XmlUtil.toString(scriptingNode));
            }
        }

        if (viewManagers == null) {
            viewManagers = (List) getViewManagers(scriptingNode);
        }



        boolean combine = XmlUtil.getAttribute(scriptingNode,
                              ImageGenerator.ATTR_COMBINE, false);


        if (combine) {
            ViewManager viewManager =
                getIdv().getVMManager().getLastActiveViewManager();

            getProperties().put(PROP_VIEWINDEX, new Integer(0));
            String name = viewManager.getName();
            if (name == null) {
                name = "view" + 0;
            }
            getProperties().put(PROP_VIEWNAME, name);

            if ( !getIdv().getArgsManager().getIsOffScreen()) {
                JFrame frame = GuiUtils.getFrame(viewManager.getContents());
                if (frame != null) {
                    LogUtil.registerWindow(frame);
                    frame.show();
                    GuiUtils.toFront(frame);
                    frame.setLocation(50, 50);
                    Misc.sleep(50);
                }
            }
            String loopFilename = applyMacros(filename);
            debug("Making movie:" + loopFilename);
            ImageSequenceGrabber isg = new ImageSequenceGrabber(viewManager,
                                           loopFilename, getIdv(), this,
                                           scriptingNode);
            } catch (Exception exc) {
                logException("Doing the captureMovie wait", exc);
            }
            debug("Done making movie:" + loopFilename);

        } else {

            for (int i = 0; i < viewManagers.size(); i++) {
                ViewManager viewManager = viewManagers.get(i);

                getProperties().put(PROP_VIEWINDEX, new Integer(i));
    /** isl tag */
                String name = viewManager.getName();
                if (name == null) {
                    name = "view" + i;
                }
                getProperties().put(PROP_VIEWNAME, name);

                if ( !getIdv().getArgsManager().getIsOffScreen()) {
                    JFrame frame =
                        GuiUtils.getFrame(viewManager.getContents());
                    if (frame != null) {
                        LogUtil.registerWindow(frame);
                        frame.show();
                        GuiUtils.toFront(frame);
                        frame.setLocation(50, 50);
                        Misc.sleep(50);
                    }
                }
                String loopFilename = applyMacros(filename);
                debug("Making movie:" + loopFilename);
                ImageSequenceGrabber isg =
                    new ImageSequenceGrabber(viewManager, loopFilename,
                                             getIdv(), this, scriptingNode);
                try {
                    wait();
                } catch (Exception exc) {
                    logException("Doing the captureMovie wait", exc);
                }
                debug("Done making movie:" + loopFilename);
            }
        }

    }


    /**
     * Find the animation time of the first Animation in a view manager we find
     *
     * @return Animation time
     */
    public Date getAnimationTime() {
        List vms = getViewManagers(currentNode);
        if (vms.size() > 0) {
            ViewManager vm        = (ViewManager) vms.get(0);
            Animation   animation = vm.getAnimation();
            if (animation != null) {
                Real v = animation.getAniValue();
                if (v != null) {
                    return new Date((long) v.getValue() * 1000);
                }

            }
        }
        return new Date(Misc.getCurrentTime());
    }


    /**
     * Create and instantiate the jython interp.
     *
     * @return The interp
     */
    private PythonInterpreter getInterpreter() {
        if (interpreter == null) {
            interpreter = getIdv().getJythonManager().createInterpreter();
            interpreter.set("ig", this);
            interpreter.set("interp", this);
            interpreter.set("islInterpreter", this);
        }
        return interpreter;
    }

    /**
     * callable by jython to find the data choices that match the given pattern
     *
     * @param datasource data source
     * @param pattern pattern to match
     *
     * @return comma separated list of data choice names
     */
    public String fields(String datasource, String pattern) {
        DataSource dataSource = findDataSource(datasource);
        if (dataSource == null) {
            throw new IllegalArgumentException("Could not find data source:"
                    + datasource);
        }
        List choices;
        if ((pattern == null) || (pattern.length() == 0)) {
            choices = dataSource.getDataChoices();
            choices = dataSource.findDataChoices(pattern);
        }

        List names = new ArrayList();
        for (int i = 0; i < choices.size(); i++) {
            DataChoice dataChoice = (DataChoice) choices.get(i);
            names.add(dataChoice.getName());
        }
        return StringUtil.join(",", names);
    }


     * Class OutputInfo is used for handling output tags
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.113 $
     */
    private class OutputInfo {

        /** The node */
        Element outputNode;

        /** mapping of where to StringBuffer */
        Hashtable buffers = new Hashtable();

        /** mapping of where to templates */
        Hashtable templates = new Hashtable();

        /**
         * ctor
         *
         * @param node The output node
         */
        public OutputInfo(Element node) {
            this.outputNode = node;
        }

        /**
         * Handle the node.
         *
         * @param node Node to process
         *
         * @throws Throwable On badness
         */
        public void process(Element node) throws Throwable {
            String       where = applyMacros(node, ATTR_TEMPLATE, "contents");
            StringBuffer sb       = (StringBuffer) buffers.get(where);
            String       template = (String) templates.get(where);
            if (sb == null) {
                sb = new StringBuffer();
                template = XmlUtil.getAttribute(outputNode,
                        ATTR_TEMPLATE + ":" + where, "${text}");
                if (template.startsWith("file:")) {
                    template = applyMacros(template);
                    template = IOUtil.readContents(template.substring(5));
                }
                buffers.put(where, sb);
                templates.put(where, template);
            }
            String text = XmlUtil.getAttribute(node, ATTR_TEXT,
                              (String) null);
            if (text == null) {
                if (XmlUtil.hasAttribute(node, ATTR_FROMFILE)) {
                    String filename = applyMacros(node, ATTR_FROMFILE);
                    text = applyMacros(IOUtil.readContents(filename));
                } else {
                    text = XmlUtil.getChildText(node);
                    if ((text != null) && (text.length() == 0)) {
                        text = null;
                    }
                }
            }
            if (text == null) {
                NamedNodeMap nnm   = node.getAttributes();
                Hashtable    props = new Hashtable();
                if (nnm != null) {
                    for (int i = 0; i < nnm.getLength(); i++) {
                        Attr attr = (Attr) nnm.item(i);
                        if ( !ATTR_TEMPLATE.equals(attr.getNodeName())) {
                            props.put(attr.getNodeName(),
                                      applyMacros(attr.getNodeValue()));
                        }
                    }
                }
                text = applyMacros(template, props);
            } else {
                text = applyMacros(text);
            }
            sb.append(text);
        }

        /**
         * Write out the output
         *
         * @throws Throwable On badness
         */
        public void write() throws Throwable {
            String outputFile = applyMacros(outputNode, ATTR_FILE);
            String template = applyMacros(outputNode, ATTR_TEMPLATE,
                                          (String) null);
            if (template == null) {
                template = "${contents}";
            }
            if (template.startsWith("file:")) {
                template = IOUtil.readContents(template.substring(5));
            }
            for (Enumeration keys =
                    buffers.keys(); keys.hasMoreElements(); ) {
                String       key  = (String) keys.nextElement();
                StringBuffer buff = (StringBuffer) buffers.get(key);
                template = applyMacros(template,
                                       Misc.newHashtable(key,
                                           buff.toString()));
            }
            IOUtil.writeFile(outputFile, template);
        }

    }


    /**
     * Print out a wanring message
     *
     * @param msg message
     */
    private void warning(String msg) {
        System.err.println(new Date() + " WARNING:" + msg);
    }



    /**
     * Print the message if in debug mode
     *
     * @param msg The message
     */
    protected void debug(String msg) {
        if (debug) {
            System.out.println(new Date() + ": " + msg);
        }
    }


    /**
     * Class MyBreakException for handling break tags
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.113 $
     */
    protected static class MyBreakException extends Exception {}

    /**
     * Class MyContinueException for handling continue tags
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.113 $
     */
    protected static class MyContinueException extends Exception {}

    /**
     * Class MyReturnException allows us to return from a isl procedure by throwing an exception.
     * Yes, I know you're not supposed to use exceptions in a non-exceptional way but it works
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.113 $
     */
    protected static class MyReturnException extends Exception {}


    /**
     * Class description
     *
     *
     * @version        Enter version here..., Tue, Jan 12, '10
     * @author         Enter your name here...
     */
    protected static class MyQuitException extends Exception {}

    /**
     * Class BadIslException is used to handle bad isl errors
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.113 $
     */
    private static class BadIslException extends RuntimeException {

        /** message */
        String msg;

        /**
         * ctor
         *
         * @param msg error message
         */
        public BadIslException(String msg) {
            this.msg = msg;
        }

        /**
         * to string
         *
         * @return error message
         */
        public String toString() {
            return msg;
        }

    }


    /**
     * IS the FtpClient in an ok state. If it isn't then disconnect it and throw and IllegalStateException
     *
     * @param f Ftp client
     * @param msg Message to use if in error
     *
     * @throws Exception On badness
     */
    private static void checkFtp(FTPClient f, String msg) throws Exception {
        int replyCode = f.getReplyCode();
        if ( !FTPReply.isPositiveCompletion(replyCode)) {
            String reply = f.getReplyString();
            f.disconnect();
            throw new IllegalStateException("Error with ftp: " + replyCode
                                            + " " + msg + "\n" + reply);
        }
    }

    /**
     * Do an FTP put of the given bytes
     *
     * @param server server
     * @param userName user name on server
     * @param password password on server
     * @param destination Where to put the bytes
     * @param bytes The bytes
     *
     * @throws Exception On badness
     */
    public static void ftpPut(String server, String userName,
                              String password, String destination,
                              byte[] bytes)
            throws Exception {
        FTPClient f = new FTPClient();

        f.connect(server);
        f.login(userName, password);
        f.setFileType(FTP.BINARY_FILE_TYPE);
        f.enterLocalPassiveMode();
        checkFtp(f, "Connecting to ftp server");
        f.storeFile(destination, new ByteArrayInputStream(bytes));
        checkFtp(f, "Storing file");
        f.logout();
        f.disconnect();
    }

    /** abcdefg... */
    private static String[] alphabet = {
        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
        "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
    };

    /** roman numerals */
    private static String[] roman = {
        "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI",
        "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XX", "XXI",
        "XXII", "XXIII", "XXIV", "XXV", "XXVI", "XXVII", "XXVIII"
    };

    /**
     * Get the letter for the index
     *
     * @param i  the index
     *
     * @return  the letter
     */
    public String getLetter(int i) {
        if ((i >= 0) && (i < alphabet.length)) {
            return alphabet[i];
        }
        //A hack for now
        return "out of range";

    }

    /**
     * Get the roman numeral
     *
     * @param i the index
     *
     * @return  the corresponding number
     */
    public String getRoman(int i) {
        if ((i >= 0) && (i < roman.length)) {
            return roman[i];
        }
        //A hack for now
        return "out of range";
    }



}
=======
/*
 * Copyright 1997-2013 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 * 
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
     *
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package ucar.unidata.idv.ui;


import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;

import org.python.util.PythonInterpreter;

import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;

import ucar.unidata.data.DataChoice;
import ucar.unidata.data.DataSelection;
import ucar.unidata.data.DataSource;
import ucar.unidata.data.GeoLocationInfo;
import ucar.unidata.data.grid.GridDataSource;
import ucar.unidata.geoloc.ProjectionRect;
import ucar.unidata.idv.ControlDescriptor;
import ucar.unidata.idv.DisplayControl;
import ucar.unidata.idv.IdvManager;
import ucar.unidata.idv.IdvPersistenceManager;
import ucar.unidata.idv.IntegratedDataViewer;
import ucar.unidata.idv.MapViewManager;
import ucar.unidata.idv.VectorGraphicsRenderer;
import ucar.unidata.idv.ViewDescriptor;
import ucar.unidata.idv.ViewManager;
import ucar.unidata.idv.control.DisplayControlImpl;
import ucar.unidata.ui.ImageUtils;
import ucar.unidata.ui.colortable.ColorTableCanvas;
import ucar.unidata.util.ColorTable;
import ucar.unidata.util.FileManager;
import ucar.unidata.util.GuiUtils;
import ucar.unidata.util.IOUtil;
import ucar.unidata.util.LogUtil;
import ucar.unidata.util.Misc;
import ucar.unidata.util.PatternFileFilter;
import ucar.unidata.util.Range;
import ucar.unidata.util.StringUtil;
import ucar.unidata.util.Trace;
import ucar.unidata.view.geoloc.NavigatedDisplay;
import ucar.unidata.view.geoloc.ViewpointInfo;
import ucar.unidata.xml.XmlUtil;

import ucar.visad.GeoUtils;
import ucar.visad.display.Animation;
import ucar.visad.display.AnimationWidget;

import visad.CommonUnit;
import visad.MouseBehavior;
import visad.Real;
import visad.RealType;
import visad.Unit;

import visad.georef.EarthLocation;
import visad.georef.EarthLocationTuple;
import visad.georef.LatLonPoint;
import visad.georef.MapProjection;

import visad.util.BaseRGBMap;
import visad.util.ColorPreview;


import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;

import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.TimeZone;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;


/**
 * Manages the user interface for the IDV
 *
 *
 * @author IDV development team
 */
public class ImageGenerator extends IdvManager {


    /** attr value */
    public static final String VALUE_TOP = "top";

    /** attr value */
    public static final String VALUE_BOTTOM = "bottom";

    /** attr value */
    public static final String VALUE_RIGHT = "right";

    /** attr value */
    public static final String VALUE_LEFT = "left";

    /** attr value */
    public static final String VALUE_ALL = "all";

    /** attr value */
    public static final String VALUE_NONE = "none";

    /** attr value */
    public static final String VALUE_FIRST = "first";

    /** attr value */
    public static final String VALUE_LAST = "last";

    /** macro property */
    public static final String PROP_LOOPINDEX = "loopindex";

    /** padded loop index */
    public static final String PROP_LOOPINDEX_PAD2 = "loopindex_pad2";

    /** padded loop index */
    public static final String PROP_LOOPINDEX_PAD3 = "loopindex_pad3";

    /** padded loop index */
    public static final String PROP_LOOPINDEX_PAD4 = "loopindex_pad4";




    /** macro property */
    public static final String PROP_VIEWINDEX = "viewindex";


    /** macro property */
    public static final String PROP_VIEWNAME = "viewname";

    /** macro property */
    public static final String PROP_IMAGEINDEX = "imageindex";


    /** macro property */
    public static final String PROP_IMAGEFILE = "imagefile";

    /** macro property */
    public static final String PROP_IMAGEPATH = "imagepath";


    /** file property */
    public static final String PROP_FILE = "file";

    /** filenosuffix property */
    public static final String PROP_FILENOSUFFIX = "filenosuffix";

    /** filetail property */
    public static final String PROP_FILETAIL = "filetail";

    /** filetailnosuffix property */
    public static final String PROP_FILETAILNOSUFFIX = "filetailnosuffix";

    /** fileprefix property */
    public static final String PROP_FILEPREFIX = "fileprefix";

    /** macro property */
    public static final String PROP_CONTENTS = "contents";

    /** macro property */
    public static final String PROP_ANIMATIONTIME = "animationtime";


    /** macro property */
    public static final String PROP_OFFSCREEN = "offscreen";


    /** Macro name */
    private static final String[] DATE_PROPS = {
        "G", "yy", "yyyy", "MM", "M", "MMM", "MMMMM", "HH", "H", "k", "kk",
        "D", "d", "dd", "K", "KK", "a", "mm", "ss", "s", "S", "EEE", "Z"
    };

    /** List of SimpleDateFormat objects. One for each DATE_PROPS. */
    private static List DATE_FORMATS;


    /** isl tag */
    public static final String TAG_FILESET = "fileset";


    /** view tag */
    public static final String TAG_VIEW = "view";

    /** isl tag */
    public static final String TAG_TEMPLATE = "template";


    /** isl tag */
    public static final String TAG_APPEND = "append";

    /** isl tag */
    public static final String TAG_SETFILES = "setfiles";


    /** isl tag */
    public static final String TAG_ISL = "isl";

    /** viewpoint tag */
    public static final String TAG_VIEWPOINT = "viewpoint";

    /** isl tag */
    public static final String TAG_PROPERTY = "property";

    /** isl tag */
    public static final String TAG_IMPORT = "import";

    /** isl tag */
    public static final String TAG_IMAGE = "image";

    /** isl tag */
    public static final String TAG_GROUP = "group";

    /** isl tag */
    public static final String TAG_PAUSE = "pause";

    /** isl tag */
    public static final String TAG_MOVIE = "movie";

    /** isl tag */
    public static final String TAG_BUNDLE = "bundle";

    /** isl tag */
    public static final String TAG_ELSE = "else";

    /** isl tag */
    public static final String TAG_THEN = "then";


    /** isl tag */
    public static final String TAG_COLORBAR = "colorbar";

    /** isl tag */
    public static final String TAG_CLIP = "clip";



    /** publish tag */
    public static final String TAG_PUBLISH = "publish";

    /** isl tag */
    public static final String TAG_DISPLAY = "display";

    /** datasource tag */
    public static final String TAG_DATASOURCE = "datasource";

    /** isl tag */
    public static final String TAG_MATTE = "matte";

    /** show tag */
    public static final String TAG_SHOW = "show";

    /** displaylist tag */
    public static final String TAG_DISPLAYLIST = "displaylist";

    /** isl tag */
    public static final String TAG_OUTPUT = "output";


    /** isl tag */
    public static final String TAG_OVERLAY = "overlay";


    /** isl tag */
    public static final String TAG_KML = "kml";

    /** kml colorbar tag */
    public static final String TAG_KML_COLORBAR = "kmlcolorbar";

    /** isl tag */
    public static final String TAG_KMZFILE = "kmzfile";

    /** isl tag */
    public static final String TAG_SPLIT = "split";

    /** isl tag */
    /** isl tag */
    public static final String TAG_RESIZE = "resize";

    /** isl tag */
    public static final String TAG_THUMBNAIL = "thumbnail";

    /** isl tag */
    public static final String TAG_TRANSPARENT = "transparent";

    /** background transparent tag */
    public static final String TAG_BGTRANSPARENT = "backgroundtransparent";


    /** index attribute */
    public static final String ATTR_INDEX = "index";

    /** stride attribute */
    public static final String ATTR_STRIDE = "stride";

    /** x stride attribute */
    public static final String ATTR_STRIDEX = "stridex";

    /** y stride attribute */
    public static final String ATTR_STRIDEY = "stridey";

    /** z stride attribute */
    public static final String ATTR_STRIDEZ = "stridez";

    /** bounding box attribute */
    public static final String ATTR_BBOX = "bbox";

    /** from level attribute */
    public static final String ATTR_LEVEL_FROM = "levelfrom";

    /** to level attribute */
    public static final String ATTR_LEVEL_TO = "levelto";

    /** azimuth attribute */
    public static final String ATTR_AZIMUTH = "azimuth";

    /** tilt attribute */
    public static final String ATTR_TILT = "tilt";

    /** x aspect attribute */
    public static final String ATTR_ASPECTX = "aspectx";

    /** y aspect attribute */
    public static final String ATTR_ASPECTY = "aspecty";

    /** z aspect attribute */
    public static final String ATTR_ASPECTZ = "aspectz";


    /** x rotation attribute */
    public static final String ATTR_ROTX = "rotx";

    /** y rotation attribute */
    public static final String ATTR_ROTY = "roty";

    /** z rotation attribute */
    public static final String ATTR_ROTZ = "rotz";

    /** scale attribute */
    public static final String ATTR_SCALE = "scale";

    /** x translation attribute */
    public static final String ATTR_TRANSX = "transx";

    /** y translation attribute */
    public static final String ATTR_TRANSY = "transy";

    /** z translation attribute */
    public static final String ATTR_TRANSZ = "transz";


    /** suffix attribute */
    public static final String ATTR_SUFFIX = "suffix";

    /** showunit atttribute */
    public static final String ATTR_SHOWUNIT = "showunit";

    /** transparency attribute */
    public static final String ATTR_TRANSPARENCY = "transparency";

    /** top attribute */
    public static final String ATTR_TOP = "top";


    /** left space attribute */
    public static final String ATTR_SPACE_LEFT = "space_left";

    /** right space attribute */
    public static final String ATTR_SPACE_RIGHT = "space_right";

    /** top space attribute */
    public static final String ATTR_SPACE_TOP = "space_top";

    /** bottom space attribute */
    public static final String ATTR_SPACE_BOTTOM = "space_bottom";




    /** isl tag */
    public static final String TAG_WRITE = "write";



    public static final String ATTR_ANCHOR = "anchor";


    /** isl attr */
    public static final String ATTR_FROM = "from";

    /** isl attr */
    public static final String ATTR_TO = "to";


    /** isl attribute */
    public static final String ATTR_GLOBAL = "global";

    /** isl attribute */
    public static final String ATTR_ONERROR = "onerror";

    /** isl attribute */
    public static final String ATTR_SORT = "sort";

    /** isl attribute */
    public static final String ATTR_SORTDIR = "sortdir";

    /** isl attribute */
    public static final String VALUE_TIME = "time";

    /** isl attribute */
    public static final String VALUE_ASCENDING = "ascending";

    /** isl attribute */
    public static final String VALUE_DESCENDING = "descending";


    /** isl attribute */
    public static final String ATTR_FIRST = "first";

    /** isl attribute */
    public static final String ATTR_LAST = "last";



    /** isl tag */
    public static final String ATTR_USEPROJECTION = "useprojection";

    /** isl tag */
    public static final String ATTR_EXPR = "expr";

    /** isl tag */
    public static final String ATTR_COPY = "copy";

    /** the count tag */
    public static final String ATTR_COUNT = "count";

    /** isl tag */
    public static final String ATTR_COLUMNS = "columns";

    /** isl attribute */
    public static final String ATTR_DATASOURCE = "datasource";


    /** isl attribute */
    public static final String ATTR_DESTINATION = "destination";

    /** isl attribute */
    public static final String ATTR_SERVER = "server";

    /** isl attribute */
    public static final String ATTR_PASSWORD = "password";

    /** isl attribute */
    public static final String ATTR_USER = "user";

    /** isl tag */
    public static final String ATTR_ROWS = "rows";

    /** isl tag */
    public static final String ATTR_CLASS = "class";

    /** isl tag */
    public static final String ATTR_ANGLE = "angle";

    /** isl tag */
    public static final String ATTR_WHERE = "where";

    /** isl tag */
    public static final String ATTR_BACKGROUND = "background";

    /** isl attribute */
    public static final String ATTR_BUNDLE = "bundle";

    /** isl tag */
    public static final String ATTR_SHOWLINES = "showlines";

    /** isl tag */
    public static final String ATTR_LINECOLOR = "linecolor";

    /** isl tag */
    public static final String ATTR_COLOR = "color";

    /** isl tag */
    public static final String ATTR_COMMAND = "command";

    /** isl tag */
    public static final String ATTR_FONTFACE = "fontface";

    /** isl attr */
    public static final String ATTR_FORMAT = "format";

    /** that latlonlabels tag */

    public static final String TAG_LATLONLABELS = "latlonlabels";

    /** that latvalues tag */
    public static final String ATTR_LAT_VALUES = "latvalues";

    /** that latlabels tag */
    public static final String ATTR_LAT_LABELS = "latlabels";

    /** that lonvalues tag */
    public static final String ATTR_LON_VALUES = "lonvalues";


    /** that lonlabels tag */
    public static final String ATTR_LON_LABELS = "lonlabels";

    /** the draw lon lines tag */
    public static final String ATTR_DRAWLONLINES = "drawlonlines";

    /** the draw lat lines tag */
    public static final String ATTR_DRAWLATLINES = "drawlatlines";

    /** dashes tag */
    public static final String ATTR_DASHES = "dashes";

    /** linewidth tag */
    public static final String ATTR_LINEWIDTH = "linewidth";

    /** isl tag */
    public static final String ATTR_LINEOFFSET_RIGHT = "lineoffsetright";

    /** isl tag */
    public static final String ATTR_LINEOFFSET_LEFT = "lineoffsetleft";

    /** isl tag */
    public static final String ATTR_LINEOFFSET_TOP = "lineoffsettop";

    /** isl tag */
    public static final String ATTR_LINEOFFSET_BOTTOM = "lineoffsetbottom";

    /** isl tag */
    public static final String ATTR_LABELBACKGROUND = "labelbackground";

    /** isl tag */
    public static final String ATTR_SHOWTOP = "showtop";

    /** isl tag */
    public static final String ATTR_SHOWBOTTOM = "showbottom";

    /** isl tag */
    public static final String ATTR_SHOWLEFT = "showleft";

    /** isl tag */
    public static final String ATTR_SHOWRIGHT = "showright";

    /** isl tag */
    public static final String ATTR_FONTSIZE = "fontsize";

    /** isl tag */
    public static final String ATTR_FRAMERATE = "framerate";

    /** isl tag for ending frame pause for animated gifs */
    public static final String ATTR_ENDFRAMEPAUSE = "endframepause";

    /** isl tag */
    public static final String ATTR_CAPTION = "caption";

    /** isl tag */
    public static final String ATTR_DEBUG = "debug";

    /** isl tag */
    public static final String ATTR_DEFAULT = "default";

    /** isl tag */
    public static final String ATTR_DISPLAY = "display";

    /** isl tag */
    public static final String ATTR_OFFSCREEN = "offscreen";

    /** isl tag */
    public static final String ATTR_TIMES = "times";

    /** isl tag */
    public static final String ATTR_ENSEMBLES = "ensembles";

    /** isl tag */
    public static final String ATTR_DIR = "dir";

    /** isl tag */
    public static final String ATTR_PATTERN = "pattern";


    /** isl attribute */
    public static final String ATTR_WAIT = "wait";

    /** isl tag */
    public static final String ATTR_PROPERTY = "property";

    /** isl tag */
    public static final String ATTR_QUALITY = "quality";

    /** isl tag */
    public static final String ATTR_LOOP = "loop";

    /** isl tag */
    public static final String ATTR_ENTRY = "entry";
    /** isl tag */
    public static final String ATTR_ID = "id";

    /** isl tag */
    public static final String ATTR_IMAGE = "image";

    /** isl tag */
    public static final String ATTR_INTERVAL = "interval";

    /** isl tag */
    public static final String ATTR_LEFT = "left";

    /** isl tag */
    public static final String ATTR_LAT = "lat";
    public static final String ATTR_MESSAGE = "message";

    /** isl tag */
    public static final String ATTR_MATTEBG = "mattebg";

    /** isl tag */
    public static final String ATTR_NAME = "name";

    /** isl tag */
    public static final String ATTR_RIGHT = "right";

    /** isl tag */
    public static final String ATTR_TICKMARKS = "tickmarks";

    /** isl tag */
    public static final String ATTR_SPACE = "space";

    /** isl tag */
    public static final String ATTR_HSPACE = "hspace";

    /** isl tag */
    public static final String ATTR_VSPACE = "vspace";

    /** isl tag */
    public static final String ATTR_BOTTOM = "bottom";

    /** the valign attribute */
    public static final String ATTR_VALIGN = "valign";

    /** isl tag */
    public static final String ATTR_TEXT = "text";

    /** isl tag */
    public static final String ATTR_TEMPLATE = "template";

    /** isl tag */
    public static final String ATTR_TYPE = "type";

    /** isl tag */
    public static final String ATTR_EVERY = "every";

    /** isl tag */
    public static final String ATTR_VALUE = "value";

    /** isl tag */
    public static final String ATTR_VALUES = "values";

    /** isl tag */
    public static final String ATTR_ORIENTATION = "orientation";


    /** isl tag */
    public static final String ATTR_PARAM = "param";

    /** isl tag */
    public static final String ATTR_PLACE = "place";

    /** isl tag */
    public static final String ATTR_VIEW = "view";

    /** the view dir attribute */
    public static final String ATTR_VIEWDIR = "viewdir";

    /** isl tag */
    public static final String ATTR_URL = "url";

    /** isl tag */
    public static final String ATTR_FILE = "file";

    /** isl tag */
    public static final String ATTR_FROMFILE = "fromfile";

    /** isl tag */
    public static final String ATTR_NORTH = "north";

    /** isl tag */
    public static final String ATTR_SOUTH = "south";

    /** isl tag */
    public static final String ATTR_EAST = "east";

    /** isl tag */
    public static final String ATTR_WEST = "west";

    /** isl tag */
    public static final String ATTR_WIDTH = "width";

    /** isl tag */
    public static final String ATTR_HEIGHT = "height";

    /** isl tag */
    public static final String ATTR_SLEEP = "sleep";

    /** isl tag */
    public static final String ATTR_SECONDS = "seconds";

    /** isl tag */
    public static final String ATTR_MINUTES = "minutes";

    /** isl tag */
    public static final String ATTR_HOURS = "hours";

    /** isl tag */
    public static final String ATTR_CLEAR = "clear";

    /** isl tag */
    public static final String ATTR_CODE = "code";

    /** isl tag */

    public static final String ATTR_LON = "lon";

    /** isl attribute */
    public static final String ATTR_WHAT = "what";

    /** isl attribute */
    public static final String ATTR_COMBINE = "combine";

    /** isl attribute */
    public static final String ATTR_ANIMATION_INDEX = "animation_index";

    /** isl attribute */
    private static final String ATTR_SUFFIXFREQUENCY = "suffixfrequency";

    /** Show debug messages */
    private boolean debug = false;

    /** Stack of properties hashtables */
    private List propertiesStack = new ArrayList();

    /** Maps id to data source */
    private Hashtable idToDataSource = new Hashtable();

    /** The interpreter */
    private PythonInterpreter interpreter;

    /** Stack of active OutputInfo objects */
    private List outputStack = new ArrayList();

    /** When we are looping this gets set so we can use in as a macro value */
    private int currentLoopIndex = 0;

    /** Holds the procedure elements. Maps procedure name to element. */
    private Hashtable procs;

    /** Holds the tag name to method */
    private Hashtable methods = new Hashtable();

    /** current xml node we are processing */
    private Element currentNode;


    /** Keep around the last image captured */
    private Image lastImage;

    /** an object map */
    private Hashtable objectMap;



    /**
     * Create me with the IDV
     *
     * @param idv The IDV
     */
    public ImageGenerator(IntegratedDataViewer idv) {
        super(idv);
    }

    /**
     * Create me with the IDV and start processing files
     *
     * @param idv The IDV
     * @param scriptFiles List of isl files
     */
    public ImageGenerator(IntegratedDataViewer idv, List scriptFiles) {
        super(idv);
        processScriptFiles(scriptFiles);
    }


    /**
     * Process the list of isl files
     *
     * @param scriptFiles isl files
     */
    public void processScriptFiles(List scriptFiles) {
        for (int fileIdx = 0; fileIdx < scriptFiles.size(); fileIdx++) {
            String filename = (String) scriptFiles.get(fileIdx);
            if ( !processScriptFile(filename)) {
                return;
            }
        }

    }


    /**
     * Process the  isl files
     *
     * @param islFile file
     *
     * @return Was it successful
     */
    public synchronized boolean processScriptFile(String islFile) {
        return processScriptFile(islFile, new Hashtable());
    }


    /**
     * Process the script file
     *
     * @param islFile  the ISL file
     * @param properties optional properties
     *
     * @return true if successful
     */
    public synchronized boolean processScriptFile(String islFile,
            Hashtable properties) {
        procs           = new Hashtable();

        idToDataSource  = new Hashtable();
        propertiesStack = new ArrayList();
        pushProperties();

        if (islFile.endsWith(".jy") || islFile.endsWith(".py")) {
            try {
                String islPath = IOUtil.getFileRoot(islFile);
                putProperty("islpath", islPath);
                String            jythonCode = IOUtil.readContents(islFile);
                PythonInterpreter interp     = getInterpreter();
                if (getIdv().getJythonManager().getInError()) {
                    return false;
                }

                interp.exec(jythonCode);
                popProperties();
                return true;
            } catch (Exception exc) {
                exc.printStackTrace();
                return error("Error running jython script:" + islFile + "\n"
                             + exc);
            }

        }


        Element root = null;
        try {
            InputStream is = null;
            try {
                if (islFile.startsWith("xml:")) {
                    String xml = islFile.substring(4);
                    is      = new ByteArrayInputStream(xml.getBytes());
                    islFile = "Inline isl";
                } else if (islFile.startsWith("b64:")) {
                    is = new ByteArrayInputStream(
                        XmlUtil.decodeBase64(islFile.substring(4)));
                    islFile = "Inline base 64 encoded isl";
                } else {
                    is = IOUtil.getInputStream(islFile, getClass());
                }
            } catch (FileNotFoundException fnfe) {}
            catch (IOException ioe) {}
            if (is == null) {
                return error(
                    "Given script file does not exist or could not be read: "
                    + islFile);
            }
            root = XmlUtil.getRoot(is);
        } catch (Exception exc) {
            exc.printStackTrace();
            return error("Could not load script file:" + islFile + "\n"
                         + exc);
        }
        if (root == null) {
            return error("Could not load script file:" + islFile);
        }
        try {
            String islPath = IOUtil.getFileRoot(islFile);
            putProperty("islpath", islPath);
            objectMap = properties;
            processNode(root);
            popProperties();
        } catch (InvocationTargetException ite) {
            Throwable inner = ite.getTargetException();
            while (inner instanceof InvocationTargetException) {
                inner =
                    ((InvocationTargetException) inner).getTargetException();
            }
            return handleError(inner);
        } catch (Throwable exc) {
            return handleError(exc);
        } finally {
            objectMap = null;
        }
        return true;
    }

    /**
     * Handle the error
     *
     * @param exc The error
     *
     * @return Just return false
     */
    private boolean handleError(Throwable exc) {
        if ( !(exc instanceof IllegalStateException)
                && !(exc instanceof IllegalArgumentException)) {
            exc.printStackTrace();
        } else {
            exc.printStackTrace();
        }
        return error("An error occurred:" + exc);
    }

    /**
     * Find the inner most non InvocationTargetException exception
     *
     * @param ite The exception
     * @return First non InvocationTargetException exception
     */
    private Throwable getInnerException(InvocationTargetException ite) {
        Throwable inner = ite.getTargetException();
        while (inner instanceof InvocationTargetException) {
            inner = ((InvocationTargetException) inner).getTargetException();
        }
        return inner;
    }


    /**
     * Process the node
     *
     * @param node The node
     * @return ok
     * @throws Throwable On badness
     */
    private boolean processNode(Element node) throws Throwable {
        String tagName = node.getTagName();
        //        System.err.println("tag:" + tagName);

        String methodName = "processTag"
                            + tagName.substring(0, 1).toUpperCase()
                            + tagName.substring(1).toLowerCase();

        //Look to see if this is a isl procedure
        Element procNode = (Element) procs.get(tagName);
        if (procNode != null) {
            return processTagCall(node, procNode);
        }


        Object tmp = methods.get(methodName);
        if (tmp == null) {
            try {
                tmp = getClass().getDeclaredMethod(methodName,
                        new Class[] { Element.class });
            } catch (Exception exc) {}
            if (tmp == null) {
                tmp = "no method";
            }
            methods.put(methodName, tmp);
        }

        if (tmp instanceof Method) {
            try {
                currentNode = node;
                Object result = ((Method) tmp).invoke(this,
                                    new Object[] { node });
                if (result.equals(Boolean.TRUE)) {
                    return true;
                }
            } catch (InvocationTargetException ite) {
                Throwable inner = getInnerException(ite);
                if (inner instanceof BadIslException) {
                    return error("Error handling ISL node:"
                                 + XmlUtil.toStringNoChildren(currentNode)
                                 + "\t" + inner.toString());
                }
                throw inner;
            }
            return false;
        }
        return error("Unknown tag:" + tagName);
    }


    /**
     * Recursively call processNode on each of the children elements
     *
     * @param node The parent node.
     *
     * @return Success.
     *
     * @throws Throwable On badness
     */
    private boolean processChildren(Element node) throws Throwable {
        NodeList elements = XmlUtil.getElements(node);
        for (int childIdx = 0; childIdx < elements.getLength(); childIdx++) {
            Element child = (Element) elements.item(childIdx);
            try {
                if ( !processNode(child)) {
                    return false;
                }
            } catch (Throwable thr) {
                String onerror = applyMacros(node, ATTR_ONERROR,
                                             (String) null);
                if (onerror == null) {
                    throw thr;
                }
                if (onerror.equals("ignore")) {}
                else {
                    System.err.println("Error occured");
                    thr.printStackTrace();
                }
            }
        }
        return true;
    }




    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagFtp(Element node) throws Throwable {
        String file        = applyMacros(node, ATTR_FILE);
        String server      = applyMacros(node, ATTR_SERVER);
        String destination = applyMacros(node, ATTR_DESTINATION);
        String user        = applyMacros(node, ATTR_USER, "anonymous");
        String password = applyMacros(node, ATTR_PASSWORD,
                                      "idvuser@unidata.ucar.edu");
        byte[] bytes = IOUtil.readBytes(IOUtil.getInputStream(file));
        ftpPut(server, user, password, destination, bytes);
        return true;
    }


    /**
     * Process the export tag. This allows one to export data from a display. It requires
     * a display identifier
     *
     * @param node isl xml node
     *
     * @return everything is cool
     *
     * @throws Throwable On badness
     */
    protected boolean processTagExport(Element node) throws Throwable {
        DisplayControlImpl display = findDisplayControl(node);
        if (display == null) {
            throw new IllegalArgumentException("Could not find display:"
                    + XmlUtil.toString(node));
        }
        String what     = applyMacros(node, ATTR_WHAT, (String) null);

        String filename = XmlUtil.getAttribute(node, ATTR_FILE);
        display.doExport(what, filename);
        return true;
    }


    /**
     * Process the tag trace
     *
     * @param node  the node
     *
     * @return true if processed
     *
     * @throws Throwable  on badness
     */
    protected boolean processTagTrace(Element node) throws Throwable {
        String pattern = applyMacros(node, ATTR_PATTERN);
        debug("turning trace on for:" + pattern);
        Trace.startTrace();
        Trace.clearOnly();
        Trace.addOnly(pattern);
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagRemovedisplays(Element node)
            throws Throwable {

        if (XmlUtil.hasAttribute(node, ATTR_DISPLAY)) {
            debug("Removing display");
            DisplayControlImpl display = findDisplayControl(node);
            if (display == null) {
                return error("Could not find display:"
                             + XmlUtil.toString(node));
            }
            display.doRemove();
            return true;
        }
        debug("Removing all displays");
        getIdv().removeAllDisplays(true);
        return true;
    }






    /**
     * Process the print cache tag
     *
     * @param node  the XML node
     *
     * @return  true if successful
     *
     * @throws Throwable  on badness
     */
    }
    protected boolean processTagPrintcache(Element node) throws Throwable {
        visad.data.DataCacheManager.getCacheManager().printStats();
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagRemoveall(Element node) throws Throwable {
        debug("Removing all displays and data");
        getIdv().removeAllDisplays(false);
        getIdv().removeAllDataSources();
        idToDataSource = new Hashtable();
        return true;
    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagSetfiles(Element node) throws Throwable {
        DataSource dataSource = findDataSource(node);
        if (dataSource == null) {
            return error("Could not find data source");
        }
        List files = new ArrayList();
        if (XmlUtil.hasAttribute(node, ATTR_FILE)) {
            files.add(applyMacros(node, ATTR_FILE));
        } else {
            List filesetFiles = findFiles(node);
            if (filesetFiles != null) {
                files.addAll(filesetFiles);
            }
        }
        if (files.size() == 0) {
            return error("Could not find files");
        }
        dataSource.setNewFiles(files);
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagExists(Element node) throws Throwable {
        List    files  = findFiles(node);
        boolean exists = ((files != null) && (files.size() > 0));
        putProperty(applyMacros(node, ATTR_PROPERTY), (exists
                ? "1"
                : "0"));
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagAsk(Element node) throws Throwable {
        String  property = applyMacros(node, ATTR_PROPERTY);
        boolean result;
        if (getIdv().getArgsManager().getIsOffScreen()) {
            if ( !XmlUtil.hasAttribute(node, ATTR_DEFAULT)) {
                throw new IllegalStateException(
                    "Running in offscreen mode and the 'ask' node does not have a 'default' attribute");
            }
            result = applyMacros(node, ATTR_DEFAULT, true);
        } else {
            result = GuiUtils.showYesNoDialog(null,
                    applyMacros(node, ATTR_MESSAGE), "");
        }
        putProperty(property, (result
                               ? "1"
                               : "0"));
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagEcho(Element node) throws Throwable {
        String message = applyMacros(node, ATTR_MESSAGE, (String) null);
        if (message == null) {
            message = applyMacros(XmlUtil.getChildText(node));
        }
        System.out.println(message);
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagAsktocontinue(Element node) throws Throwable {
        String message = applyMacros(node, ATTR_MESSAGE, (String) null);
        if (message == null) {
            message = applyMacros(XmlUtil.getChildText(node));
        }
        return GuiUtils.askOkCancel("ISL", message);
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagGc(Element node) throws Throwable {
        Runtime.getRuntime().gc();
        return true;
    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagBreak(Element node) throws Throwable {
        throw new MyBreakException();
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagContinue(Element node) throws Throwable {
        throw new MyContinueException();
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagReturn(Element node) throws Throwable {
        throw new MyReturnException();
    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagProcedure(Element node) throws Throwable {
        procs.put(applyMacros(node, ATTR_NAME), node);
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagMkdir(Element node) throws Throwable {
        IOUtil.makeDir(applyMacros(node, ATTR_FILE));
        return true;

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagStop(Element node) throws Throwable {
        return false;
    }


    /**
     * Get the property value
     *
     * @param node the XML node
     *
     * @return  the values
     *
     * @throws Throwable on badness
     */
    protected String[] getPropertyValue(Element node) throws Throwable {
        String name  = (String) applyMacros(node, ATTR_NAME);
        String value = null;
        if (XmlUtil.hasAttribute(node, ATTR_VALUE)) {
            value = (String) applyMacros(node, ATTR_VALUE);
        } else if (XmlUtil.hasAttribute(node, ATTR_FROMFILE)) {
            String filename = applyMacros(node, ATTR_FROMFILE);
            value = applyMacros(IOUtil.readContents(filename));
        } else {
            value = XmlUtil.getChildText(node);
            if ((value == null) || (value.trim().length() == 0)) {
                throw new IllegalArgumentException(
                    "No value in property tag: " + XmlUtil.toString(node));
            }
            value = applyMacros(value);
        }
        return new String[] { name, value };
    }

    /**
     * Process IDV property tag
     *
     * @param node  the XML node
     *
     * @return  true if successful
     *
     * @throws Throwable  on badness
     */
    protected boolean processTagIdvproperty(Element node) throws Throwable {
        String[] tuple = getPropertyValue(node);
        debug("setting idv property: " + tuple[0] + " =" + tuple[1]);
        getIdv().getStateManager().putProperty(applyMacros(tuple[0]),
                applyMacros(tuple[1]));
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagProperty(Element node) throws Throwable {
        String[] tuple = getPropertyValue(node);
        putProperty(applyMacros(tuple[0]), tuple[1],
                    applyMacros(node, ATTR_GLOBAL, false));
        return true;
    }




    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagMove(Element node) throws Throwable {
        List files = findFiles(node);
        if (files != null) {
            File dir = new File(applyMacros(node, ATTR_DIR));
            debug("moving files to: " + dir + " files=" + files);
            for (int i = 0; i < files.size(); i++) {
                IOUtil.moveFile(new File(files.get(i).toString()), dir);
            }
        }
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagRename(Element node) throws Throwable {
        String from = applyMacros(node, ATTR_FROM);
        String to   = applyMacros(node, ATTR_TO);
        IOUtil.moveFile(new File(from), new File(to));
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagDelete(Element node) throws Throwable {
        List files = findFiles(node);
        if (files != null) {
            debug("deleting files:" + files);
            for (int i = 0; i < files.size(); i++) {
                ((File) files.get(i)).delete();
            }
        }
        return true;
    }

    /**
     * Handle the clear tag
     *
     * @param node node
     *
     * @return ok
     *
     * @throws Throwable On badness
     */
    protected boolean processTagClear(Element node) throws Throwable {
        String    name = applyMacros(node, ATTR_NAME);
        Hashtable ht   = (Hashtable) propertiesStack.get(0);
        ht.remove(name);
        return true;
    }

    /**
     * Handle the append tag
     *
     * @param node node
     *
     * @return ok
     *
     * @throws Throwable On badness
     */
    protected boolean processTagAppend(Element node) throws Throwable {
        String    name  = applyMacros(node, ATTR_NAME);
        Hashtable ht    = findTableFor(name);
        String    value = (String) ht.get(name);
        if (value == null) {
            value = "";
        }
        if (XmlUtil.hasAttribute(node, ATTR_VALUE)) {
            value = value + applyMacros(node, ATTR_VALUE);
        } else if (XmlUtil.hasAttribute(node, ATTR_FROMFILE)) {
            String filename = applyMacros(node, ATTR_FROMFILE);
            value = value + applyMacros(IOUtil.readContents(filename));
        } else {
            value = value + applyMacros(XmlUtil.getChildText(node)).trim();
        }
        ht.put(name, value);
        return true;
    }


    /**
     * Handle the append tag
     *
     * @param node node
     *
     * @return ok
     *
     * @throws Throwable On badness
     */
    protected boolean processTagIncrement(Element node) throws Throwable {
        String    name  = applyMacros(node, ATTR_NAME);
        Hashtable ht    = findTableFor(name);
        String    value = (String) ht.get(name);
        if (value == null) {
            value = "0";
        }
        String by = "1";
        if (XmlUtil.hasAttribute(node, ATTR_VALUE)) {
            by = applyMacros(node, ATTR_VALUE);
        }
        double num = new Double(value).doubleValue()
                     + new Double(by).doubleValue();
        ht.put(name, "" + num);
        return true;
    }

    /**
     * Handle the append tag
     *
     * @param node node
     *
     * @return ok
     *
     * @throws Throwable On badness
     */
    protected boolean processTagReplace(Element node) throws Throwable {
    /**
        String    name = applyMacros(node, ATTR_NAME);
        Hashtable ht   = findTableFor(name);
        ht.put(name, applyMacros(node, ATTR_VALUE));
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagCopy(Element node) throws Throwable {
        List files = findFiles(node);
        if (files != null) {
            File dir = new File(applyMacros(node, ATTR_DIR));
            IOUtil.makeDir(dir);
            if ( !dir.isDirectory()) {
                return error("Specified file:" + dir + " is not a directory");
            }
            debug("copying files to: " + dir + " files=" + files);
            for (int i = 0; i < files.size(); i++) {
                IOUtil.copyFile(new File(files.get(i).toString()), dir);
            }
        }
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagReload(Element node) throws Throwable {
        List dataSources = getIdv().getDataSources();
        for (int i = 0; i < dataSources.size(); i++) {
            DataSource dataSource = (DataSource) dataSources.get(i);
            dataSource.reloadData();
        }
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagExec(Element node) throws Throwable {
        String command = applyMacros(node, ATTR_COMMAND);
        debug("Calling exec:" + command);
        Process process = Runtime.getRuntime().exec(command);
        //This seems to hang?
        process.waitFor();
        if (process.exitValue() != 0) {
            String result = IOUtil.readContents(process.getInputStream());
            System.err.println("Exec:\n\t" + command + "\nreturned:"
                               + process.exitValue() + "\n" + result);
        }
        return true;

    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagJython(Element node) throws Throwable {
        String jythonFile = applyMacros(node, ATTR_FILE, (String) null);
        if (jythonFile != null) {
            InputStream is = IOUtil.getInputStream(jythonFile, getClass());
            if (is == null) {
                return error("Could not open jython file:" + jythonFile);
            } else {
                getInterpreter().execfile(is, jythonFile);
            }
        } else {
            String jython = applyMacros(node, ATTR_CODE, (String) null);
            if (jython == null) {
                jython = XmlUtil.getChildText(node);
            }
            if (jython != null) {
                getInterpreter().exec(jython);
            }
        }
        return true;
    }


     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagFileset(Element node) throws Throwable {
        List files = findFiles(Misc.newList(node));
        pushProperties();
        for (int i = 0; i < files.size(); i++) {
            try {
                String filePath = files.get(i).toString();
                String tail     = IOUtil.getFileTail(filePath);
                putProperty(PROP_FILE, filePath);
                putProperty(PROP_FILEPREFIX, IOUtil.stripExtension(filePath));
                putProperty(PROP_FILENOSUFFIX,
                            IOUtil.stripExtension(filePath));
                putProperty(PROP_FILETAIL, tail);
                putProperty(PROP_FILETAILNOSUFFIX,
                            IOUtil.stripExtension(tail));
                if ( !processChildren(node)) {
                    return false;
                }
            } catch (MyBreakException be) {
                break;
            } catch (MyContinueException ce) {}
        }
        popProperties();
        return true;

    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagImport(Element node) throws Throwable {
        Element parent  = (Element) node.getParentNode();
        String  file    = applyMacros(node, ATTR_FILE);
        Element root    = XmlUtil.findRoot(node);
        Element newRoot = XmlUtil.getRoot(file, getClass());
        newRoot = (Element) root.getOwnerDocument().importNode(newRoot, true);
        parent.insertBefore(newRoot, node);
        parent.removeChild(node);
        if ( !processNode(newRoot)) {
            return false;
        }
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagDatasource(Element node) throws Throwable {
        debug("Creating data source");

        DataSource dataSource = null;
        String     id         = applyMacros(node, ATTR_ID, (String) null);
        if ((id != null) && (objectMap != null)) {
            dataSource = (DataSource) objectMap.get(id);
        }


        if (dataSource == null) {
            Object dataObject = applyMacros(node, ATTR_URL, (String) null);
            if (dataObject == null) {
                dataObject = StringUtil.toString(findFiles(node));
            }
            String bundle = applyMacros(node, ATTR_BUNDLE, (String) null);
            String type   = applyMacros(node, ATTR_TYPE, (String) null);
            if ((bundle == null) && (dataObject == null)) {
                return error(
                    "datasource tag requires either a url, fileset or a bundle");
            }

            if (dataObject != null) {
                dataSource = getIdv().makeOneDataSource(dataObject, type,
                        null);
                if (dataSource == null) {
                    return error("Failed to create data source:" + dataObject
                                 + " " + type);
                }
            } else {
                try {
                    String bundleXml = IOUtil.readContents(bundle);
                    Object obj =
                        getIdv().getEncoderForRead().toObject(bundleXml);
                    if ( !(obj instanceof DataSource)) {
                        return error("datasource bundle is not a DataSource:"
                                     + obj.getClass().getName());
                    }
                    dataSource = (DataSource) obj;
                } catch (Exception exc) {
                    return error("Error loading data source bundle: "
                                 + bundle, exc);
                }
            }
        }


        if (XmlUtil.hasAttribute(node, ATTR_TIMES)) {
            List timesList =
                StringUtil.parseIntegerListString(applyMacros(node,
                    ATTR_TIMES, (String) null));
            dataSource.setDateTimeSelection(timesList);
        }

        if (XmlUtil.hasAttribute(node, ATTR_ENSEMBLES)) {
            List ensList =
                StringUtil.parseIntegerListString(applyMacros(node,
                    ATTR_ENSEMBLES, (String) null));
            if (dataSource instanceof GridDataSource) {
                ((GridDataSource) dataSource).setEnsembleSelection(ensList);
            }
        }

        processGeoSelectionTags(node, dataSource.getDataSelection());


        Hashtable properties = getProperties(node);
        dataSource.setObjectProperties(properties);
        if (id != null) {
            idToDataSource.put(id, dataSource);
        }

        NodeList elements = XmlUtil.getElements(node);
        for (int childIdx = 0; childIdx < elements.getLength(); childIdx++) {
            Element child = (Element) elements.item(childIdx);
            if (child.getTagName().equals(TAG_DISPLAY)) {
                if ( !processDisplayNode(child, dataSource)) {
                    return false;
                }
            }
        }
        //        getIdv().getVMManager().setDisplayMastersActive();
        updateViewManagers();

        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagJoin(Element node) throws Throwable {
        List files = findFiles(node);
        if (files != null) {
            List images = new ArrayList();
            int  cols   = applyMacros(node, ATTR_COLUMNS, 0);
            int  rows   = applyMacros(node, ATTR_ROWS, 0);
            if ((cols == 0) && (rows == 0)) {
                cols = 1;
            }
            if ((cols != 0) && (rows != 0)) {
                cols = 0;
            }
            int colNum = 0;
            int rowNum = 0;
            for (int i = 0; i < files.size(); i++) {
                Image theImage =
            }
                    ImageUtils.readImage(files.get(i).toString());
                if (theImage == null) {
                    continue;
                }
                images.add(theImage);
            }
            if (images.size() > 0) {
                if (cols == 0) {
                    cols = images.size() / rows;
                } else {
                    rows = images.size() / cols;
                }

                int maxWidth  = 0;
                int maxHeight = 0;
                int colCnt    = 0;
     *
                for (int i = 0; i < images.size(); i++) {
                    Image theImage = (Image) images.get(i);
                    int   width    = theImage.getWidth(null);
                    int   height   = theImage.getHeight(null);
                }
            }
        }
        return true;
    }

    /**
     * Process the view tag
     *
     * @param node the element
     *
     * @return  true if it was processed
     *
     * @throws Throwable  problems
     */
    protected boolean processTagView(Element node) throws Throwable {
        List   vms    = getViewManagers(node);

        String width  = applyMacros(node, ATTR_WIDTH, (String) null);
        String height = applyMacros(node, ATTR_HEIGHT, (String) null);
        if ((width != null) && (height != null)) {
            getIdv().getStateManager().setViewSize(
                new Dimension(
                    new Integer(width).intValue(),
                    new Integer(height).intValue()));
        }

        if (vms.size() == 0) {
            StringBuffer properties = new StringBuffer();
            List         nodes      = XmlUtil.findChildren(node,
                                          TAG_PROPERTY);
            for (int childIdx = 0; childIdx < nodes.size(); childIdx++) {
                Element child = (Element) nodes.get(childIdx);
                properties.append(applyMacros(child, ATTR_NAME) + "="
                                  + applyMacros(child, ATTR_VALUE) + ";");
            }
            vms.add(getIdv().getViewManager(ViewDescriptor.LASTACTIVE, false,
                                            properties.toString()));
            return true;
        }

        for (int i = 0; i < vms.size(); i++) {
            ViewManager vm    = (ViewManager) vms.get(i);
            List        nodes = XmlUtil.findChildren(node, TAG_PROPERTY);
            for (int childIdx = 0; childIdx < nodes.size(); childIdx++) {
                Element child = (Element) nodes.get(childIdx);
                vm.setProperty(applyMacros(child, ATTR_NAME),
                               applyMacros(child, ATTR_VALUE), false);
            }
        }
        return true;
    }


    /**
     * Handle the animation tag. The index attribute can either be a number or be "end"
     *
     * @param node  the node
     *
     * @return  true if successful
     *
     * @throws Throwable  problems
     */
    protected boolean processTagAnimation(Element node) throws Throwable {
        String  indexString = applyMacros(node, ATTR_INDEX, "0");
        int     index       = -1;
        boolean end         = indexString.equals("end");
        boolean step        = indexString.equals("step");
        if ( !end && !step) {
            index = new Integer(indexString).intValue();
        }
        for (ViewManager viewManager : getViewManagers(node)) {
            AnimationWidget animationWidget =
                viewManager.getAnimationWidget();
            if (animationWidget == null) {
                continue;
            if (end) {
                animationWidget.gotoEnd();
            } else if (step) {
                animationWidget.stepForward();
            } else {
                animationWidget.gotoIndex(index);
            }
        }
        return true;
    }



    /**
     * Process the viewpoint tag
     *
     * @param node  the node
     *
     * @return  true if successful
     * @throws Throwable  problems
     */
    protected boolean processTagViewpoint(Element node) throws Throwable {

        List vms = getViewManagers(node);
        if (vms.size() == 0) {
            debug("Could not find view managers processing:"
                  + XmlUtil.toString(node));
        }
        ViewpointInfo viewpointInfo = null;


        for (int i = 0; i < vms.size(); i++) {
            ViewManager vm = (ViewManager) vms.get(i);
            if (XmlUtil.hasAttribute(node, ATTR_VIEWDIR)) {
                vm.setView(XmlUtil.getAttribute(node, ATTR_VIEWDIR));
            }

            if (XmlUtil.hasAttribute(node, ATTR_AZIMUTH)
                    || XmlUtil.hasAttribute(node, ATTR_TILT)) {
                viewpointInfo = new ViewpointInfo(toDouble(node,
                        ATTR_AZIMUTH, 0.0), toDouble(node, ATTR_TILT, 0.0));
                if ( !(vm instanceof MapViewManager)) {
                    continue;
                }
                MapViewManager mvm = (MapViewManager) vm;
                mvm.setViewpointInfo(viewpointInfo);
            }

            if (XmlUtil.hasAttribute(node, ATTR_ASPECTX)
                    || XmlUtil.hasAttribute(node, ATTR_ASPECTY)
                    || XmlUtil.hasAttribute(node, ATTR_ASPECTZ)) {
                double[] a = vm.getMaster().getDisplayAspect();
                a = new double[] { toDouble(node, ATTR_ASPECTX, a[0]),
                                   toDouble(node, ATTR_ASPECTY, a[1]),
                                   toDouble(node, ATTR_ASPECTZ, a[2]) };
                vm.getMaster().setDisplayAspect(a);
                vm.setAspectRatio(a);
            }

            if (XmlUtil.hasAttribute(node, ATTR_ROTX)
                    || XmlUtil.hasAttribute(node, ATTR_ROTY)
                    || XmlUtil.hasAttribute(node, ATTR_ROTZ)
                    || XmlUtil.hasAttribute(node, ATTR_TRANSX)
                    || XmlUtil.hasAttribute(node, ATTR_TRANSY)
                    || XmlUtil.hasAttribute(node, ATTR_TRANSZ)
                    || XmlUtil.hasAttribute(node, ATTR_SCALE)) {
                double[]      a = vm.getMaster().getDisplayAspect();
                double[]      currentMatrix = vm.getDisplayMatrix();
                double[]      trans         = { 0.0, 0.0, 0.0 };
                double[]      rot           = { 0.0, 0.0, 0.0 };
                double[]      scale         = { 0.0, 0.0, 0.0 };

                MouseBehavior mb = vm.getMaster().getMouseBehavior();
                mb.instance_unmake_matrix(rot, scale, trans, currentMatrix);
                double scaleValue = applyMacros(node, ATTR_SCALE, 0.0);
                if (scaleValue != 0) {
                    double scaleX = scaleValue * a[0];
                    double scaleY = scaleValue * a[1];
                    double scaleZ = scaleValue * a[2];
                    double[] scaleMatrix = mb.make_matrix(0.0, 0.0, 0.0,
                                               scaleX / scale[0],
                                               scaleY / scale[1],
                                               scaleZ / scale[2], 0.0, 0.0,
                                               0.0);
                    currentMatrix = mb.multiply_matrix(scaleMatrix,
                            currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }
                double[] tmp;
                double   dummy = 0.0;
                //TODO: the rotation maybe doesn't work
                if (XmlUtil.hasAttribute(node, ATTR_ROTX)) {
                    tmp = mb.make_matrix(applyMacros(node, ATTR_ROTX, dummy),
                                         0.0, 0.0, 1.0, 0.0, 0.0, 0.0);
                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }
                if (XmlUtil.hasAttribute(node, ATTR_ROTY)) {
                    tmp = mb.make_matrix(0.0,
                                         applyMacros(node, ATTR_ROTY, dummy)
            } else {
                                         - rot[1], 0.0, 1.0, 0.0, 0.0, 0.0);

                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }
                if (XmlUtil.hasAttribute(node, ATTR_ROTZ)) {
                    tmp = mb.make_matrix(0.0, 0.0,
                                         applyMacros(node, ATTR_ROTZ, dummy)
                                         - rot[2], 1.0, 0.0, 0.0, 0.0);

                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }

                if (XmlUtil.hasAttribute(node, ATTR_TRANSX)) {
                    tmp = mb.make_translate(applyMacros(node, ATTR_TRANSX,
                            dummy) - trans[0], 0.0, 0.0);
                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }
                if (XmlUtil.hasAttribute(node, ATTR_TRANSY)) {
                    tmp = mb.make_translate(0.0,
                                            applyMacros(node, ATTR_TRANSY,
                                                dummy) - trans[1], 0.0);
                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }

                if (XmlUtil.hasAttribute(node, ATTR_TRANSZ)) {
                    tmp = mb.make_translate(0.0, 0.0,
                                            applyMacros(node, ATTR_TRANSZ,
                                                dummy) - trans[2]);
                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }


                vm.setDisplayMatrix(currentMatrix);

            }
        }
        return true;

    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagCenter(Element node) throws Throwable {
        List vms = getViewManagers(node);
        if (XmlUtil.hasAttribute(node, ATTR_LAT)) {
            getVMManager().center(
                ucar.visad.Util.makeEarthLocation(
                    toDouble(node, ATTR_LAT), toDouble(node, ATTR_LON)), vms);
            return true;
        }


        if (XmlUtil.hasAttribute(node, ATTR_NORTH)) {
            ProjectionRect projRect = new ProjectionRect(toDouble(node,
                                          ATTR_WEST), toDouble(node,
                                              ATTR_NORTH), toDouble(node,
                                                  ATTR_EAST), toDouble(node,
                                                      ATTR_SOUTH));
            getVMManager().center(projRect, vms);
            return true;
        }

        if (XmlUtil.hasAttribute(node, ATTR_DISPLAY)) {
            DisplayControlImpl display = findDisplayControl(node);
            if (display == null) {
                throw new IllegalArgumentException("Could not find display:"
                        + XmlUtil.toString(node));
            }
            if (XmlUtil.getAttribute(node, ATTR_USEPROJECTION, false)) {
                MapProjection mp = display.getDataProjection();
                if (mp != null) {
                    getVMManager().center(mp, vms);
                }
                LatLonPoint llp = display.getDisplayCenter();
                if (llp != null) {
                    getVMManager().center(
                        ucar.visad.Util.makeEarthLocation(llp), vms);
                }
            }
            return true;
        }

        getVMManager().center(vms);
        return true;
    }


    /**
     * Find the data source that is identified by the given xml node
     *
     * @param node node
     *
     * @return The data source or null
     */
    private DataSource findDataSource(Element node) {
        String id = XmlUtil.getAttribute(node, ATTR_DATASOURCE);
        return findDataSource(id);
    }

    /**
     * Find the data source with the given id
     *
     * @param id the id we pass to datasource.identifiedByName
     *
     * @return The data source or null if none found
     */
    private DataSource findDataSource(String id) {
        List       dataSources = getIdv().getDataSources();
        DataSource dataSource  = (DataSource) idToDataSource.get(id);
        if (dataSource != null) {
            return dataSource;
        }
        for (int i = 0; i < dataSources.size(); i++) {
            dataSource = (DataSource) dataSources.get(i);
            if (dataSource.identifiedByName(id)) {
                return dataSource;
            }
        }
        return null;
    }




    /**
     * Find the display control that is identified by the given xml node
     *
     * @param node node
     *
     * @return The display control source or null
     */
    private DisplayControlImpl findDisplayControl(Element node) {
        String id = XmlUtil.getAttribute(node, ATTR_DISPLAY);
        return findDisplayControl(id);
    }


    /**
     * Find the display control identified by the given id
     *
     * @param id The id of the display control. This can be the id or it can be a 'class:class name'
     *
     * @return The display control or null
     */
    public DisplayControlImpl findDisplayControl(String id) {
        List controls = getIdv().getDisplayControls();
        return findDisplayControl(id, controls);
    }


    /**
     * Find the display control
     *
     * @param id  the control id
     * @param controls  the list of controls
     *
     * @return  the control or null
     */
    public DisplayControlImpl findDisplayControl(String id,
            List controls) {
        for (int i = 0; i < controls.size(); i++) {
        /*            System.err.println(
            DisplayControlImpl control = (DisplayControlImpl) controls.get(i);

            if (id.startsWith("class:")) {
                if (StringUtil.stringMatch(control.getClass().getName(),
                                           id.substring(6), true, true)) {
                    return control;
                }
            }
            if (control.getId() != null) {
                if (StringUtil.stringMatch(control.getId(), id, true, true)) {
                    return control;
                }
            }
        }
        return null;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagBundle(Element node) throws Throwable {

        List timesList = null;
        List ensList   = null;
        if (XmlUtil.hasAttribute(node, ATTR_TIMES)) {
            timesList = StringUtil.parseIntegerListString(applyMacros(node,
                    ATTR_TIMES, (String) null));
        }
        if (XmlUtil.hasAttribute(node, ATTR_ENSEMBLES)) {
            ensList = StringUtil.parseIntegerListString(applyMacros(node,
                    ATTR_ENSEMBLES, (String) null));
        }


        List nodes    = XmlUtil.findChildren(node, TAG_SETFILES);
        List ids      = new ArrayList();
        List fileList = new ArrayList();
        getPersistenceManager().clearFileMapping();
        for (int i = 0; i < nodes.size(); i++) {
            Element child       = (Element) nodes.get(i);
            String dataSourceId = XmlUtil.getAttribute(child,
                                      ATTR_DATASOURCE);
            ids.add(dataSourceId);
            List files = new ArrayList();
            if (XmlUtil.hasAttribute(child, ATTR_FILE)) {
                String file = applyMacros(child, ATTR_FILE);
                debug("For data source: " + dataSourceId + " Using file: "
                      + file);
                files.add(file);
            } else {
                List filesetFiles = findFiles(child);
                if (filesetFiles != null) {
                    debug("For data source: " + dataSourceId
                          + " Using file: " + filesetFiles);
                    files.addAll(filesetFiles);
                } else {
                    debug("For data source: " + dataSourceId
                          + " Could not find any files");
                }
            }
            fileList.add(files);
            debug("Adding a file override id=" + dataSourceId + " files="
                  + files);
        }
        if (ids.size() > 0) {
            getPersistenceManager().setFileMapping(ids, fileList);
        }


        String width  = applyMacros(node, ATTR_WIDTH, (String) null);
        String height = applyMacros(node, ATTR_HEIGHT, (String) null);
        if ((width != null) && (height != null)) {
            getIdv().getStateManager().setViewSize(
                new Dimension(
                    new Integer(width).intValue(),
                    new Integer(height).intValue()));
        }
        String  bundleFile = applyMacros(node, ATTR_FILE, (String) null);
        boolean doRemove   = applyMacros(node, ATTR_CLEAR, true);
        if (doRemove) {
            //            try {
            cleanup();
            //            } catch(Exception exc) {
            //                System.err.println ("Error cleanup");
            //                System.exit(1);
            //            }
        }
    /**
        getIdv().getStateManager().setAlwaysLoadBundlesSynchronously(true);
        Hashtable bundleProperties = new Hashtable();
        if (timesList != null) {
            bundleProperties.put(IdvPersistenceManager.PROP_TIMESLIST,
                                 timesList);
        }
        if (ensList != null) {
            bundleProperties.put(IdvPersistenceManager.PROP_ENSLIST, ensList);
        }


        if (bundleFile != null) {
            debug("Loading bundle: " + bundleFile);
            if (bundleFile.endsWith(".jnlp")) {
                String xml =
                    getPersistenceManager().extractBundleFromJnlp(bundleFile);
     *
                getPersistenceManager().decodeXml(xml, false, bundleFile,
                        null, false, true, bundleProperties, true, false);
            } else if (getArgsManager().isZidvFile(bundleFile)) {
                Hashtable properties = new Hashtable();
                boolean   ask        = getStore().get(PREF_ZIDV_ASK, true);
                getStore().put(PREF_ZIDV_ASK, false);
                getPersistenceManager().decodeXmlFile(bundleFile, "", false,
                        false, properties);
                getStore().put(PREF_ZIDV_ASK, ask);
            } else {
                String xml = IOUtil.readContents(bundleFile);
                xml = applyMacros(xml, null, false);
                getPersistenceManager().decodeXml(xml, false, bundleFile,
                        null, false, true, bundleProperties, true, false);
                //                getPersistenceManager().decodeXmlFile(bundleFile, false,
                //                        timesList);
            }
        } else {
            String b64Bundle = XmlUtil.getChildText(node).trim();
            if (b64Bundle.length() == 0) {
                return error("Could not bundle");
            }
            String xml = new String(XmlUtil.decodeBase64(b64Bundle));
            getPersistenceManager().decodeXml(xml, false, "", null, false,
                    true, bundleProperties, true, false);

        }

        if (applyMacros(node, ATTR_WAIT, true)) {
            getIdv().getIdvUIManager().waitUntilDisplaysAreDone(
                getIdv().getIdvUIManager());
        }
        getPersistenceManager().clearFileMapping();
        Color c            = applyMacros(node, ATTR_COLOR, (Color) null);
        List  viewManagers = getVMManager().getViewManagers();
        for (int i = 0; i < viewManagers.size(); i++) {
            ViewManager viewManager = (ViewManager) viewManagers.get(i);
            if (c != null) {
                viewManager.setColors(null, c);
            }
            viewManager.updateDisplayList();
        }

        //One more pause for the display lists
        updateViewManagers();
        getIdv().getIdvUIManager().waitUntilDisplaysAreDone(
            getIdv().getIdvUIManager());
        return true;
    }

    /**
     * remove data and displays, etc
     */
    private void cleanup() {
        getIdv().removeAllDisplays(false);
        getIdv().removeAllDataSources();
        idToDataSource = new Hashtable();
        ucar.unidata.util.CacheManager.clearCache();

        //        getIdv().getIdvUIManager().disposeAllWindows();
        if (getIdv().getArgsManager().getIsOffScreen()) {
            getIdv().getVMManager().removeAllViewManagers(true);
        }
        getIdv().getIdvUIManager().clearWaitCursor();

        double totalMemory   = (double) Runtime.getRuntime().maxMemory();
        double highWaterMark = (double) Runtime.getRuntime().totalMemory();
        double freeMemory    = (double) Runtime.getRuntime().freeMemory();
        double usedMemory    = (highWaterMark - freeMemory);
        totalMemory   = totalMemory / 1000000.0;
        usedMemory    = usedMemory / 1000000.0;
        highWaterMark = highWaterMark / 1000000.0;

                      "MEM:" + ((int) usedMemory) + "/" + ((int) highWaterMark)
                      + " vms:" + getIdv().getVMManager().getViewManagers().size());
        */

    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagCall(Element node) throws Throwable {
        String  name     = applyMacros(node, ATTR_NAME);
        Element procNode = (Element) procs.get(name);
        return processTagCall(node, procNode);
    }

    /**
     * process the given node
     *
     * @param node Node to process
     * @param procNode The procedure node
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagCall(Element node, Element procNode)
            throws Throwable {
        if (procNode == null) {
            return error("Could not find procedure node for call:"
                         + XmlUtil.toString(node));
        }

        pushProperties();
        String cdata = XmlUtil.getChildText(node);
        if ((cdata != null) && (cdata.trim().length() > 0)) {
            putProperty("paramtext", cdata);
        } else {
            putProperty("paramtext", "");
        }

        NamedNodeMap procnnm = procNode.getAttributes();
        if (procnnm != null) {
            for (int i = 0; i < procnnm.getLength(); i++) {
                Attr attr = (Attr) procnnm.item(i);
                if ( !ATTR_NAME.equals(attr.getNodeName())) {
                    putProperty(attr.getNodeName(),
                                applyMacros(attr.getNodeValue()));
                }
            }
        }


        NamedNodeMap nnm = node.getAttributes();
        if (nnm != null) {
            for (int i = 0; i < nnm.getLength(); i++) {
                Attr attr = (Attr) nnm.item(i);
                if ( !ATTR_NAME.equals(attr.getNodeName())) {
                    putProperty(attr.getNodeName(),
                                applyMacros(attr.getNodeValue()));
                }
            }
        }
        try {
            if ( !processChildren(node)) {
                return false;
            }
            try {
                if ( !processChildren(procNode)) {
                    return false;
                }
            } catch (MyReturnException mre) {
                //noop
            }
        } catch (Throwable throwable) {
            popProperties();
            throw throwable;
        }
        popProperties();
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagIf(Element node) throws Throwable {
        String expr = applyMacros(node, ATTR_EXPR, (String) null);
        if (expr == null) {
            expr = applyMacros(XmlUtil.getChildText(node));
        }
        if ((expr == null) || (expr.trim().length() == 0)) {
            return error("Could not find if expression");
        }
        expr = expr.trim();
        boolean result = getInterpreter().eval(expr).toString().equals("1");

        Element thenNode      = XmlUtil.findChild(node, TAG_THEN);
        Element elseNode      = XmlUtil.findChild(node, TAG_ELSE);
        Element statementNode = (result
                                 ? thenNode
                                 : elseNode);
        if (statementNode == null) {
            if (result && (thenNode == null) && (elseNode == null)) {
                statementNode = node;
            } else {
                return true;
            }
        }
        if (statementNode != null) {

            //            pushProperties();
            try {
                if ( !processChildren(statementNode)) {
                    return false;
                }
            } catch (Throwable throwable) {
                //                popProperties();
                throw throwable;
            }
            //            popProperties();
        }
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagOutput(Element node) throws Throwable {
        if ( !XmlUtil.hasAttribute(node, ATTR_FILE)) {
            for (int i = 0; i < outputStack.size(); i++) {
                OutputInfo oi = (OutputInfo) outputStack.get(i);
                oi.process(node);
            }
            return true;
        }
        OutputInfo outputInfo = new OutputInfo(node);
        outputStack.add(outputInfo);
        pushProperties();
        try {
            if ( !processChildren(node)) {
                return false;
            }
        } catch (Throwable throwable) {
            popProperties();
            throw throwable;
        }
        popProperties();
        outputStack.remove(outputStack.size() - 1);
        outputInfo.write();
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagIsl(Element node) throws Throwable {
        debug = applyMacros(node, ATTR_DEBUG, false);
        boolean offScreen = applyMacros(node, ATTR_OFFSCREEN, true);
        //        System.err.println ("offscreen:" + offScreen);
        if ( !getIdv().getArgsManager().getIslInteractive()) {
            //            System.err.println ("setting offscreen:" + offScreen);
            getIdv().getArgsManager().setIsOffScreen(offScreen);
        }
        putProperty(PROP_OFFSCREEN,
                    (getIdv().getArgsManager().getIsOffScreen()
                     ? "1"
                     : "0"));

        //        System.err.println("setting offScreen " +         getIdv().getArgsManager().getIsOffScreen());

        return processTagGroup(node);
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagGroup(Element node) throws Throwable {
        pushProperties();
     * process the given node
        int    loopTimes   = applyMacros(node, ATTR_LOOP, 1);
        String sleepString = applyMacros(node, ATTR_SLEEP, (String) null);
        long   sleepTime   = 0;
        if (sleepString != null) {
            sleepString = sleepString.trim();
            long   multiplier = 1000;
            String unit = StringUtil.findPattern(sleepString, "[0-9.]+(.*)$");

            if ((unit != null) && (unit.trim().length() > 0)) {
                sleepString = sleepString.substring(0,
                        sleepString.length() - unit.length());
                if (unit.equals("s")) {}
                else if (unit.equals("seconds")) {}

                else if (unit.equals("minutes")) {
                    multiplier = 60 * 1000;
                } else if (unit.equals("m")) {
                    multiplier = 60 * 1000;
                } else if (unit.equals("hours")) {
                    multiplier = 60 * 60 * 1000;
                } else if (unit.equals("h")) {
                    multiplier = 60 * 60 * 1000;
                } else {
                    return error("Unknown sleep time unit:" + unit);
                }
            }
            sleepTime = (long) (multiplier
                                * new Double(sleepString).doubleValue());
        }
        for (int i = 0; i < loopTimes; i++) {
            currentLoopIndex = i;
            try {
                if ( !processChildren(node)) {
                    return false;
                }
            } catch (MyBreakException be) {
                break;
            } catch (MyContinueException ce) {}
            if ((loopTimes > 1) && (sleepTime > 0)) {
                Misc.sleep(sleepTime);
            }
        }
        popProperties();

        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagForeach(Element node) throws Throwable {
        pushProperties();
        List         allValues   = new ArrayList();
        int          numElements = 0;
        int          cnt         = 1;
        NamedNodeMap attrs       = node.getAttributes();
        if (attrs == null) {
            return error("No values in foreach tag");
        }

        for (int i = 0; i < attrs.getLength(); i++) {
            Attr   attr   = (Attr) attrs.item(i);
            String var    = attr.getNodeName();
            String values = applyMacros(attr.getNodeValue());
            List   tokens;
            //Check if it starts with file:, if so read the contents and split on new line
            if (values.startsWith("file:")) {
                String filename =
                    applyMacros(values.substring("file:".length()));
                values = IOUtil.readContents(filename, getClass()).trim();
                tokens = StringUtil.split(values, "\n");
            } else {
                tokens = StringUtil.split(values, ",");
            }

            if (allValues.size() == 0) {
                numElements = tokens.size();
            } else if (numElements != tokens.size()) {
                return error("Bad number of tokens (" + tokens.size()
                             + " should be:" + numElements
                             + ") in foreach argument:\n" + var + "="
                             + tokens);
            }
            allValues.add(new Object[] { var, tokens });
            cnt++;
        }
        for (int tokIdx = 0; tokIdx < numElements; tokIdx++) {
            for (int valueIdx = 0; valueIdx < allValues.size(); valueIdx++) {
     *
                Object[] tuple = (Object[]) allValues.get(valueIdx);
                putProperty(tuple[0], ((List) tuple[1]).get(tokIdx));
            }
            try {
                if ( !processChildren(node)) {
                    return false;
                }
            } catch (MyBreakException be) {
                break;
            } catch (MyContinueException ce) {}
        }
        popProperties();
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagMovie(Element node) throws Throwable {
        pushProperties();
        captureMovie(null, node);
        popProperties();
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagHtml(Element node) throws Throwable {

        String html = null;
        if (XmlUtil.hasAttribute(node, ATTR_FROMFILE)) {
            html = IOUtil.readContents(applyMacros(node, ATTR_FROMFILE));
        } else {
            html = XmlUtil.getChildText(node);
        }
        html = applyMacros(html);
        int   width = XmlUtil.getAttribute(node, ATTR_WIDTH, -1);
        Image image = ImageUtils.renderHtml(html, width, null, null);
        image = processImage(ImageUtils.toBufferedImage(image),
                             XmlUtil.getAttribute(node, ATTR_FILE), node,
                             getAllProperties(), null, new Hashtable());

        //        writeImageToFile(image, XmlUtil.getAttribute(node, ATTR_FILE));
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagPanel(Element node) throws Throwable {
        pushProperties();
        captureMovie(null, node);
        popProperties();
        return true;
    }


    /**
     * Parse the xml
     *
     * @param xml the xml
     *
     * @return the root
     *
     * @throws Exception On badness
     */
    private Element makeElement(String xml) throws Exception {
        return XmlUtil.getRoot(xml);
    }


    /**
     * Capture a movie and write it out. This is typically called by the jython scripting
     *
     * @param filename Movie filename
     * @param params xml parameters of the the form:  "task arg=val arg2=val; task2 arg3=val"
     *
     * @throws Exception On badness
     */
    public void writeMovie(String filename, String params) throws Exception {
        String isl = makeXmlFromString(params);

        String xml = XmlUtil.getHeader()+"\n"
                     + isl + "";
        captureMovie(applyMacros(filename), makeElement(xml));
    }



     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagImage(Element node) throws Throwable {
        captureImage(XmlUtil.getAttribute(node, ATTR_FILE), node);
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     * @throws Throwable On badness
     */
    protected boolean processTagWait(Element node) throws Throwable {
        File f = null;
        if (XmlUtil.hasAttribute(node, ATTR_FILE)) {
            f = new File(applyMacros(node, ATTR_FILE));
        }
        double seconds = applyMacros(node, ATTR_SECONDS, 60.0);
        if ((f != null) && f.isDirectory()) {
            String patternStr = applyMacros(applyMacros(node, ATTR_PATTERN,
                                    (String) null));
            IOUtil.wait(f, patternStr, seconds);
        } else {
            if (f != null) {
                IOUtil.wait(Misc.newList(f), seconds);
            } else {
                IOUtil.wait(findFiles(node), seconds);
            }
        }
        return true;
    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagPause(Element node) throws Throwable {
        if (XmlUtil.hasAttribute(node, ATTR_EVERY)) {
            Misc.pauseEvery((int) (60 * toDouble(node, ATTR_EVERY)));
            return true;
        }
        if (XmlUtil.hasAttribute(node, ATTR_SECONDS)) {
            Misc.sleep((long) (1000 * toDouble(node, ATTR_SECONDS)));
        } else if (XmlUtil.hasAttribute(node, ATTR_MINUTES)) {
            Misc.sleep((long) (60 * 1000 * toDouble(node, ATTR_MINUTES)));
        } else if (XmlUtil.hasAttribute(node, ATTR_HOURS)) {
            Misc.sleep((long) (60 * 60 * 1000 * toDouble(node, ATTR_HOURS)));
        } else {
            updateViewManagers();
            getIdv().getIdvUIManager().waitUntilDisplaysAreDone(
                getIdv().getIdvUIManager());
        }
        return true;

    }

    /**
     * Update the view managers
     */
    protected void updateViewManagers() {
        try {
            List viewManagers = getVMManager().getViewManagers();
            for (int i = 0; i < viewManagers.size(); i++) {
                ViewManager viewManager = (ViewManager) viewManagers.get(i);
                viewManager.updateDisplayIfNeeded();
            }
        } catch (Exception exc) {
            logException("Updating view manager", exc);
        }
    }

    /**
     * Process tag display properties
     *
     * @param node  the node
     *
     * @return true if successful
     */
    protected boolean processTagDisplayproperties(Element node) {
        DisplayControlImpl display = findDisplayControl(node);
        if (display == null) {
            throw new IllegalArgumentException("Could not find display:"
                    + XmlUtil.toString(node));
        }
        Hashtable properties = getProperties(node);
        display.applyProperties(properties);
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     */

    protected boolean processTagDisplay(Element node) {
        if ( !processDisplayNode(node, null)) {
            return false;
        }
        if (applyMacros(node, ATTR_WAIT, true)) {
            pause();
        }
        updateViewManagers();
        return true;
    }

    /**
     * Process the display node. If data source is not null then use that
     * to find data choices. If null then use all loaded data sources to find
     * data choice.
     *
     * @param node Node to process
     * @param dataSource The data source. May be null.
     *
     * @return keep going
     */
    private boolean processDisplayNode(Element node, DataSource dataSource) {

        //TODO:
        String     type       = applyMacros(node, ATTR_TYPE, (String) null);
        String     param      = applyMacros(node, ATTR_PARAM, (String) null);
        DataChoice dataChoice = null;
        debug("Creating display: " + type + " param:" + param);

        if ((dataSource == null)
                && XmlUtil.hasAttribute(node, ATTR_DATASOURCE)) {
            dataSource = findDataSource(node);
            if (dataSource == null) {
                return error("Failed to to find data source for display tag:"
                             + XmlUtil.toString(node));
            }
        }

        if (param != null) {
            if (dataSource != null) {
                dataChoice = dataSource.findDataChoice(param);
            } else {
                List dataSources = getIdv().getDataSources();
                for (int i = 0;
                        (i < dataSources.size()) && (dataChoice == null);
                        i++) {
                    dataSource = (DataSource) dataSources.get(i);
                    dataChoice = dataSource.findDataChoice(param);
                }
            }
            if (dataChoice == null) {
                return error("Failed to find parameter:" + param);
            }
        }
        List dataChoices = new ArrayList();
        if (dataChoice != null) {
            dataChoice = dataChoice.createClone();
            DataSelection dataSelection =
                new DataSelection(applyMacros(node, ATTR_LEVEL_FROM,
                    (String) null), applyMacros(node, ATTR_LEVEL_TO,
                        (String) null));


            processGeoSelectionTags(node, dataSelection);

            String timeString = applyMacros(node, ATTR_TIMES, (String) null);
            if (timeString != null) {
                List times = new ArrayList();
                for (String tok :
                        StringUtil.split(timeString, ",", true, true)) {
                    times.add(new Integer(tok));
                }
                dataSelection.setTimes(times);
            }
            if (XmlUtil.hasAttribute(node, ATTR_ENSEMBLES)) {
                List ensList =
                    StringUtil.parseIntegerListString(applyMacros(node,
                        ATTR_ENSEMBLES, (String) null));
                dataSelection.putProperty(
                    GridDataSource.PROP_ENSEMBLEMEMBERS, ensList);
            }
            dataChoice.setDataSelection(dataSelection);
            dataChoices.add(dataChoice);
        }

        if (type == null) {
            String bundleXml = null;
                    files.add(new File(filename));
            if (XmlUtil.hasAttribute(node, ATTR_TEMPLATE)) {
                String filename = applyMacros(node, ATTR_TEMPLATE);
                try {
                    bundleXml = IOUtil.readContents(filename);
                } catch (IOException exc) {
                    return error("Could not find file: " + filename);
                }
            } else {
                Element templateNode = XmlUtil.findChild(node, TAG_TEMPLATE);
                if (templateNode != null) {
                    bundleXml = XmlUtil.getChildText(templateNode);
                }
            }
            if (bundleXml == null) {
                return error(
                    " tag does not contain type attribute or template attribute/tag");
            }
            try {
                Object obj = getIdv().getEncoderForRead().toObject(bundleXml);

                if ( !(obj instanceof DisplayControl)) {
                    return error("display template is not a DisplayControl:"
                                 + obj.getClass().getName());
                }
                DisplayControl displayControl = (DisplayControl) obj;
                displayControl.initAfterUnPersistence(getIdv(),
                        getProperties(node), dataChoices);
                getIdv().addDisplayControl(displayControl);
            } catch (Exception exc) {
                return error("Creating display", exc);
            }
        } else {
            ControlDescriptor cd = getIdv().getControlDescriptor(type);
            if (cd == null) {
                return error("Failed to find display control:" + type);
            }
            Trace.call1("ImageGenerator making display");
            getIdv().doMakeControl(dataChoices, cd, getProperties(node),
                                   null, false);
            Trace.call2("ImageGenerator making display");
        }
        return true;

    }

    /**
     * Process geo selection tags.
     *
     * @param node the node
     * @param dataSelection the data selection
     * @return true, if successful
     */
    private boolean processGeoSelectionTags(Element node,
                                            DataSelection dataSelection) {
        String strideString = applyMacros(node, ATTR_STRIDE, (String) null);
        if (strideString != null) {
            dataSelection.getGeoSelection(true).setXStride(applyMacros(node,
                    ATTR_STRIDE, 1));
            dataSelection.getGeoSelection(true).setYStride(applyMacros(node,
                    ATTR_STRIDE, 1));

        }


        String strideXString = applyMacros(node, ATTR_STRIDEX, (String) null);
        if (strideXString != null) {
            dataSelection.getGeoSelection(true).setXStride(applyMacros(node,
                    ATTR_STRIDEX, 1));

        }

        String strideYString = applyMacros(node, ATTR_STRIDEY, (String) null);
        if (strideYString != null) {
            dataSelection.getGeoSelection(true).setYStride(applyMacros(node,
                    ATTR_STRIDEY, 1));

        }
        String strideZString = applyMacros(node, ATTR_STRIDEZ, (String) null);
        if (strideZString != null) {
            dataSelection.getGeoSelection(true).setZStride(applyMacros(node,
                    ATTR_STRIDEX, 1));

        }

        String bboxString = applyMacros(node, ATTR_BBOX, (String) null);
        if (bboxString != null) {
            List toks = StringUtil.split(bboxString, ",", true, true);
            if (toks.size() != 4) {
                return error("Bad idv.data.geosubset property:" + bboxString);
            } else {
                GeoLocationInfo boundingBox =
                    new GeoLocationInfo(

                        Misc.decodeLatLon((String) toks.get(0)),
                        Misc.decodeLatLon((String) toks.get(1)),
                        Misc.decodeLatLon((String) toks.get(2)),
                        Misc.decodeLatLon((String) toks.get(3)));
                dataSelection.getGeoSelection(true).setBoundingBox(
                    boundingBox);
            }
        }
        return true;
    }


    /**
     * Process the property tag children of the given node
     *
     * @param node parent node that holds property tags
     *
     * @return properties
     */
    private Hashtable getProperties(Element node) {
        Hashtable properties = new Hashtable();
        List      nodes      = XmlUtil.findChildren(node, TAG_PROPERTY);
        for (int i = 0; i < nodes.size(); i++) {
            Element child = (Element) nodes.get(i);
            properties.put(applyMacros(child, ATTR_NAME),
                           applyMacros(child, ATTR_VALUE));
        }
        return properties;
    }


    /**
     * Utility to print a message and return false.
     *
     * @param msg message
     *
     * @return false
     */
    protected boolean error(String msg) {
        if ( !getIdv().getArgsManager().getIsOffScreen()) {
            LogUtil.userErrorMessage(msg);
        } else {
            System.err.println(msg);
        }
        return false;
    }



    /**
     * Utility to print a message and return false.
     *
     * @param msg message
     * @param exc exception
     *
     * @return false
     */
    protected boolean error(String msg, Exception exc) {
        if ( !getIdv().getArgsManager().getIsOffScreen()) {
            LogUtil.logException(msg, exc);
        } else {
            exc.printStackTrace();
            System.err.println(msg);
        }
        return false;
    }


    /**
     * Find all of the files that are defined by contained fileset nodes.
     *
     * @param parentNode Node to process
     *
     * @return List of files
     */
    private List findFiles(Element parentNode) {
        List resultFiles = null;
        List filesets    = XmlUtil.findChildren(parentNode, TAG_FILESET);
        if (filesets.size() > 0) {
            if (resultFiles == null) {
                resultFiles = new ArrayList();
            }
            resultFiles.addAll(findFiles(filesets));
        }
        if (resultFiles == null) {
            return null;
        }
        return resultFiles;
    }


    /**
     * Find all of the files that are defined by any fileset nodes in the nodes list..
     *
     *
     * @param nodes List of nodes
     *
     * @return List of files
     */
    private List findFiles(List nodes) {
        List files = new ArrayList();
        for (int i = 0; i < nodes.size(); i++) {
            Element node = (Element) nodes.get(i);
            if (node.getTagName().equals(TAG_FILESET)) {
                String filename = applyMacros(node, ATTR_FILE, (String) null);
                if (filename != null) {
                    continue;
                }
                File dir = new File(applyMacros(node, ATTR_DIR, "."));
                String pattern = applyMacros(applyMacros(node, ATTR_PATTERN,
                                     (String) null));
                File[] allFiles = ((pattern == null)
                                   ? dir.listFiles()
                                   : dir.listFiles(
                                       (java.io
                                           .FileFilter) new PatternFileFilter(
                                               pattern)));
                if (allFiles == null) {
                    continue;
                }
                List tmpFiles = new ArrayList();
                for (int fileIdx = 0; fileIdx < allFiles.length; fileIdx++) {
                    if ( !allFiles[fileIdx].isDirectory()) {
                        if ( !files.contains(allFiles[fileIdx])) {
                            tmpFiles.add(allFiles[fileIdx]);
                        }
                    }
                }

                String sort = applyMacros(node, ATTR_SORT, (String) null);
                String sortDir = applyMacros(node, ATTR_SORTDIR,
                                             VALUE_ASCENDING);
                if (sort != null) {
                    if (sort.equals(VALUE_TIME)) {
                        if (sortDir.equals(VALUE_ASCENDING)) {
                            tmpFiles = Misc.toList(
                                IOUtil.sortFilesOnAge(
                                    IOUtil.toFiles(tmpFiles), false));
                        } else if (sortDir.equals(VALUE_DESCENDING)) {
                            tmpFiles = Misc.toList(
                                IOUtil.sortFilesOnAge(
                                    IOUtil.toFiles(tmpFiles), true));
                        } else {
                            System.err.println("unknown sort direction:"
                                    + sortDir);
                        }
                    } else {
                        System.err.println("unknown sort type:" + sort);
                    }
                }


                if (XmlUtil.hasAttribute(node, ATTR_FIRST)) {
                    int first = applyMacros(node, ATTR_FIRST, 0);
                    if (first < tmpFiles.size()) {
                        List tmp = new ArrayList();
                        for (int fileIdx = 0; fileIdx < first; fileIdx++) {
                            tmp.add(tmpFiles.get(fileIdx));
                        }
                        tmpFiles = tmp;

                    }
                } else if (XmlUtil.hasAttribute(node, ATTR_LAST)) {
                    int last = applyMacros(node, ATTR_LAST, 0);
                    if (last < tmpFiles.size()) {
                        List tmp = new ArrayList();
                        for (int fileIdx = tmpFiles.size() - 1; fileIdx >= 0;
                                fileIdx--) {
                            tmp.add(0, tmpFiles.get(fileIdx));
                            if (tmp.size() >= last) {
                                break;
                            }
                        }
                        tmpFiles = tmp;
                    }
                }
                files.addAll(tmpFiles);


            }
        }
        return files;
    }


    /**
     * Put the property in the current properties hashtable
     *
     * @param key key
     * @param value value
     */
    private void putProperty(Object key, Object value) {
        putProperty(key, value, false);
    }

    /**
     * Put the property in the current properties hashtable
     *
     * @param key key
     * @param value value
     * @param global If true put it in the base stack frame
     */
    private void putProperty(Object key, Object value, boolean global) {
        Hashtable properties = (global
                                ? (Hashtable) propertiesStack.get(0)
                                : getProperties());
        properties.put(key, value);
    }


    /**
     * Find the property table for the given key
     *
     * @param key The key
     *
     * @return The properties table. If none found then it returns the top of the stack
     */
    private Hashtable findTableFor(Object key) {
        for (int i = propertiesStack.size() - 1; i >= 0; i--) {
            Hashtable properties = (Hashtable) propertiesStack.get(i);
            if (properties.get(key) != null) {
                return properties;
            }
        }
        return getProperties();
    }

    /**
     * Find the table that contains the given property and replace it with the new value
     *
     * @param key key
     * @param value new value
     */
    private void replaceProperty(Object key, Object value) {
        findTableFor(key).put(key, value);
    }


    /**
     * Get the top most hashtable in the properties stack.
     *
     * @return Current properties hashtable.
     */
    private Hashtable getProperties() {
        if (propertiesStack.size() == 0) {
            return new Hashtable();
        }
        return (Hashtable) propertiesStack.get(propertiesStack.size() - 1);
    }


    /**
     * Add a Hashtable to the properties stack.
     *
     * @return The newly created hashtable.
     */
    private Hashtable pushProperties() {
        Hashtable properties = new Hashtable();
        propertiesStack.add(properties);
        return properties;
    }


    /**
     * Remove the top most hashtable in the properties stack
     */
    private void popProperties() {
        propertiesStack.remove(propertiesStack.size() - 1);
    }


    /**
     * utility to convert a string to a double. If the string ends with '%'
     * then return the percentage of the given base value
     *
     *
     * @param node Node to process
     * @param attr The attribute to look up
     * @param baseValue Used to handle '%'
     * @return The value
     */
    private double toDouble(Element node, String attr, double baseValue) {
        String s = applyMacros(node, attr);
        return toDouble(s, baseValue);
    }


    /**
     * utility to make a double. If the string begins with '%' then we take a percentage of the baseValue
     *
     * @param s string
     * @param baseValue Used if s is a percentage
     *
     * @return double value
     */
    private double toDouble(String s, double baseValue) {
        if (s.endsWith("%")) {
            double percent = Misc.toDouble(s.substring(0, s.length() - 1));
            return (percent / 100.0) * baseValue;
        }
        return new Double(s).doubleValue();
    }


    /**
     * Convert the attribute value of the given node to a double
     *
     * @param node Node to process
     * @param attr Attribute name
     *
     * @return double value
     */
    private double toDouble(Element node, String attr) {
        return Misc.toDouble(applyMacros(node, attr));
    }


    /**
     * Find the attribute  value of the given node. Apply the macros to it.
     *
     * @param node Node to process
     * @param attr Attribute name
     *
     * @return The value
     */
    }
    public String applyMacros(Element node, String attr) {
        return applyMacros(XmlUtil.getAttribute(node, attr));
    }


    /**
     * If the attribute does not exist return the dflt. Else return the value.
     *
     * @param node Node to process
     * @param attr Attribute name
     * @param dflt The default value to use if the attribute does not exist
     *
     * @return The value
     */
    public String applyMacros(Element node, String attr, String dflt) {
        return applyMacros(XmlUtil.getAttribute(node, attr, dflt));
    }


    /**
     * If the attribute does not exist return the dflt. Else return the value.
     *
     * @param node Node to process
     * @param attr Attribute name
     * @param dflt The default value to use if the attribute does not exist
     *
     * @return The value
     */
    public int applyMacros(Element node, String attr, int dflt) {
        String value = XmlUtil.getAttribute(node, attr, (String) null);
        if (value == null) {
            return dflt;
        }
        return (int) Misc.toDouble(applyMacros(value));
    }

    /**
     * If the attribute does not exist return the dflt. Else return the value.
     *
     * @param node Node to process
     * @param attr Attribute name
     * @param dflt The default value to use if the attribute does not exist
     *
     * @return The value
     */
    public boolean applyMacros(Element node, String attr, boolean dflt) {
        String value = XmlUtil.getAttribute(node, attr, (String) null);
        if (value == null) {
            return dflt;
        }
        return new Boolean(applyMacros(value)).booleanValue();
    }




    /**
     * If the attribute does not exist return the dflt. Else return the value.
     *
     * @param node Node to process
     * @param attr Attribute name
     * @param dflt The default value to use if the attribute does not exist
     *
     * @return The value
     */
    public Color applyMacros(Element node, String attr, Color dflt) {
        String value = XmlUtil.getAttribute(node, attr, (String) null);
        if (value == null) {
            return dflt;
        }
        String result = applyMacros(value);
        if (result.equals("none")) {
            return null;
        }
        return GuiUtils.decodeColor(result, dflt);
    }


    /**
     * If the attribute does not exist return the dflt. Else return the value.
     *
     * @param node Node to process
     * @param attr Attribute name
     * @param dflt The default value to use if the attribute does not exist
     *
     * @return The value
     */
    public double applyMacros(Element node, String attr, double dflt) {
        String value = XmlUtil.getAttribute(node, attr, (String) null);
        if (value == null) {
            return dflt;
        }
        return Misc.toDouble(applyMacros(value));
    }



    /**
     * Do the macro substitution
     *
     * @param s The string
     *
     * @return The expanded string
     */
    public String applyMacros(String s) {
        return applyMacros(s, null);

    /**
     * Merge all of the proeprties together
     *
     * @return The properties
     */
    private Hashtable getAllProperties() {
        Hashtable props = new Hashtable();
        for (int i = 0; i < propertiesStack.size(); i++) {
            Hashtable properties = (Hashtable) propertiesStack.get(i);
            props.putAll(properties);
        }
        return props;
    }


    /**
     * Do the macro substitution
     *
     * @param s The string
     * @param props Properties
     *
     * @return The expanded string
     */
    private String applyMacros(String s, Hashtable props) {
        return applyMacros(s, props, true);
    }

    /**
     * Do the macro substitution
     *
     * @param s The string
     * @param props Properties
     * @param doTime  process time macros
     *
     * @return The expanded string
     */
    private String applyMacros(String s, Hashtable props, boolean doTime) {
        if (s == null) {
            return null;
        }
        if (props == null) {
            props = new Hashtable();
        } else {
            Hashtable tmp = props;
            props = new Hashtable();
            props.putAll(tmp);
        }
        props.putAll(getAllProperties());

        putIndex(props, PROP_LOOPINDEX, currentLoopIndex);
        props.put(PROP_LOOPINDEX_PAD2,
                  StringUtil.padLeft("" + currentLoopIndex, 2, "0"));
        props.put(PROP_LOOPINDEX_PAD3,
                  StringUtil.padLeft("" + currentLoopIndex, 3, "0"));
        props.put(PROP_LOOPINDEX_PAD4,
                  StringUtil.padLeft("" + currentLoopIndex, 4, "0"));

        Date now = new Date(Misc.getCurrentTime());


        if (DATE_FORMATS == null) {
            TimeZone timeZone = TimeZone.getTimeZone("GMT");
            DATE_FORMATS = new ArrayList();
            for (int i = 0; i < DATE_PROPS.length; i++) {
                SimpleDateFormat sdf = new SimpleDateFormat(DATE_PROPS[i]);
                sdf.setTimeZone(timeZone);
                DATE_FORMATS.add(sdf);
            }
        }

        for (int i = 0; i < DATE_FORMATS.size(); i++) {
            SimpleDateFormat sdf = (SimpleDateFormat) DATE_FORMATS.get(i);
            props.put(DATE_PROPS[i], sdf.format(now));
        }


        props.put("memory", "" + Misc.usedMemory());


        /*
        if (s.indexOf("${anim:") >= 0) {
            now = getAnimationTime();
            if (now != null) {
                for (int i = 0; i < DATE_FORMATS.size(); i++) {
                    SimpleDateFormat sdf =
                        (SimpleDateFormat) DATE_FORMATS.get(i);
                    props.put("anim:" + DATE_PROPS[i], sdf.format(now));
                }

            }
            }*/


        TimeZone tz = getIdv().getPreferenceManager().getDefaultTimeZone();


        s = StringUtil.replaceDate(s, "now:", now, tz);

        if ((s.indexOf("anim:") >= 0) || (s.indexOf("time:") >= 0)) {
            Date animationTime = getAnimationTime();
            if (animationTime == null) {
                animationTime = now;
            }
            if (doTime) {
                s = StringUtil.replaceDate(s, "anim:", animationTime, tz);
                s = StringUtil.replaceDate(s, "time:", animationTime, tz);
                s = StringUtil.replaceDate(s, "now:", now, tz);
            }
        }
        s = StringUtil.applyMacros(s, props, false);
        //Now use the idv properties
        s = StringUtil.applyMacros(s, getStateManager().getProperties(),
                                   false);
        if (s.indexOf("${") >= 0) {
            throw new BadIslException("Undefined macro in: " + s);
        }

        if (s.startsWith("jython:")) {
            Object result = getInterpreter().eval(s.substring(7));
            s = result.toString();
        }

        if (s.startsWith("interp.")) {
            Object result = getInterpreter().eval(s);
            s = result.toString();
        }
        if (s.startsWith("islInterpreter.")) {
            Object result = getInterpreter().eval(s);
            s = result.toString();
        }
        s = s.replace("\\n", "\n");
        return s;
    }




    /**
     * Capture an image from the first active view managers
     *
     * @param filename The image filename
     */
    public void captureImage(String filename) {
        try {
            captureImage(filename, null);
        } catch (Throwable exc) {
            logException("Capturing image", exc);
        }
    }


    /**
     * Put the index
     *
     * @param props  the properties
     * @param name the name
     * @param v  the index
     */
    public void putIndex(Hashtable props, String name, int v) {
        props.put(name, new Integer(v));
        props.put(name + "_alpha", getLetter(v).toLowerCase());
        props.put(name + "_ALPHA", getLetter(v).toUpperCase());
        
        props.put(name + "_ROMAN", getRoman(v).toUpperCase());
        props.put(name + "_roman", getRoman(v).toLowerCase());


    }


    /**
     * Find all view managers that are identified by the given xml node. If the node
     * does not have a "view" attribute return all view managers. Else use the view
     * attribute to find the vms. The view can be class:class name or just a name. If a name can
     * also be a regular expression
     *
     * @param node node
     *
     * @return List of view managers
     */
    private List getViewManagers(Element node) {
        List viewManagers =
            (List) getVMManager().getViewManagers();
        if ((node == null) || !XmlUtil.hasAttribute(node, ATTR_VIEW)) {
            return viewManagers;
        }

        List   goodOnes = new ArrayList();
        String viewId   = applyMacros(node, ATTR_VIEW);
        //        System.err.println ("viewManagers:" + viewManagers);
        if (viewId.startsWith("name:")) {
            viewId = viewId.substring(5);
        }

        for (int i = 0; i < viewManagers.size(); i++) {
            ViewManager viewManager = (ViewManager) viewManagers.get(i);
            if (viewId.startsWith("#")) {
                int viewIndex = new Integer(viewId.substring(1)).intValue();
                if (viewIndex == i) {
                    goodOnes.add(viewManager);
                    //                    System.err.println("\tskipping index");
                }
                continue;
            }
            if (viewId.startsWith("class:")) {
                if (StringUtil.stringMatch(viewManager.getClass().getName(),
                                           viewId.substring(6), true, true)) {
                    goodOnes.add(viewManager);
                }
                continue;
            }
            String name = viewManager.getName();
            if (name == null) {
                name = "";
            }
            if (StringUtil.stringMatch(name, viewId, true, true)) {
                goodOnes.add(viewManager);
            }
        }
        if (goodOnes.size() == 0) {
            warning("Unable to find any views with id:" + viewId);
        } else {
            //            System.err.println(viewId + " " + goodOnes);
        }
        return goodOnes;
    }


    /**
     * Wait until all displays are built
     */
    public void pause() {
        getIdv().waitUntilDisplaysAreDone();
    }


    /**
     * Toggle debug
     *
     * @param v debug
     */
    public void setDebug(boolean v) {
        debug = v;
    }


    /**
     * Evaluate the given isl
     *
     * @param isl The isl
     *
     * @return success
     *
     * @throws Throwable On badness
     */
    public boolean evaluateIsl(String isl) throws Throwable {
        isl = XmlUtil.tag(TAG_GROUP, "", isl);
        return processNode(XmlUtil.getRoot(isl));
    }



    /**
     * Load the given bundle file
     *
     * @param bundleFile The bundle
     * @param setFiles This is a list, which may be null, of datasource patterns and file names to change
     *
     * @throws Throwable     On badness
     */
    public void loadBundle(String bundleFile, List setFiles)
            throws Throwable {
        loadBundle(bundleFile, setFiles, -1, -1, "", true);
    }

    /**
     * Load the given bundle file, list of datasets, width and height
     *
     * @param bundleFile The bundle
     * @param setFiles This is a list, which may be null, of datasource patterns and file names to change
     * @param width The width of the display area to use
     * @param height The height of the display are to use
     *
     * @throws Throwable  an exception
     */
    public void loadBundle(String bundleFile, List setFiles, int width,
                           int height)
            throws Throwable {
        loadBundle(bundleFile, setFiles, width, height, "", true);
    }


    /**
     * Load the given bundle file, list of datasets, width and height
     *
     * @param bundleFile The bundle
     * @param setFiles This is a list, which may be null, of datasource patterns and file names to change
     * @param width The width of the display area to use
     * @param height The height of the display are to use
     * @param times A string of times to use from the bundle file
     * @param clear If false then do not clear out the data sources and displays (which is otherwise the default)
     *
     * @throws Throwable  an exception
     */
    public void loadBundle(String bundleFile, List setFiles, int width,
                           int height, String times, boolean clear)
            throws Throwable {

        StringBuffer extra = new StringBuffer();
        if (setFiles != null) {
            for (int i = 0; i < setFiles.size(); i += 2) {
                String datasource = (String) setFiles.get(i);
                String files      = (String) setFiles.get(i + 1);
                if ((datasource != null) && (files != null)) {
                    extra.append(XmlUtil.tag(TAG_SETFILES,
                                             XmlUtil.attrs("datasource",
                                                 datasource, "file", files)));
                }
                extra.append("\n");
            }
        }
        StringBuffer attrs = new StringBuffer();
        attrs.append(" ");
        attrs.append(ATTR_FILE + "=" + quote(bundleFile));
        attrs.append(" ");
        if ((width > 0) && (height > 0)) {
            attrs.append(" ");
            attrs.append(ATTR_WIDTH + "=" + quote("" + width));
            attrs.append(" ");
            attrs.append(ATTR_HEIGHT + "=" + quote("" + height));
            attrs.append(" ");
        }
        if ((times != null) && !times.isEmpty()) {
            attrs.append(" ");
            attrs.append(ATTR_TIMES + "=" + quote("" + times));
        }
        if ( !clear) {
            attrs.append(" ");
            attrs.append(ATTR_CLEAR + "=" + quote("false"));
        }

        String xml = "" + extra + "";
        System.err.println(xml);
        processTagBundle(makeElement(xml));
    }

    /**
     * Write an Image to the specified file
     *
     * @param image Image to be written
     * @param file Name of output file (may use macros)
     *
     * @throws Exception On badness
     */
    public void writeImageToFile(Image image, String file) throws Exception {
        ImageUtils.writeImageToFile(image,
                                    applyMacros(getImageFileName(file)));
    }


    /**
     * Create XML from the input String
     *
     * @param s in the form:  "task arg=val arg2=val; task2 arg3=val"
     *
     * @return  
     *
     */
    protected static String makeXmlFromString(String s) {
        if ((s == null) || (s.length() == 0)) {
            return "";
        }
        StringTokenizer st = new StringTokenizer(s, ";");
        StringBuffer    sb = new StringBuffer();

        while (st.hasMoreTokens()) {
            String          so  = st.nextToken();
            StringTokenizer sot = new StringTokenizer(so, "=");
            int             k   = sot.countTokens();

            for (int i = 0; i < k; i++) {

                StringTokenizer sbt =
                    new StringTokenizer(sot.nextToken().trim(), " ");

                if (i == 0) {
                    sb.append("<" + sbt.nextToken().trim());
                }

                int     n      = sbt.countTokens();
                boolean gotone = false;
                while (n > 1) {
                    if (gotone) {
                        sb.append(" ");
                    }
                    sb.append(sbt.nextToken().trim());
                    n      = n - 1;
                    gotone = true;
                }

                // now deal with the last value
                if (sbt.hasMoreTokens()) {
                    if (i != k - 1) {
                        if (gotone) {
                            sb.append("\" " + sbt.nextToken().trim() + "=\"");
                        } else {
                            sb.append(" " + sbt.nextToken().trim() + "=\"");
                        }
                    } else {
                        if (gotone) {
                            sb.append(" " + sbt.nextToken().trim() + "\"");
                        } else {
                            sb.append(sbt.nextToken().trim() + "\"");
                        }
                    }
                }
            }

            sb.append(" />");
        }

        return sb.toString();
    }

    /**
     * Quote a string
     *
     * @param s  the string
     *
     * @return  the quotated string
     */
    private static String quote(String s) {
        return "\"" + s + "\"";
    }

    /**
     * Get the image of the current display and write to file. Image
     * may be modified by the params given in the form:
     *    tag1 arg=val arg2=val2; tag2 arg=val
     * where 'tag' are ISL tags.
     *
     * @param filename Output filename (may be modified by macros)
     * @param params String of parameters
     * @param qual Quality (def=1.0)
     *
     *
     * @throws Exception On badness
     * @throws Throwable On badness
     */
    public void writeImage(String filename, String params, float qual)
            throws Exception, Throwable {
        String isl = makeXmlFromString(params);
        String xml = XmlUtil.getHeader()+"\n" + isl + "";
        captureImage(applyMacros(filename), makeElement(xml));
    }


    /**
     * Get the Image of the current display
     *
     * @return the Image
     *
     * @throws Exception On badness
     */
    public Image getImage() throws Exception {
        //        updateViewManagers();
        List viewManagers = getVMManager().getViewManagers();
        for (int i = 0; i < viewManagers.size(); i++) {
            ViewManager viewManager = (ViewManager) viewManagers.get(i);
            if ( !getIdv().getArgsManager().getIsOffScreen()) {
                IdvWindow window = viewManager.getDisplayWindow();
                if (window != null) {
                    window.setLocation(50, 50);
                    viewManager.toFront();
                }
            }
            return viewManager.getMaster().getImage(false);
        }
        return null;
    }


    /**
     * Capture the image
     *
     * @param filename file
     * @param scriptingNode THe node from the isl. Possibly null.
     *
     * @throws Throwable On badness
     */
    private void captureImage(String filename, Element scriptingNode)
            throws Throwable {

        Hashtable imageProperties = new Hashtable();
        //See if we're in test mode
        if ((scriptingNode != null)
                && XmlUtil.hasAttribute(scriptingNode, "test")) {
            BufferedImage tmpImage =
                new BufferedImage(applyMacros(scriptingNode, ATTR_WIDTH,
                    300), applyMacros(scriptingNode, ATTR_HEIGHT, 300),
                          BufferedImage.TYPE_INT_RGB);
            String loopFilename = applyMacros(filename);
            lastImage = processImage((BufferedImage) tmpImage, loopFilename,
                                     scriptingNode, getAllProperties(), null,
                                     imageProperties);
            return;
        }


        List viewManagers = null;
        if ((scriptingNode != null)
                && XmlUtil.hasAttribute(scriptingNode, ATTR_DISPLAY)) {
            DisplayControlImpl display = findDisplayControl(scriptingNode);
            if (display == null) {
                throw new IllegalArgumentException("Could not find display:"
                        + XmlUtil.toString(scriptingNode));
            }
            String loopFilename = applyMacros(filename);
            String what = applyMacros(scriptingNode, ATTR_WHAT,
                                      (String) null);

            ViewManager viewManager = display.getViewManagerForCapture(what);
            if (viewManager != null) {
                viewManager.updateDisplayIfNeeded();
                viewManagers = (List) Misc.newList(viewManager);
            } else {
                lastImage = display.getImage(what);
                lastImage = processImage((BufferedImage) lastImage,
                                         loopFilename, scriptingNode,
                                         getAllProperties(), null,
                                         imageProperties);
                return;
            }
        }

        if (viewManagers == null) {
            viewManagers = (List) getViewManagers(scriptingNode);
        }

        if (viewManagers.size() == 0) {
            debug("No views to capture");
        }
        pushProperties();

        List indices = StringUtil.parseIntegerListString(
                                    XmlUtil.getAttribute(
                                        scriptingNode, ATTR_ANIMATION_INDEX,
                                        "1"));

        int idx = 0;
        for (int j = 0; j < indices.size(); j++) {
            List images = new ArrayList();
            String      fname  = (indices.size() > 1)
                                 ? fixFileName(filename, indices.get(j))
                                 : filename;
            for (int i = 0; i < viewManagers.size(); i++) {
                ViewManager viewManager = (ViewManager) viewManagers.get(i);
                if (viewManager.getAnimation() != null)
                   viewManager.getAnimation().setCurrent(indices.get(j));
                putIndex(getProperties(), PROP_VIEWINDEX, idx);
                String name = viewManager.getName();
                if (name == null) {
                    name = "view" + idx;
                }
                getProperties().put(PROP_VIEWNAME, name);
                if ( !getIdv().getArgsManager().getIsOffScreen()) {
                    IdvWindow window = viewManager.getDisplayWindow();
                    if (window != null) {
                        window.setLocation(50, 50);
                        viewManager.toFront();
                        //                    Misc.sleep(100);
                    }
                }
                String loopFilename = applyMacros(fname);
                if (scriptingNode == null) {
                    File imageFile = null;
                    if (loopFilename != null) {
                        imageFile = new File(getImageFileName(loopFilename));
                    }
                    viewManager.writeImage(imageFile, true, false);
     * Resize the image
                } else if ((loopFilename != null)
                           && ViewManager.isVectorGraphicsFile(
                               loopFilename)) {
                    VectorGraphicsRenderer vectorRenderer =
                        new VectorGraphicsRenderer(viewManager);
                    vectorRenderer.renderTo(loopFilename);
                } else {
                    getIdv().getIdvUIManager().waitUntilDisplaysAreDone(
                        getIdv().getIdvUIManager(), 0);
                    lastImage       = viewManager.getMaster().getImage(false);
                    imageProperties = new Hashtable();

                    lastImage = processImage((BufferedImage) lastImage,
                                             loopFilename, scriptingNode,
                                             getAllProperties(), viewManager,
                                             imageProperties);
                }
                images.add(lastImage);
                idx++;
            }

            boolean combine = XmlUtil.getAttribute(scriptingNode,
                                  ATTR_COMBINE, false);
            if (combine) {
                String          combineFilename = applyMacros(filename);
                List components      = new LinkedList();
                for (ViewManager vm : viewManagers) {
                    components.add(vm.getComponent());
                }
                int cols = 2;
                if ( !getIdv().getArgsManager().getIsOffScreen()) {
                    cols = ImageUtils.getColumnCountFromComps(components);
                } else {
                    cols = XmlUtil.getAttribute(scriptingNode, ATTR_COLUMNS,
                            cols);
                }

                if (ViewManager.isVectorGraphicsFile(combineFilename)) {
                    VectorGraphicsRenderer vectorRenderer =
                        new VectorGraphicsRenderer(viewManagers, cols);
                    vectorRenderer.renderTo(combineFilename);
                } else {
                    Image i = ImageUtils.gridImages2(images, 0, Color.GRAY,
                                  cols);
                    ImageUtils.writeImageToFile(i, combineFilename);
                }
            }
        }

        popProperties();
    }

    /**
     * Fixing file name for animation indices.
     *
     * @param filename
     * @param integer
     *
     * @return The file name with an appended index.
     */
    private String fixFileName(String filename, Integer integer) {
        String[] tokens = filename.split("\\.(?=[^\\.]+$)");
        return tokens[0] + integer + "." + tokens[1];
    }

    /**
     * Resize the image
     *
     * @param image The image
     * @param node Node to process. This may contain a width or a height attribute.
     *
     * @return The resized image
     */
    protected Image resize(Image image, Element node) {
        int imageWidth  = image.getWidth(null);
        int imageHeight = image.getHeight(null);
        int width       = -1;
        int height      = -1;
        if (XmlUtil.hasAttribute(node, ATTR_WIDTH)) {
            width = (int) toDouble(node, ATTR_WIDTH, imageWidth);
        }
        if (XmlUtil.hasAttribute(node, ATTR_HEIGHT)) {
            height = (int) toDouble(node, ATTR_HEIGHT, imageWidth);
        }
        if ((width == -1) && (height == -1)) {
            return image;
        }

        return image.getScaledInstance(width, height,
                                       Image.SCALE_AREA_AVERAGING);
    }




    /**
     *
     * @param image The image
     * @param widthStr width of desired image (pixels)
     * @param heightStr height of desired image (pixels)
     *
     * @return The resized image
     */
    public BufferedImage resizeImage(BufferedImage image, String widthStr,
                                     String heightStr) {
        int imageWidth  = image.getWidth(null);
        int imageHeight = image.getHeight(null);
        int width       = -1;
        int height      = -1;
        if ( !widthStr.equals("-1")) {
            width = (int) toDouble(widthStr, imageWidth);
        }
        if ( !heightStr.equals("-1")) {
            height = (int) toDouble(heightStr, imageHeight);
        }
        if ((width == -1) && (height == -1)) {
            return image;
        }

        BufferedImage resizedImage =
            ImageUtils.toBufferedImage(image.getScaledInstance(width, height,
                Image.SCALE_AREA_AVERAGING), BufferedImage.TYPE_INT_RGB);
        return resizedImage;

    }



    /**
     * Matte the image
     *
     * @param image The image
     * @param bgString color for the matte ("red", "green", etc)
     * @param top number of lines for the top (north) matte
     * @param left number of pixels for the left (west) matte
     * @param bottom number of lines for the bottom (south) matte
     * @param right number of pixels for the right (east) matte
     *
     * @return The matte'd image
     */
    public BufferedImage matteImage(BufferedImage image, String bgString,
                                    int top, int left, int bottom,
                                    int right) {
        Color bg = GuiUtils.decodeColor(bgString, (Color) null);
        return ImageUtils.matte(image, top, bottom, left, right, bg);
    }



    /**
     * Process the image
     *
     * @param image The image
     * @param filename File to write the image to
     * @param node Node to process
     * @param props Extra properties
     * @param viewManager The viewmanager this image came from
     * @param imageProps  the image properties
     *
     *
     * @return The processed image
     * @throws Throwable On badness
     */
    protected BufferedImage processImage(BufferedImage image,
                                         String filename, Element node,
                                         Hashtable props,
                                         ViewManager viewManager,
                                         Hashtable imageProps)
            throws Throwable {

        if (node == null) {
            return image;
        }

        if (props == null) {
            props = new Hashtable();
        }
        if (viewManager != null) {
            Animation animation = viewManager.getAnimation();
            props.put(PROP_ANIMATIONTIME, "");
            if (animation != null) {
                if (animation.getAniValue() != null) {
                    props.put(PROP_ANIMATIONTIME, animation.getAniValue());
                }
            }
        }
        getProperties().putAll(props);

        NodeList  elements       = XmlUtil.getElements(node);
        Hashtable seenColorTable = new Hashtable();
        for (int childIdx = 0; childIdx < elements.getLength(); childIdx++) {
            boolean       shouldIterateChildren = true;
            BufferedImage newImage              = null;
            int           imageWidth            = image.getWidth(null);
            int           imageHeight           = image.getHeight(null);
            Element       child = (Element) elements.item(childIdx);
            String        tagName               = child.getTagName();

            if (tagName.equals(TAG_RESIZE)) {
                newImage = ImageUtils.toBufferedImage(resize(image, child));
            } else if (tagName.equals(TAG_FILESET)) {
                //ignore
            } else if (tagName.equals(TAG_OUTPUT)) {
                processTagOutput(child);
            } else if (tagName.equals(TAG_DISPLAYLIST)) {
                if (viewManager != null) {
                    newImage = ImageUtils.toBufferedImage(image, true);
                    Graphics g = newImage.getGraphics();
                    String valign = applyMacros(child, ATTR_VALIGN,
                                        VALUE_BOTTOM);
                    Font font = getFont(child);
                    if (XmlUtil.hasAttribute(child, ATTR_MATTEBG)) {
                        int height =
                            viewManager.paintDisplayList((Graphics2D) g,
                                null, imageWidth, imageHeight,
                                valign.equals(VALUE_BOTTOM), null, font);

                        int top    = (valign.equals(VALUE_TOP)
                                      ? height
                                      : 0);
                        int bottom = (valign.equals(VALUE_BOTTOM)
                                      ? height
                                      : 0);
                        newImage = ImageUtils.matte(image, top, bottom, 0, 0,
                                applyMacros(child, ATTR_MATTEBG,
                                            Color.white));
                        g           = newImage.getGraphics();
                        imageHeight += height;
                    }

                    Color c = applyMacros(child, ATTR_COLOR, (Color) null);
                    viewManager.paintDisplayList((Graphics2D) g, null,
                            imageWidth, imageHeight,
                            valign.equals(VALUE_BOTTOM), c, font);
                }
            } else if (tagName.equals(TAG_COLORBAR)
                       || tagName.equals(TAG_KML_COLORBAR)) {
                // only do one colorbar if we are writing to kml
                Integer index = (Integer) props.get(PROP_IMAGEINDEX);
                if ((index != null) && (index.intValue() > 0)
                        && tagName.equals(TAG_KML_COLORBAR)) {
                    continue;
                }

                boolean showLines = applyMacros(child, ATTR_SHOWLINES, false);

                List controls =
                    (List) ((viewManager != null)
                        ? viewManager.getControls()
                        : new ArrayList());

                if (XmlUtil.hasAttribute(child, ATTR_DISPLAY)) {
                    DisplayControlImpl display = ((controls.size() > 0)
                            ? findDisplayControl(XmlUtil.getAttribute(child,
                                ATTR_DISPLAY), controls)
                            : findDisplayControl(child));
                    if (display == null) {
                        error("Could not find display:"
                              + XmlUtil.toString(node));
                        return null;
                    }
                    controls = Misc.newList(display);
                }

                int    width    = applyMacros(child, ATTR_WIDTH, 150);
                int    height   = applyMacros(child, ATTR_HEIGHT, 20);
                int    ticks    = applyMacros(child, ATTR_TICKMARKS, 0);
                double interval = applyMacros(child, ATTR_INTERVAL, -1.0);
                String valuesStr = applyMacros(child, ATTR_VALUES,
                                       (String) null);
                Color c = applyMacros(child, ATTR_COLOR, Color.black);

                Color lineColor = applyMacros(child, ATTR_LINECOLOR, c);

                Rectangle imageRect = new Rectangle(0, 0, imageWidth,
                                          imageHeight);

                Point pp = ImageUtils.parsePoint(applyMacros(child,
                               ATTR_PLACE, "ll,10,-10"), imageRect);
                Point ap = ImageUtils.parsePoint(applyMacros(child,
                               ATTR_ANCHOR, "ll"), new Rectangle(0, 0, width,
                                   height));

                String orientation = applyMacros(child, ATTR_ORIENTATION,
                                         VALUE_BOTTOM);
                boolean vertical = orientation.equals(VALUE_RIGHT)
                                   || orientation.equals(VALUE_LEFT);
                int     baseY       = pp.y - ap.y + (vertical
                        ? 0
                        : height);
                int     baseX       = pp.x - ap.x;

                List    colorTables = new ArrayList();
                List    ranges      = new ArrayList();
                List    units       = new ArrayList();

                boolean forKml      = tagName.equals(TAG_KML_COLORBAR);

                for (int i = 0; i < controls.size(); i++) {
                    DisplayControlImpl control =
                        (DisplayControlImpl) controls.get(i);
                    ColorTable colorTable = control.getColorTable();
                    if (colorTable == null) {
                        continue;
                    }
                    Range range = control.getRangeForColorTable();
                    //only do unique color tables
                    Object[] key = { colorTable, range };
                    if (seenColorTable.get(key) != null) {
                        continue;
                    }
                    seenColorTable.put(key, key);
                    colorTables.add(colorTable);
                    ranges.add(range);
                    units.add(control.getDisplayUnit());
                }

                for (int i = 0; i < colorTables.size(); i++) {
                    ColorTable colorTable = (ColorTable) colorTables.get(i);
                    Range      range      = (Range) ranges.get(i);
                    Unit       unit       = (Unit) units.get(i);
                    Image      imageToDrawIn;
                    if (forKml) {
                        if (vertical) {
                            baseX = 0;
                            baseY = 0;
                        } else {
                            baseX = 0;
                            baseY = height;
                        }
                        int space = applyMacros(child, ATTR_SPACE, (vertical
                                ? width
                                : height));
                        imageToDrawIn = new BufferedImage(width + (vertical
                                ? space
                                : 0), height + (vertical
                                ? 0
                                : space), BufferedImage.TYPE_INT_RGB);
                    } else {
                        imageToDrawIn = newImage =
                            ImageUtils.toBufferedImage(image);
                    }
                    Graphics g = imageToDrawIn.getGraphics();
                    if (forKml) {
                        Color bgColor = applyMacros(child, ATTR_BACKGROUND,
                                            Color.white);
                        g.setColor(bgColor);
                        g.fillRect(0, 0, imageToDrawIn.getWidth(null),
                                   imageToDrawIn.getHeight(null));
                    }
                    ColorPreview preview =
                        new ColorPreview(
                            new BaseRGBMap(colorTable.getNonAlphaTable()),
                            (vertical
                             ? width
                             : height));
                    if (vertical) {
                        preview.setSize(new Dimension(height, width));
                    } else {
                        preview.setSize(new Dimension(width, height));
                    }
                    Image previewImage = GuiUtils.getImage(preview);
                    boolean includeAlpha = applyMacros(child, ATTR_TRANSPARENCY,
                                               true);
                    previewImage = ColorTableCanvas.getImage(colorTable,
                            (vertical
                             ? height
                             : width), (vertical
                                        ? width
                                        : height), includeAlpha);


                    if (vertical) {
                        BufferedImage tmpImage =

                            new BufferedImage(width, height,
                                BufferedImage.TYPE_INT_RGB);

                        BufferedImage tmpImagexxx =
                            new BufferedImage(500, 500,
                                BufferedImage.TYPE_INT_RGB);
                        Graphics2D tmpG = (Graphics2D) tmpImage.getGraphics();
                        tmpG.setColor(Color.red);
                        tmpG.fillRect(0, 0, 1000, 1000);
                        tmpG.rotate(Math.toRadians(90.0));
                        tmpG.drawImage(previewImage, 0, 0 - width, null);
                        previewImage = tmpImage;
                    }
                    if (forKml) {
                        g.drawImage(previewImage, 0, 0, null);
                    } else {
                        g.drawImage(previewImage, baseX, (vertical
                                ? baseY
                                : baseY - height), null);
                    }
                    if (showLines) {
                        g.setColor(lineColor);
                        g.drawRect(baseX, (vertical
                                           ? baseY
                                           : baseY - height), width - 1,
                                           height - (vertical
                                ? 1
                                : 0));
                    }
                    setFont(g, child);
                    FontMetrics fm     = g.getFontMetrics();
                    List        values = new ArrayList();
                    String suffixFrequency = XmlUtil.getAttribute(child,
                                                 ATTR_SUFFIXFREQUENCY,
                                                 XmlUtil.getAttribute(child,
                                                     ATTR_SHOWUNIT,
                                                     "false")).toLowerCase();
                    String unitDefault = ( !suffixFrequency.equals("false"))
                                         ? " %unit%"
                                         : "";
                    String labelSuffix = applyMacros(child, ATTR_SUFFIX,
                                             unitDefault);
                    if (unit != null) {
                        labelSuffix = labelSuffix.replace("%unit%",
                                "" + unit);
                    } else {
                        labelSuffix = labelSuffix.replace("%unit%", "");
                    }
                    if (valuesStr != null) {
                        double[] valueArray = Misc.parseDoubles(valuesStr,
                                                  ",");
                        for (int valueIdx = 0; valueIdx < valueArray.length;
                                valueIdx++) {
                            values.add(new Double(valueArray[valueIdx]));
                        }
                    } else if (ticks > 0) {
                        int spacing = ((ticks == 1)
                                       ? 0
                                       : (vertical
                                          ? height
                                          : width) / (ticks - 1));
                        for (int tickIdx = 0; tickIdx < ticks; tickIdx++) {
                            double percent = ((ticks > 1)
                                    ? (double) tickIdx / (double) (ticks - 1)
                                    : 0.0);
                            values.add(
                                new Double(range.getValueOfPercent(percent)));
                        }
                    } else if (interval > 0) {

                        double value = range.getMin();
                        double max   = range.getMax();
                        while (value <= max) {
                            values.add(new Double(value));
                            value += interval;
                        }
                    }
                    for (int valueIdx = 0; valueIdx < values.size();
                            valueIdx++) {
                        double value =
                            ((Double) values.get(valueIdx)).doubleValue();
                        int x;
                        int y;
                        if (vertical) {
                            if (orientation.equals(VALUE_RIGHT)) {
                                x = baseX + width;
                            } else {
                                x = baseX;
                            }
                            y = baseY
                                + (int) (range.getPercent(value) * height);
                            if (y > baseY + height) {
                                break;
                            }
                        } else {
                            if (orientation.equals(VALUE_BOTTOM)) {
                                y = baseY;
                            } else {
                                y = baseY - height;
                            }

                            if (range != null) {
                                x = baseX
                                    + (int) (range.getPercent(value) * width);
                            } else {
                                x = baseX;
                            }

                            if (x > baseX + width) {
                                break;
                            }
                        }
                        String tickLabel =
                            getIdv().getDisplayConventions().format(value);
                        if (suffixFrequency.equals(VALUE_LAST)
                                && (valueIdx == values.size() - 1)) {
                            tickLabel += labelSuffix;
                        } else if (suffixFrequency.equals(VALUE_FIRST)
                                   && (valueIdx == 0)) {
                            tickLabel += labelSuffix;
                        } else if (suffixFrequency.equals(VALUE_ALL)
                                   || suffixFrequency.equals("true")) {
                            tickLabel += labelSuffix;
                        }


                        Rectangle2D rect = fm.getStringBounds(tickLabel, g);
                        g.setColor(lineColor);
                        if (orientation.equals(VALUE_RIGHT)) {
                            g.drawLine(x + 1, y, x, y);
                            if (showLines) {
                                g.drawLine(x, y, x - width, y);
                            }
                        } else if (orientation.equals(VALUE_LEFT)) {
                            g.drawLine(x - 1, y, x, y);
                            if (showLines) {
                                g.drawLine(x, y, x + width, y);
                            }
                        } else if (orientation.equals(VALUE_BOTTOM)) {
                            g.drawLine(x, y + 1, x, y);
                            if (showLines) {
                                g.drawLine(x, y, x, y - height);
                            }
                        } else {
                            g.drawLine(x, y - 1, x, y);
                            if (showLines) {
                                g.drawLine(x, y, x, y + height);
                            }
                        }
                        g.setColor(c);
                        if (orientation.equals(VALUE_RIGHT)) {
                            int yLoc = y + (int) (rect.getHeight() / 2) - 2;
                            if (forKml) {
                                if (valueIdx == 0) {
                                    yLoc = y + (int) (rect.getHeight()) - 2;
                                } else if (valueIdx == values.size() - 1) {
                                    yLoc = y - (int) (rect.getHeight()) + 6;
                                }
                            }
                            g.drawString(tickLabel, x + 2, yLoc);
                        } else if (orientation.equals(VALUE_LEFT)) {
                            int xLoc = x - 2 - (int) rect.getWidth();
                            g.drawString(tickLabel, xLoc,
                                         y + (int) (rect.getHeight() / 2)
                                         - 2);
                        } else if (orientation.equals(VALUE_BOTTOM)) {
        /**
                            int xLoc = x - (int) (rect.getWidth() / 2);
                            if (forKml) {
                                if (valueIdx == 0) {
                                    xLoc = x + 2;
                                } else if (valueIdx == values.size() - 1) {
                                    xLoc = x - (int) rect.getWidth() + 2;
                                }
                            }
                            g.drawString(tickLabel, xLoc,
                                         y + (int) rect.getHeight() + 2);
                        } else {
                            g.drawString(tickLabel,
                                         x - (int) (rect.getWidth() / 2),
                                         y - 2);
                        }
                    }
                    if (vertical) {
                        baseX += width + 30;
                    } else {
                        baseY += height + 30;
                    }
                    if (forKml) {
                        String tmpImageFile =
                            applyMacros(
                                child, ATTR_FILE,
                                getIdv().getStore().getTmpFile(
                                    "testcolorbar${viewindex}.png"));
                        String template =
                            "${kml.name}${icon}\n"
                            + "\n"
                            + "\n"
                            + "\n"
                            + "\n";
                        String[] macros = {
                            "kml.name", "kml.overlayXY.x", "kml.overlayXY.y",
                            "kml.overlayXY.xunits", "kml.overlayXY.yunits",
                            "kml.screenXY.x", "kml.screenXY.y",
                            "kml.screenXY.xunits", "kml.screenXY.yunits",
                            "kml.size.x", "kml.size.y", "kml.size.xunits",
                            "kml.size.yunits"
                        };
                        String[] macroValues = {
                            "", "0", "1", "fraction", "fraction", "0", "1",
                            "fraction", "fraction", "-1", "-1", "pixels",
                            "pixels"
                        };

                        for (int macroIdx = 0; macroIdx < macros.length;
                                macroIdx++) {
                            template =
                                template.replace("${" + macros[macroIdx]
                                    + "}", applyMacros(child,
                                        macros[macroIdx],
                                        macroValues[macroIdx]));
                        }
                        template = template.replace("${icon}",
                                IOUtil.getFileTail(tmpImageFile));
                        imageProps.put("kml", template);
                        List kmlFiles = (List) imageProps.get("kmlfiles");
                        //TODO: Only do the first one for now
                        if (kmlFiles == null) {
                            kmlFiles = new ArrayList();
                            imageProps.put("kmlfiles", kmlFiles);
                        }
                        kmlFiles.add(tmpImageFile);

                        //                        System.out.println(template);
                        ImageUtils.writeImageToFile(imageToDrawIn,
                                tmpImageFile);
                    }
                }


            } else if (tagName.equals(TAG_TRANSPARENT)
                       || tagName.equals(TAG_BGTRANSPARENT)) {
                Color c = null;
                if (tagName.equals(TAG_BGTRANSPARENT)) {
                    c = viewManager.getBackground();
                } else {
        /**
                    c = applyMacros(child, ATTR_COLOR, (Color) null);
                }
                //                System.err.println ("c:" + c);
                int[] redRange   = { 0, 0 };
                int[] greenRange = { 0, 0 };
                int[] blueRange  = { 0, 0 };
                if (c != null) {
                    //                    System.err.println("got color");
                    redRange[0]   = redRange[1] = c.getRed();
                    greenRange[0] = greenRange[1] = c.getGreen();
                    blueRange[0]  = blueRange[1] = c.getBlue();
                } else {}
                newImage = ImageUtils.makeColorTransparent(image, redRange,
                        greenRange, blueRange);
            } else if (tagName.equals(TAG_SHOW)) {
                JComponent contents = new JLabel(new ImageIcon(image));
                String message = applyMacros(child, ATTR_MESSAGE,
                                             (String) null);
                if (message != null) {
                    contents = GuiUtils.topCenter(new JLabel(message),
                            contents);
                }
                if ( !GuiUtils.askOkCancel("Continue?", contents)) {
                    throw new MyQuitException();
                }
            } else if (tagName.equals(TAG_MATTE)) {
                newImage = doMatte(image, child, 0);
            } else if (tagName.equals(TAG_LATLONLABELS)) {
                newImage = doLatLonLabels(child, viewManager, image,
                                          imageProps);
            } else if (tagName.equals(TAG_WRITE)) {
                ImageUtils.writeImageToFile(
                    image, getImageFileName(applyMacros(child, ATTR_FILE)));

            } else if (tagName.equals(TAG_PUBLISH)) {
                getIdv().getPublishManager().publishIslImage(this, node,
                        image);
            } else if (tagName.equals(TAG_CLIP)) {
                int[] ul;
                int[] lr;
                if (XmlUtil.hasAttribute(child, ATTR_DISPLAY)) {
                    //                    System.err.println("Clipping from display");
                    DisplayControlImpl dc = findDisplayControl(child);
                    if (dc == null) {
                        throw new IllegalArgumentException(
                            "Could not find display:"
                            + XmlUtil.toString(node));
                    }
                    NavigatedDisplay display =
                        (NavigatedDisplay) viewManager.getMaster();
                    MapProjection mapProjection = dc.getDataProjection();
                    java.awt.geom.Rectangle2D rect =
                        mapProjection.getDefaultMapArea();
                    LatLonPoint llplr =
                        mapProjection.getLatLon(new double[][] {
                        { rect.getX() + rect.getWidth() },
                        { rect.getY() + rect.getHeight() }
                    });
                    LatLonPoint llpul =
                        mapProjection.getLatLon(new double[][] {
                        { rect.getX() }, { rect.getY() }
                    });
                    EarthLocation ulEl = new EarthLocationTuple(llpul,
                                             new Real(RealType.Altitude, 0));
                    EarthLocation lrEl = new EarthLocationTuple(llplr,
                                             new Real(RealType.Altitude, 0));
                    ul = display.getScreenCoordinates(
                        display.getSpatialCoordinates(ulEl, null));
                    lr = display.getScreenCoordinates(
                        display.getSpatialCoordinates(lrEl, null));

                    //System.err.println("ul:" + ulEl + " lr:" + lrEl);
                    if (ul[0] > lr[0]) {
                        int tmp = ul[0];
                        ul[0] = lr[0];
                        lr[0] = tmp;
                    }
                    if (ul[1] > lr[1]) {
                        int tmp = ul[1];
                        ul[1] = lr[1];
                        lr[1] = tmp;
                    }
                    imageProps.put(ATTR_NORTH,
                                   new Double(ulEl.getLatitude().getValue()));
                    imageProps.put(
                        ATTR_WEST,
                        new Double(ulEl.getLongitude().getValue()));
                    imageProps.put(ATTR_SOUTH,
                                   new Double(lrEl.getLatitude().getValue()));
                    imageProps.put(
                        ATTR_EAST,
                        new Double(lrEl.getLongitude().getValue()));
                } else if ((viewManager != null)
                           && XmlUtil.hasAttribute(child, ATTR_NORTH)) {
                    NavigatedDisplay display =
                        (NavigatedDisplay) viewManager.getMaster();
                    EarthLocation el1 =
                        DisplayControlImpl.makeEarthLocation(toDouble(child,
                            ATTR_NORTH), toDouble(child, ATTR_WEST), 0);
                    EarthLocation el2 =
                        DisplayControlImpl.makeEarthLocation(toDouble(child,
                            ATTR_SOUTH), toDouble(child, ATTR_EAST), 0);
                    ul = display.getScreenCoordinates(
                        display.getSpatialCoordinates(el1, null));
                    lr = display.getScreenCoordinates(
                        display.getSpatialCoordinates(el2, null));
                    imageProps.put(ATTR_NORTH,
                                   new Double(el1.getLatitude().getValue()));
                    imageProps.put(ATTR_WEST,
                                   new Double(el1.getLongitude().getValue()));
                    imageProps.put(ATTR_SOUTH,
                                   new Double(el2.getLatitude().getValue()));
                    imageProps.put(ATTR_EAST,
                                   new Double(el2.getLongitude().getValue()));
                } else if (XmlUtil.hasAttribute(child, ATTR_LEFT)) {
                    ul = new int[] {
                        (int) toDouble(child, ATTR_LEFT, imageWidth),
                        (int) toDouble(child, ATTR_TOP, imageHeight) };
                    lr = new int[] {
                        (int) toDouble(child, ATTR_RIGHT, imageWidth),
                        (int) toDouble(child, ATTR_BOTTOM, imageHeight) };
                } else if (viewManager != null) {
                    //TODO: Clip on visad coordinates
                    NavigatedDisplay display =
                        (NavigatedDisplay) viewManager.getMaster();
                    ul = display.getScreenCoordinates(new double[] { -1, 1,
                            0 });
                    lr = display.getScreenCoordinates(new double[] { 1, -1,
                            0 });
                    int space  = applyMacros(child, ATTR_SPACE, 0);
                    int hspace = applyMacros(child, ATTR_HSPACE, space);
                    int vspace = applyMacros(child, ATTR_VSPACE, space);
                    ul[0] -= applyMacros(child, ATTR_SPACE_LEFT, hspace);
                    ul[1] -= applyMacros(child, ATTR_SPACE_TOP, vspace);
                    lr[0] += applyMacros(child, ATTR_SPACE_RIGHT, hspace);
                    lr[1] += applyMacros(child, ATTR_SPACE_BOTTOM, vspace);
                } else {
                    continue;
                }


                for (String attr :
                        (List) Misc.newList(ATTR_NORTH, ATTR_SOUTH,
                            ATTR_EAST, ATTR_WEST)) {
                    String kmlAttr = "kml." + attr;
                    if (XmlUtil.hasAttribute(child, kmlAttr)) {
                        imageProps.put(attr,
                                       new Double(applyMacros(child, kmlAttr,
                                           0.0)));
                    }
                }

                ul[0]    = Math.max(0, ul[0]);
                ul[1]    = Math.max(0, ul[1]);

                lr[0]    = Math.min(lr[0], imageWidth);
                lr[1]    = Math.min(lr[1], imageHeight);


                newImage = ImageUtils.clip(image, ul, lr);
            } else if (tagName.equals(TAG_SPLIT)) {
                shouldIterateChildren = false;
                int    width  = image.getWidth(null);
                int    height = image.getHeight(null);
                int    cols   = applyMacros(child, ATTR_COLUMNS, 2);
                int    rows   = applyMacros(child, ATTR_ROWS, 2);
                String file   = applyMacros(child, ATTR_FILE);
                int    cnt    = 0;
                int    hSpace = width / cols;
                int    vSpace = height / rows;
                for (int row = 0; row < rows; row++) {
                    for (int col = 0; col < cols; col++) {
                        pushProperties();
                        Hashtable myprops = new Hashtable();
                        putProperty("row", new Integer(row));
                        putProperty("column", new Integer(col));
                        putProperty("count", new Integer(++cnt));
                        String realFile = applyMacros(file, myprops);
                        Image splitImage = image.getSubimage(hSpace * col,
                                               vSpace * row, hSpace, vSpace);
                        processImage(ImageUtils.toBufferedImage(splitImage),
                                     realFile, child, myprops, viewManager,
                                     new Hashtable());
                        popProperties();
                    }
                }
            } else if (tagName.equals(TAG_THUMBNAIL)) {
                shouldIterateChildren = false;
                BufferedImage thumbImage =
                    ImageUtils.toBufferedImage(resize(image, child));
                String thumbFile = applyMacros(child, ATTR_FILE,
                                       (String) null);
                if (thumbFile == null) {
                    thumbFile = IOUtil.stripExtension(filename) + "_thumb"
                                + IOUtil.getFileExtension(filename);
                }
                processImage(thumbImage, thumbFile, child, null, viewManager,
                             new Hashtable());
            } else if (tagName.equals(TAG_KML)) {
                //NOOP
            } else if (tagName.equals(TAG_KMZFILE)) {
                //NOOP
            } else if (tagName.equals(TAG_OVERLAY)) {
                double transparency = applyMacros(child, ATTR_TRANSPARENCY,
                                          0.0);
                Graphics2D g = (Graphics2D) image.getGraphics();
                String imagePath = applyMacros(child, ATTR_IMAGE,
                                       (String) null);

                Rectangle imageRect = new Rectangle(0, 0, imageWidth,
                                          imageHeight);
                Point pp = ImageUtils.parsePoint(applyMacros(child,
                               ATTR_PLACE, "lr,-10,-10"), imageRect);
                String text = applyMacros(child, ATTR_TEXT, (String) null);
                Color  bg = applyMacros(child, ATTR_BACKGROUND, (Color) null);
                if (text != null) {
                    double angle = Math.toRadians(applyMacros(child,
                                       ATTR_ANGLE, 0.0));
                    text = applyMacros(text);
                    Color c = applyMacros(child, ATTR_COLOR, Color.white);
                    if ((c != null) && (transparency > 0)) {
                        c = new Color(c.getRed(), c.getGreen(), c.getBlue(),
                                      ImageUtils.toAlpha(transparency));
                    }
                    //Color bg = applyMacros(child, ATTR_BACKGROUND,
                    //                       (Color) null);
                    if ((bg != null) && (transparency > 0)) {
                        bg = new Color(bg.getRed(), bg.getGreen(),
                                       bg.getBlue(),
                                       ImageUtils.toAlpha(transparency));
                    }
                    setFont(g, child);
     *
                    FontMetrics fm     = g.getFontMetrics();
                    Rectangle2D rect   = fm.getStringBounds(text, g);
                    int         width  = (int) rect.getWidth();
                    int         height = (int) (rect.getHeight());

                    Point ap = ImageUtils.parsePoint(applyMacros(child,
                                   ATTR_ANCHOR,
                                   "lr,-10,-10"), new Rectangle(0, 0, width,
                                       height));

                    g.rotate(angle);
                    if (bg != null) {
                        g.setColor(bg);
                        g.fillRect(pp.x - ap.x - 1, pp.y - ap.y - 1,
                                   (int) width + 2, (int) height + 2);
                    }
                    g.setColor(c);
                    g.drawString(text, pp.x - ap.x, pp.y - ap.y + height);
                }

                if (imagePath != null) {
                    Image overlay = ImageUtils.readImage(imagePath);
                    if (overlay != null) {
                        if (transparency > 0) {
                            overlay = ImageUtils.setAlpha(overlay,
                                    transparency);
                        }
                        int width  = overlay.getWidth(null);
                        int height = overlay.getHeight(null);
                        Point ap = ImageUtils.parsePoint(applyMacros(child,
                                       ATTR_ANCHOR,
                                       "lr,-10,-10"), new Rectangle(0, 0,
                                           width, height));
                        g.drawImage(overlay, pp.x - ap.x, pp.y - ap.y, bg,
                                    null);
                    }
                }
            } else {
                error("Unknown tag:" + tagName);
            }
            if (newImage != null) {
                String newFileName = applyMacros(child, ATTR_FILE,
                                         (String) null);
                if (shouldIterateChildren) {
                    newImage = processImage(newImage, newFileName, child,
                                            null, viewManager,
                                            new Hashtable());
                }
                if (newFileName != null) {
                    ImageUtils.writeImageToFile(newImage,
                            getImageFileName(newFileName));
                    debug("Writing image:" + newFileName);
                }
                if ( !applyMacros(child, ATTR_COPY, false)) {
                    image = newImage;
                }
            }
        }


        if (filename != null) {
            float quality = (float) applyMacros(node, ATTR_QUALITY, 1.0);
            List fileToks = (List) StringUtil.split(filename,
                                        ",", true, true);
            for (String file : fileToks) {
                file = getImageFileName(file);
                debug("Writing image:" + file);
                if (file.endsWith(FileManager.SUFFIX_KMZ)) {
                    GeoLocationInfo bounds = null;
                    if (viewManager != null) {
                        bounds = viewManager.getVisibleGeoBounds();
                        ImageSequenceGrabber.subsetBounds(bounds, imageProps);
                        String tail = IOUtil.getFileTail(file);
                        String tmpImageFile =
                            getIdv().getStore().getTmpFile(tail + ".png");
                        ImageUtils.writeImageToFile(image, tmpImageFile,
                                quality);
                        ImageWrapper imageWrapper =
                            new ImageWrapper(tmpImageFile, null, bounds,
                                             null);
                        imageWrapper.setProperties(imageProps);
                        new ImageSequenceGrabber(
                            file, getIdv(), this, node,
                    g.setColor(bg);
                            (List) Misc.newList(imageWrapper),
                            null, 1);
                    }
                } else {
                    ImageUtils.writeImageToFile(image, file, quality);
                }
            }
        }
        return image;
    }


    /**
     * Get the insets
     *
     * @param child  the element
     * @param dflt   the default value
     *
     * @return the Insets
     */
    public Insets getInsets(Element child, int dflt) {
        int space  = applyMacros(child, ATTR_SPACE, dflt);
        int hspace = applyMacros(child, ATTR_HSPACE, space);
        int vspace = applyMacros(child, ATTR_VSPACE, space);
        int top    = applyMacros(child, ATTR_TOP, vspace);
        int bottom = applyMacros(child, ATTR_BOTTOM, vspace);
        int left   = applyMacros(child, ATTR_LEFT, hspace);
        int right  = applyMacros(child, ATTR_RIGHT, hspace);
        return new Insets(top, left, bottom, right);
    }


    /**
     * Process the lat/lon labels tag
     *
     * @param child  the XML
     * @param viewManager  the associated view manager
     * @param image  the image to draw on
     * @param imageProps  the image properties
     *
     * @return  a new image
     *
     * @throws Exception  on badness
     */
    public BufferedImage doLatLonLabels(Element child,
                                        ViewManager viewManager,
                                        BufferedImage image,
                                        Hashtable imageProps)
            throws Exception {

        if (viewManager == null) {
            throw new IllegalArgumentException("Tag " + TAG_LATLONLABELS
                    + " requires a view");
        }
        if ( !(viewManager instanceof MapViewManager)) {
            throw new IllegalArgumentException("Tag " + TAG_LATLONLABELS
                    + " requires a map view");
        }
        MapViewManager   mvm     = (MapViewManager) viewManager;
        NavigatedDisplay display = (NavigatedDisplay) viewManager.getMaster();
        DecimalFormat format = new DecimalFormat(applyMacros(child,
                                   ATTR_FORMAT, "##0.0"));
        Color color     = applyMacros(child, ATTR_COLOR, Color.red);
        Color lineColor = applyMacros(child, ATTR_LINECOLOR, color);
        Color bg = applyMacros(child, ATTR_LABELBACKGROUND, (Color) null);

        double[] latValues = Misc.parseDoubles(applyMacros(child,
                                 ATTR_LAT_VALUES, ""));
        List latLabels = StringUtil.split(applyMacros(child,
                                     ATTR_LAT_LABELS, ""), ",", true, true);
        double[] lonValues = Misc.parseDoubles(applyMacros(child,
                                 ATTR_LON_VALUES, ""));
        List lonLabels = StringUtil.split(applyMacros(child,
                                     ATTR_LON_LABELS, ""), ",", true, true);

        boolean drawLonLines = applyMacros(child, ATTR_DRAWLONLINES, false);
        boolean drawLatLines = applyMacros(child, ATTR_DRAWLATLINES, false);
        boolean       showTop    = applyMacros(child, ATTR_SHOWTOP, false);
        boolean       showBottom = applyMacros(child, ATTR_SHOWBOTTOM, true);
        boolean       showLeft   = applyMacros(child, ATTR_SHOWLEFT, true);
        boolean       showRight  = applyMacros(child, ATTR_SHOWRIGHT, false);

        int           width      = image.getWidth(null);
        int           height     = image.getHeight(null);
        int           centerX    = width / 2;
        int           centerY    = height / 2;
        EarthLocation nw, ne, se, sw;

        //don: this  what I added
        Double north = (Double) imageProps.get(ATTR_NORTH);
        Double south = (Double) imageProps.get(ATTR_SOUTH);
        Double east  = (Double) imageProps.get(ATTR_EAST);
        Double west  = (Double) imageProps.get(ATTR_WEST);
        //Assume if we have one we have them all
        if (north != null) {
            nw = DisplayControlImpl.makeEarthLocation(north.doubleValue(),
                    west.doubleValue(), 0);

            ne = DisplayControlImpl.makeEarthLocation(north.doubleValue(),
                    east.doubleValue(), 0);
            sw = DisplayControlImpl.makeEarthLocation(south.doubleValue(),
                    west.doubleValue(), 0);
            se = DisplayControlImpl.makeEarthLocation(south.doubleValue(),
                    east.doubleValue(), 0);

        } else {
            nw = display.screenToEarthLocation(0, 0);
            ne = display.screenToEarthLocation(width, 0);
            se = display.screenToEarthLocation(0, height);
            sw = display.screenToEarthLocation(width, height);
        }


        double widthDegrees = ne.getLongitude().getValue()
                              - nw.getLongitude().getValue();
        double heightDegrees = ne.getLatitude().getValue()
                               - se.getLatitude().getValue();

        Insets insets = getInsets(child, 0);
        int    delta  = 2;
        int    bgPad  = 1;

        image = doMatte(image, child, 0);

        Graphics2D g = (Graphics2D) image.getGraphics();
        g.setFont(getFont(child));
        FontMetrics fm            = g.getFontMetrics();

        int lineOffsetRight = applyMacros(child, ATTR_LINEOFFSET_RIGHT, 0);
        int lineOffsetLeft = applyMacros(child, ATTR_LINEOFFSET_LEFT, 0);
        int         lineOffsetTop = applyMacros(child, ATTR_LINEOFFSET_TOP,
                                        0);
        int lineOffsetBottom = applyMacros(child, ATTR_LINEOFFSET_BOTTOM, 0);



        Stroke      lineStroke;
        if (XmlUtil.hasAttribute(child, ATTR_DASHES)) {
            lineStroke = new BasicStroke((float) applyMacros(child,
                    ATTR_LINEWIDTH, 1.0), BasicStroke.CAP_BUTT,
                                          BasicStroke.JOIN_BEVEL, 1.0f,
                                          Misc.parseFloats(applyMacros(child,
                                              ATTR_DASHES, "8")), 0.0f);
        } else {
            lineStroke = new BasicStroke((float) applyMacros(child,
                    ATTR_LINEWIDTH, 1.0));
        }

        g.setStroke(lineStroke);
        double leftLon  = nw.getLongitude().getValue(CommonUnit.degree);
        double rightLon = ne.getLongitude().getValue(CommonUnit.degree);
        Range  lonRange = new Range(leftLon, rightLon);

        for (int i = 0; i < lonValues.length; i++) {
            double lon = GeoUtils.normalizeLongitude(lonRange, lonValues[i]);
            double percent = (lon - nw.getLongitude().getValue())
                             / widthDegrees;
            //            if(percent<0 || percent>1) continue;
            String label;
            if (i < lonLabels.size()) {
                label = lonLabels.get(i);
            } else {
                label = format.format(lonValues[i]);
            }
            Rectangle2D rect  = fm.getStringBounds(label, g);
            int         baseX = insets.left + (int) (percent * width);
            int         x     = baseX - (int) rect.getWidth() / 2;
            int         topY;
            if (insets.top == 0) {
                topY = (int) rect.getHeight() + delta;
            } else {
                topY = insets.top - delta;
            }
            if (drawLonLines) {
                g.setColor(lineColor);
                g.drawLine(baseX, insets.top + lineOffsetTop, baseX,
                           insets.top + height - lineOffsetBottom);
            }

            if (showTop) {
                if (bg != null) {
                    g.fillRect(x - bgPad,
                               topY - (int) rect.getHeight() - bgPad,
                               (int) rect.getWidth() + bgPad * 2,
                               (int) rect.getHeight() + bgPad * 2);
                }
                g.setColor(color);
                g.drawString(label, x, topY);
            }
            int bottomY;
            if (insets.bottom == 0) {
                bottomY = insets.top + height - delta;
            } else {
                bottomY = insets.top + height + (int) rect.getHeight()
                          + delta;
            }
            if (showBottom) {
                if (bg != null) {
                    g.setColor(bg);
                    g.fillRect(x - bgPad,
                               bottomY - (int) rect.getHeight() - bgPad,
                               (int) rect.getWidth() + bgPad * 2,
                               (int) rect.getHeight() + bgPad * 2);
                }
                g.setColor(color);
                g.drawString(label, x, bottomY);
            }
        }


        for (int i = 0; i < latValues.length; i++) {
            double lat = latValues[i];
            double percent = 1.0
                             - (lat - se.getLatitude().getValue())
                               / heightDegrees;
            int baseY = insets.top + (int) (percent * height);
            //            if(percent<0 || percent>1) continue;
            String label;
            if (i < latLabels.size()) {
                label = latLabels.get(i);
            } else {
                label = format.format(lat);
            }
            Rectangle2D rect = fm.getStringBounds(label, g);
            int         y    = baseY + (int) rect.getHeight() / 2;
            int         leftX;
            if (insets.left == 0) {
                leftX = 0 + delta;
            } else {
                leftX = insets.left - (int) rect.getWidth() - delta;
            }
            if (drawLonLines) {
                g.setColor(lineColor);
                g.drawLine(insets.left + lineOffsetRight, baseY,
                           insets.left + width - lineOffsetRight, baseY);
            }

            if (showLeft) {
                if (bg != null) {
                    g.setColor(bg);
                    g.fillRect(leftX - bgPad,
                               y - (int) rect.getHeight() - bgPad,
                               (int) rect.getWidth() + bgPad * 2,
                               (int) rect.getHeight() + bgPad * 2);
                }
                g.setColor(color);
                g.drawString(label, leftX, y);
            }

            if (insets.right == 0) {
                leftX = insets.left + width - (int) rect.getWidth() - delta;
            } else {
                leftX = insets.left + width + delta;
            }
            if (showRight) {
                if (bg != null) {
                    g.setColor(bg);
                    g.fillRect(leftX - bgPad,
                               y - (int) rect.getHeight() - bgPad,
                               (int) rect.getWidth() + bgPad * 2,
                               (int) rect.getHeight() + bgPad * 2);
                }
                g.setColor(color);
                g.drawString(label, leftX, y);
            }
        }

        return image;

    }

    /**
     * Matte the image
     *
     * @param image  the image
     * @param child  the XML defining the matting
     * @param dfltSpace  default spacing
     * @return a new image
     */
    public BufferedImage doMatte(BufferedImage image, Element child,
                                 int dfltSpace) {
        return doMatte(image, child, getInsets(child, dfltSpace));
    }


    /**
     * Matte the image
     *
     * @param image  the image
     * @param child  the matte specs
     * @param insets the insets
     *
     * @return  a new image
     */
    public BufferedImage doMatte(BufferedImage image, Element child,
                                 Insets insets) {
        Color bg = applyMacros(child, ATTR_BACKGROUND, Color.white);
        return ImageUtils.matte(image, insets.top, insets.bottom,
                                insets.left, insets.right, bg);
    }



    /*
      public void setDataSourceFiles(String[] datasource, String[] filenames) {

      List dataSources = getIdv().getDataSources();
        for (int k = 0; k < datasource.size(); k++) {
          for (int i = 0; i < dataSources.size(); i++) {
            DataSource theDataSource = (DataSource) dataSources.get(i);
            if (theDataSource.identifiedByName(datasource[k])) {
              theDataSource.setNewFiles(new ArrayList().add(filenames[k]));
            }
          }
        }
      }

     */


    /**
     * Get the file name to write images to. If we are in test mode then prepend the test directory
     *
     * @param filename image file name
     *
     * @return filename to use
     */
    private String getImageFileName(String filename) {
        if (LogUtil.getTestMode()) {
            if (getIdv().getArgsManager().testDir != null) {
                filename = IOUtil.joinDir(getIdv().getArgsManager().testDir,
                                          filename);
            }
        }
        return filename;
    }


    /**
     * Set the font on the graphics from the font defined on the node.
     *
     * @param g The graphics
     * @param node Node to get font info from
     */
    private void setFont(Graphics g, Element node) {
        int fontSize = applyMacros(node, ATTR_FONTSIZE, 12);
        Font f = new Font(applyMacros(node, ATTR_FONTFACE, "dialog"),
                          Font.PLAIN, fontSize);
        g.setFont(f);
    }


    /**
     * Get the font from the XML
     *
     * @param node  the XML
     *
     * @return  the font or null
     */
    private Font getFont(Element node) {
        if (XmlUtil.hasAttribute(node, ATTR_FONTSIZE)
                || XmlUtil.hasAttribute(node, ATTR_FONTFACE)) {
            int fontSize = applyMacros(node, ATTR_FONTSIZE, 12);
            return new Font(applyMacros(node, ATTR_FONTFACE, "dialog"),
                            Font.PLAIN, fontSize);
        }
        return null;
    }



    /**
     * Called to notify this object that the movie capture is done
     */
    public synchronized void doneCapturingMovie() {
        this.notify();
    }


    /**
     * Capture a movie from the first view manager
     *
     * @param filename The movie  filename
     */
    /**
    public synchronized void captureMovie(String filename) {
        captureMovie(filename, null);
    }


    /**
     * Capture the movie
     *
     * @param filename The file
     * @param scriptingNode Node form isl.
     */
    public synchronized void captureMovie(String filename,
                                          Element scriptingNode) {

         * to string
        if ((filename == null) && (scriptingNode != null)) {
            filename = XmlUtil.getAttribute(scriptingNode, ATTR_FILE);
        }

        if (scriptingNode != null) {
            List files = findFiles(scriptingNode);
            if (files != null) {
                debug("Making movie from existing images " + filename);
                filename = applyMacros(filename);
                Dimension size = new Dimension(applyMacros(scriptingNode,
                                     ATTR_WIDTH,
                                     400), applyMacros(scriptingNode,
                                         ATTR_HEIGHT, 300));
                ImageSequenceGrabber isg = new ImageSequenceGrabber(
                                               filename, getIdv(), this,
                                               scriptingNode, files, size,
                                               applyMacros(
                                                   scriptingNode,
                                                   ATTR_FRAMERATE,
                                                   2), applyMacros(
                                                       scriptingNode,
                                                       ATTR_ENDFRAMEPAUSE,
                                                       -1));
                return;
            }
        }

        List viewManagers = null;
        if ((scriptingNode != null)
                && XmlUtil.hasAttribute(scriptingNode, ATTR_DISPLAY)) {
            DisplayControlImpl display = findDisplayControl(scriptingNode);
            if (display == null) {
                throw new IllegalArgumentException("Could not find display:"
                        + XmlUtil.toString(scriptingNode));
            }
            String what = applyMacros(scriptingNode, ATTR_WHAT,
                                      (String) null);

            ViewManager viewManager = null;
            try {
                viewManager = display.getViewManagerForCapture(what);
                if (viewManager != null) {
                    viewManager.updateDisplayIfNeeded();
                }
            } catch (Exception exc) {
                throw new RuntimeException(exc);
            }

            if (viewManager != null) {
                viewManagers = (List) Misc.newList(viewManager);
            } else {
                throw new IllegalArgumentException(
                    "Cannot capture a movie with display:"
                    + XmlUtil.toString(scriptingNode));
            }
        }

        if (viewManagers == null) {
            viewManagers = (List) getViewManagers(scriptingNode);
        }



        boolean combine = XmlUtil.getAttribute(scriptingNode,
                              ImageGenerator.ATTR_COMBINE, false);


        if (combine) {
            ViewManager viewManager =
                getIdv().getVMManager().getLastActiveViewManager();

            getProperties().put(PROP_VIEWINDEX, new Integer(0));
            String name = viewManager.getName();
            if (name == null) {
                name = "view" + 0;
            }
            getProperties().put(PROP_VIEWNAME, name);

            if ( !getIdv().getArgsManager().getIsOffScreen()) {
                JFrame frame = GuiUtils.getFrame(viewManager.getContents());
                if (frame != null) {
                    LogUtil.registerWindow(frame);
                    frame.show();
                    GuiUtils.toFront(frame);
                    frame.setLocation(50, 50);
                    Misc.sleep(50);
                }
            }
            String loopFilename = applyMacros(filename);
            debug("Making movie:" + loopFilename);
         *
            ImageSequenceGrabber isg = new ImageSequenceGrabber(viewManager,
                                           loopFilename, getIdv(), this,
                                           scriptingNode);
            try {
                wait();
            } catch (Exception exc) {
                logException("Doing the captureMovie wait", exc);
            }
            debug("Done making movie:" + loopFilename);

        } else {

            for (int i = 0; i < viewManagers.size(); i++) {
                ViewManager viewManager = viewManagers.get(i);

                getProperties().put(PROP_VIEWINDEX, new Integer(i));
                String name = viewManager.getName();
                if (name == null) {
                    name = "view" + i;
                }
                getProperties().put(PROP_VIEWNAME, name);

                if ( !getIdv().getArgsManager().getIsOffScreen()) {
                    JFrame frame =
                        GuiUtils.getFrame(viewManager.getContents());
                    if (frame != null) {
                        LogUtil.registerWindow(frame);
                        frame.show();
                        GuiUtils.toFront(frame);
                        frame.setLocation(50, 50);
                        Misc.sleep(50);
                    }
                }
                String loopFilename = applyMacros(filename);
                debug("Making movie:" + loopFilename);
                ImageSequenceGrabber isg =
                    new ImageSequenceGrabber(viewManager, loopFilename,
                                             getIdv(), this, scriptingNode);
                try {
                    wait();
                } catch (Exception exc) {
                    logException("Doing the captureMovie wait", exc);
                }
                debug("Done making movie:" + loopFilename);
            }
        }

    }


    /**
     * Find the animation time of the first Animation in a view manager we find
     *
     * @return Animation time
     */
    public Date getAnimationTime() {
        List vms = getViewManagers(currentNode);
        if (vms.size() > 0) {
            ViewManager vm        = (ViewManager) vms.get(0);
            Animation   animation = vm.getAnimation();
            if (animation != null) {
                Real v = animation.getAniValue();
                if (v != null) {
                    return new Date((long) v.getValue() * 1000);
                }

            }
        }
        return new Date(Misc.getCurrentTime());
    }


    /**
     * Create and instantiate the jython interp.
     *
     * @return The interp
     */
    private PythonInterpreter getInterpreter() {
        if (interpreter == null) {
            interpreter = getIdv().getJythonManager().createInterpreter();
            interpreter.set("ig", this);
            interpreter.set("interp", this);
            interpreter.set("islInterpreter", this);
        }
        return interpreter;
    }

     * callable by jython to find the data choices that match the given pattern
     *
     * @param datasource data source
     * @param pattern pattern to match
     *
     * @return comma separated list of data choice names
     */
    public String fields(String datasource, String pattern) {
        DataSource dataSource = findDataSource(datasource);
        if (dataSource == null) {
            throw new IllegalArgumentException("Could not find data source:"
                    + datasource);
        }
        List choices;
        if ((pattern == null) || (pattern.length() == 0)) {
            choices = dataSource.getDataChoices();
        } else {
            choices = dataSource.findDataChoices(pattern);
        }

        List names = new ArrayList();
        for (int i = 0; i < choices.size(); i++) {
            DataChoice dataChoice = (DataChoice) choices.get(i);
            names.add(dataChoice.getName());
        }
        return StringUtil.join(",", names);
    }



    /**
     * Class OutputInfo is used for handling output tags
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.113 $
     */
    private class OutputInfo {

        /** The node */
        Element outputNode;

        /** mapping of where to StringBuffer */
        Hashtable buffers = new Hashtable();

        /** mapping of where to templates */
        Hashtable templates = new Hashtable();

        /**
         * ctor
         *
         * @param node The output node
         */
        public OutputInfo(Element node) {
            this.outputNode = node;
        }

        /**
         * Handle the node.
         *
         * @param node Node to process
         *
         * @throws Throwable On badness
         */
        public void process(Element node) throws Throwable {
            String       where = applyMacros(node, ATTR_TEMPLATE, "contents");
            StringBuffer sb       = (StringBuffer) buffers.get(where);
            String       template = (String) templates.get(where);
            if (sb == null) {
                sb = new StringBuffer();
                template = XmlUtil.getAttribute(outputNode,
                        ATTR_TEMPLATE + ":" + where, "${text}");
                if (template.startsWith("file:")) {
                    template = applyMacros(template);
                    template = IOUtil.readContents(template.substring(5));
                }
                buffers.put(where, sb);
                templates.put(where, template);
            }
            String text = XmlUtil.getAttribute(node, ATTR_TEXT,
                              (String) null);
            if (text == null) {
                if (XmlUtil.hasAttribute(node, ATTR_FROMFILE)) {
                    String filename = applyMacros(node, ATTR_FROMFILE);
                    text = applyMacros(IOUtil.readContents(filename));
                } else {
                    text = XmlUtil.getChildText(node);
                    if ((text != null) && (text.length() == 0)) {
                        text = null;
                    }
                }
            }
            if (text == null) {
                NamedNodeMap nnm   = node.getAttributes();
                Hashtable    props = new Hashtable();
                if (nnm != null) {
                    for (int i = 0; i < nnm.getLength(); i++) {
                        Attr attr = (Attr) nnm.item(i);
     *
                        if ( !ATTR_TEMPLATE.equals(attr.getNodeName())) {
                            props.put(attr.getNodeName(),
                                      applyMacros(attr.getNodeValue()));
                        }
                    }
                }
                text = applyMacros(template, props);
            } else {
                text = applyMacros(text);
            }
            sb.append(text);
        }
         * Write out the output
         *
         * @throws Throwable On badness
         */
        public void write() throws Throwable {
            String outputFile = applyMacros(outputNode, ATTR_FILE);
            String template = applyMacros(outputNode, ATTR_TEMPLATE,
                                          (String) null);
            if (template == null) {
                template = "${contents}";
            }
            if (template.startsWith("file:")) {
                template = IOUtil.readContents(template.substring(5));
            }
            for (Enumeration keys =
                    buffers.keys(); keys.hasMoreElements(); ) {
                String       key  = (String) keys.nextElement();
                StringBuffer buff = (StringBuffer) buffers.get(key);
                template = applyMacros(template,
                                       Misc.newHashtable(key,
                                           buff.toString()));
            }
            IOUtil.writeFile(outputFile, template);
        }

    }


    /**
     * Print out a wanring message
     *
     * @param msg message
     */
    private void warning(String msg) {
        System.err.println(new Date() + " WARNING:" + msg);
    }



    /**
     * Print the message if in debug mode
     *
     * @param msg The message
     */
    protected void debug(String msg) {
        if (debug) {
            System.out.println(new Date() + ": " + msg);
        }
    }


    /**
     * Class MyBreakException for handling break tags
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.113 $
     */
    protected static class MyBreakException extends Exception {}

    /**
     * Class MyContinueException for handling continue tags
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.113 $
     */
    protected static class MyContinueException extends Exception {}

    /**
     * Class MyReturnException allows us to return from a isl procedure by throwing an exception.
     * Yes, I know you're not supposed to use exceptions in a non-exceptional way but it works
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.113 $
     */
    protected static class MyReturnException extends Exception {}


    /**
     * Class description
     *
     *
     * @version        Enter version here..., Tue, Jan 12, '10
     * @author         Enter your name here...
     */
    protected static class MyQuitException extends Exception {}

    /**
     * Class BadIslException is used to handle bad isl errors
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.113 $
     */
    private static class BadIslException extends RuntimeException {

        /** message */
        String msg;

        /**
         * ctor
         *
         * @param msg error message
         */
        public BadIslException(String msg) {
            this.msg = msg;
        }

         * @return error message
         */
        public String toString() {
            return msg;
        }

    }


    /**
     * IS the FtpClient in an ok state. If it isn't then disconnect it and throw and IllegalStateException
     *
     * @param f Ftp client
     * @param msg Message to use if in error
     *
     * @throws Exception On badness
     */
    private static void checkFtp(FTPClient f, String msg) throws Exception {
        int replyCode = f.getReplyCode();
        if ( !FTPReply.isPositiveCompletion(replyCode)) {
            String reply = f.getReplyString();
            f.disconnect();
            throw new IllegalStateException("Error with ftp: " + replyCode
                                            + " " + msg + "\n" + reply);
        }
    }

    /**
     * Do an FTP put of the given bytes
     *
     * @param server server
     * @param userName user name on server
     * @param password password on server
     * @param destination Where to put the bytes
     * @param bytes The bytes
     *
     * @throws Exception On badness
     */
    public static void ftpPut(String server, String userName,
                              String password, String destination,
                              byte[] bytes)
            throws Exception {
        FTPClient f = new FTPClient();

        f.connect(server);
        f.login(userName, password);
        f.setFileType(FTP.BINARY_FILE_TYPE);
        f.enterLocalPassiveMode();
        checkFtp(f, "Connecting to ftp server");
        f.storeFile(destination, new ByteArrayInputStream(bytes));
        checkFtp(f, "Storing file");
        f.logout();
        f.disconnect();
    }

    /** abcdefg... */
    private static String[] alphabet = {
        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
        "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
    };

    /** roman numerals */
    private static String[] roman = {
        "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI",
        "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XX", "XXI",
        "XXII", "XXIII", "XXIV", "XXV", "XXVI", "XXVII", "XXVIII"
    };

    /**
     * Get the letter for the index
     *
     * @param i  the index
     *
     * @return  the letter
     */
    public String getLetter(int i) {
        if ((i >= 0) && (i < alphabet.length)) {
            return alphabet[i];
        }
        //A hack for now
        return "out of range";

    }

    /**
     * Get the roman numeral
     *
     * @param i the index
     * @return  the corresponding number
     */
    public String getRoman(int i) {
        if ((i >= 0) && (i < roman.length)) {
            return roman[i];
        }
        //A hack for now
        return "out of range";
    }



}
>>>>>>> 72c1c20662f16e1b98193bc09dd2baf4024fb28a
Solution content
/*
 * Copyright 1997-2013 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 * 
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
import java.text.DecimalFormat;
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package ucar.unidata.idv.ui;


import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;

import org.python.util.PythonInterpreter;

import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;

import ucar.unidata.data.DataChoice;
import ucar.unidata.data.DataSelection;
import ucar.unidata.data.DataSource;
import ucar.unidata.data.GeoLocationInfo;
import ucar.unidata.data.grid.GridDataSource;
import ucar.unidata.geoloc.ProjectionRect;
import ucar.unidata.idv.ControlDescriptor;
import ucar.unidata.idv.DisplayControl;
import ucar.unidata.idv.IdvManager;
import ucar.unidata.idv.IdvPersistenceManager;
import ucar.unidata.idv.IntegratedDataViewer;
import ucar.unidata.idv.MapViewManager;
import ucar.unidata.idv.VectorGraphicsRenderer;
import ucar.unidata.idv.ViewDescriptor;
import ucar.unidata.idv.ViewManager;
import ucar.unidata.idv.control.DisplayControlImpl;
import ucar.unidata.ui.ImageUtils;
import ucar.unidata.ui.colortable.ColorTableCanvas;
import ucar.unidata.util.ColorTable;
import ucar.unidata.util.FileManager;
import ucar.unidata.util.GuiUtils;
import ucar.unidata.util.IOUtil;
import ucar.unidata.util.LogUtil;
import ucar.unidata.util.Misc;
import ucar.unidata.util.PatternFileFilter;
import ucar.unidata.util.Range;
import ucar.unidata.util.StringUtil;
import ucar.unidata.util.Trace;
import ucar.unidata.view.geoloc.NavigatedDisplay;
import ucar.unidata.view.geoloc.ViewpointInfo;
import ucar.unidata.xml.XmlUtil;

import ucar.visad.GeoUtils;
import ucar.visad.display.Animation;
import ucar.visad.display.AnimationWidget;

import visad.CommonUnit;
import visad.MouseBehavior;
import visad.Real;
import visad.RealType;
import visad.Unit;

import visad.georef.EarthLocation;
import visad.georef.EarthLocationTuple;
import visad.georef.LatLonPoint;
import visad.georef.MapProjection;

import visad.util.BaseRGBMap;
import visad.util.ColorPreview;


import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import java.text.SimpleDateFormat;

import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.TimeZone;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;


/**
 * Manages the user interface for the IDV
 *
 *
 * @author IDV development team
 */
public class ImageGenerator extends IdvManager {


    /** attr value */
    public static final String VALUE_TOP = "top";

    /** attr value */
    public static final String VALUE_BOTTOM = "bottom";

    /** attr value */
    public static final String VALUE_RIGHT = "right";

    /** attr value */
    public static final String VALUE_LEFT = "left";

    /** attr value */
    public static final String VALUE_ALL = "all";

    /** attr value */
    public static final String VALUE_NONE = "none";

    /** attr value */
    public static final String VALUE_FIRST = "first";

    /** attr value */
    public static final String VALUE_LAST = "last";

    /** macro property */
    public static final String PROP_LOOPINDEX = "loopindex";

    /** padded loop index */
    public static final String PROP_LOOPINDEX_PAD2 = "loopindex_pad2";

    /** padded loop index */
    public static final String PROP_LOOPINDEX_PAD3 = "loopindex_pad3";

    /** padded loop index */
    public static final String PROP_LOOPINDEX_PAD4 = "loopindex_pad4";




    /** macro property */
    public static final String PROP_VIEWINDEX = "viewindex";


    /** macro property */
    public static final String PROP_VIEWNAME = "viewname";

    /** macro property */
    public static final String PROP_IMAGEINDEX = "imageindex";


    /** macro property */
    public static final String PROP_IMAGEFILE = "imagefile";

    /** macro property */
    public static final String PROP_IMAGEPATH = "imagepath";


    /** file property */
    public static final String PROP_FILE = "file";

    /** filenosuffix property */
    public static final String PROP_FILENOSUFFIX = "filenosuffix";

    /** filetail property */
    public static final String PROP_FILETAIL = "filetail";

    /** filetailnosuffix property */
    public static final String PROP_FILETAILNOSUFFIX = "filetailnosuffix";

    /** fileprefix property */
    public static final String PROP_FILEPREFIX = "fileprefix";

    /** macro property */
    public static final String PROP_CONTENTS = "contents";

    /** macro property */
    public static final String PROP_ANIMATIONTIME = "animationtime";


    /** macro property */
    public static final String PROP_OFFSCREEN = "offscreen";



    /** Macro name */
    private static final String[] DATE_PROPS = {
        "G", "yy", "yyyy", "MM", "M", "MMM", "MMMMM", "HH", "H", "k", "kk",
        "D", "d", "dd", "K", "KK", "a", "mm", "ss", "s", "S", "EEE", "Z"
    };

    /** List of SimpleDateFormat objects. One for each DATE_PROPS. */
    private static List DATE_FORMATS;


    /** isl tag */
    public static final String TAG_FILESET = "fileset";


    /** view tag */
    public static final String TAG_VIEW = "view";

    /** isl tag */
    public static final String TAG_TEMPLATE = "template";


    /** isl tag */
    public static final String TAG_APPEND = "append";

    /** isl tag */
    public static final String TAG_SETFILES = "setfiles";


    /** isl tag */
    public static final String TAG_ISL = "isl";

    /** viewpoint tag */
    public static final String TAG_VIEWPOINT = "viewpoint";

    /** isl tag */
    public static final String TAG_PROPERTY = "property";

    /** isl tag */
    public static final String TAG_IMPORT = "import";

    /** isl tag */
    public static final String TAG_IMAGE = "image";

    /** isl tag */
    public static final String TAG_GROUP = "group";

    /** isl tag */
    public static final String TAG_PAUSE = "pause";

    /** isl tag */
    public static final String TAG_MOVIE = "movie";

    /** isl tag */
    public static final String TAG_BUNDLE = "bundle";

    /** isl tag */
    public static final String TAG_ELSE = "else";

    /** isl tag */
    public static final String TAG_THEN = "then";


    /** isl tag */
    public static final String TAG_COLORBAR = "colorbar";

    /** isl tag */
    public static final String TAG_CLIP = "clip";



    /** publish tag */
    public static final String TAG_PUBLISH = "publish";

    /** isl tag */
    public static final String TAG_DISPLAY = "display";

    /** datasource tag */
    public static final String TAG_DATASOURCE = "datasource";

    /** isl tag */
    public static final String TAG_MATTE = "matte";

    /** show tag */
    public static final String TAG_SHOW = "show";

    /** displaylist tag */
    public static final String TAG_DISPLAYLIST = "displaylist";

    /** isl tag */
    public static final String TAG_OUTPUT = "output";


    /** isl tag */
    public static final String TAG_OVERLAY = "overlay";


    /** isl tag */
    public static final String TAG_KML = "kml";

    /** kml colorbar tag */
    public static final String TAG_KML_COLORBAR = "kmlcolorbar";

    /** isl tag */
    public static final String TAG_KMZFILE = "kmzfile";

    /** isl tag */
    public static final String TAG_SPLIT = "split";

    /** isl tag */
    public static final String TAG_RESIZE = "resize";

    /** isl tag */
    public static final String TAG_THUMBNAIL = "thumbnail";

    /** isl tag */
    public static final String TAG_TRANSPARENT = "transparent";

    /** background transparent tag */
    public static final String TAG_BGTRANSPARENT = "backgroundtransparent";


    /** index attribute */
    public static final String ATTR_INDEX = "index";

    /** stride attribute */
    public static final String ATTR_STRIDE = "stride";

    /** x stride attribute */
    public static final String ATTR_STRIDEX = "stridex";

    /** y stride attribute */
    public static final String ATTR_STRIDEY = "stridey";

    /** z stride attribute */
    public static final String ATTR_STRIDEZ = "stridez";

    /** bounding box attribute */
    public static final String ATTR_BBOX = "bbox";

    /** from level attribute */
    public static final String ATTR_LEVEL_FROM = "levelfrom";

    /** to level attribute */
    public static final String ATTR_LEVEL_TO = "levelto";

    /** azimuth attribute */
    public static final String ATTR_AZIMUTH = "azimuth";

    /** tilt attribute */
    public static final String ATTR_TILT = "tilt";

    /** x aspect attribute */
    public static final String ATTR_ASPECTX = "aspectx";

    /** y aspect attribute */
    public static final String ATTR_ASPECTY = "aspecty";

    /** z aspect attribute */
    public static final String ATTR_ASPECTZ = "aspectz";


    /** x rotation attribute */
    public static final String ATTR_ROTX = "rotx";

    /** y rotation attribute */
    public static final String ATTR_ROTY = "roty";

    /** z rotation attribute */
    public static final String ATTR_ROTZ = "rotz";

    /** scale attribute */
    public static final String ATTR_SCALE = "scale";

    /** x translation attribute */
    public static final String ATTR_TRANSX = "transx";

    /** y translation attribute */
    public static final String ATTR_TRANSY = "transy";

    /** z translation attribute */
    public static final String ATTR_TRANSZ = "transz";


    /** suffix attribute */
    public static final String ATTR_SUFFIX = "suffix";

    /** showunit atttribute */
    public static final String ATTR_SHOWUNIT = "showunit";

    /** transparency attribute */
    public static final String ATTR_TRANSPARENCY = "transparency";

    /** top attribute */
    public static final String ATTR_TOP = "top";


    /** left space attribute */
    public static final String ATTR_SPACE_LEFT = "space_left";

    /** right space attribute */
    public static final String ATTR_SPACE_RIGHT = "space_right";

    /** top space attribute */
    public static final String ATTR_SPACE_TOP = "space_top";

    /** bottom space attribute */
    public static final String ATTR_SPACE_BOTTOM = "space_bottom";




    /** isl tag */
    public static final String TAG_WRITE = "write";



    /** isl tag */
    public static final String ATTR_ANCHOR = "anchor";


    /** isl attr */
    public static final String ATTR_FROM = "from";

    /** isl attr */
    public static final String ATTR_TO = "to";


    /** isl attribute */
    public static final String ATTR_GLOBAL = "global";

    /** isl attribute */
    public static final String ATTR_ONERROR = "onerror";

    /** isl attribute */
    public static final String ATTR_SORT = "sort";

    /** isl attribute */
    public static final String ATTR_SORTDIR = "sortdir";

    /** isl attribute */
    public static final String VALUE_TIME = "time";

    /** isl attribute */
    public static final String VALUE_ASCENDING = "ascending";

    /** isl attribute */
    public static final String VALUE_DESCENDING = "descending";


    /** isl attribute */
    public static final String ATTR_FIRST = "first";

    /** isl attribute */
    public static final String ATTR_LAST = "last";



    /** isl tag */
    public static final String ATTR_USEPROJECTION = "useprojection";

    /** isl tag */
    public static final String ATTR_EXPR = "expr";

    /** isl tag */
    public static final String ATTR_COPY = "copy";

    /** the count tag */
    public static final String ATTR_COUNT = "count";

    /** isl tag */
    public static final String ATTR_COLUMNS = "columns";

    /** isl attribute */
    public static final String ATTR_DATASOURCE = "datasource";


    /** isl attribute */
    public static final String ATTR_DESTINATION = "destination";

    /** isl attribute */
    public static final String ATTR_SERVER = "server";

    /** isl attribute */
    public static final String ATTR_PASSWORD = "password";

    /** isl attribute */
    public static final String ATTR_USER = "user";

    /** isl tag */
    public static final String ATTR_ROWS = "rows";

    /** isl tag */
    public static final String ATTR_CLASS = "class";

    /** isl tag */
    public static final String ATTR_ANGLE = "angle";

    /** isl tag */
    public static final String ATTR_WHERE = "where";

    /** isl tag */
    public static final String ATTR_BACKGROUND = "background";

    /** isl attribute */
    public static final String ATTR_BUNDLE = "bundle";

    /** isl tag */
    public static final String ATTR_SHOWLINES = "showlines";

    /** isl tag */
    public static final String ATTR_LINECOLOR = "linecolor";

    /** isl tag */
    public static final String ATTR_COLOR = "color";

    /** isl tag */
    public static final String ATTR_COMMAND = "command";

    /** isl tag */
    public static final String ATTR_FONTFACE = "fontface";

    /** isl attr */
    public static final String ATTR_FORMAT = "format";

    /** that latlonlabels tag */
    public static final String TAG_LATLONLABELS = "latlonlabels";

    /** that latvalues tag */
    public static final String ATTR_LAT_VALUES = "latvalues";

    /** that latlabels tag */
    public static final String ATTR_LAT_LABELS = "latlabels";

    /** that lonvalues tag */
    public static final String ATTR_LON_VALUES = "lonvalues";


    /** that lonlabels tag */
    public static final String ATTR_LON_LABELS = "lonlabels";

    /** the draw lon lines tag */
    public static final String ATTR_DRAWLONLINES = "drawlonlines";

    /** the draw lat lines tag */
    public static final String ATTR_DRAWLATLINES = "drawlatlines";

    /** dashes tag */
    public static final String ATTR_DASHES = "dashes";

    /** linewidth tag */
    public static final String ATTR_LINEWIDTH = "linewidth";

    /** isl tag */
    public static final String ATTR_LINEOFFSET_RIGHT = "lineoffsetright";

    /** isl tag */
    public static final String ATTR_LINEOFFSET_LEFT = "lineoffsetleft";

    /** isl tag */
    public static final String ATTR_LINEOFFSET_TOP = "lineoffsettop";

    /** isl tag */
    public static final String ATTR_LINEOFFSET_BOTTOM = "lineoffsetbottom";

    /** isl tag */
    public static final String ATTR_LABELBACKGROUND = "labelbackground";

    /** isl tag */
    public static final String ATTR_SHOWTOP = "showtop";

    /** isl tag */
    public static final String ATTR_SHOWBOTTOM = "showbottom";

    /** isl tag */
    public static final String ATTR_SHOWLEFT = "showleft";

    /** isl tag */
    public static final String ATTR_SHOWRIGHT = "showright";

    /** isl tag */
    public static final String ATTR_FONTSIZE = "fontsize";

    /** isl tag */
    public static final String ATTR_FRAMERATE = "framerate";

    /** isl tag for ending frame pause for animated gifs */
    public static final String ATTR_ENDFRAMEPAUSE = "endframepause";

    /** isl tag */
    public static final String ATTR_CAPTION = "caption";

    /** isl tag */
    public static final String ATTR_DEBUG = "debug";

    /** isl tag */
    public static final String ATTR_DEFAULT = "default";

    /** isl tag */
    public static final String ATTR_DISPLAY = "display";

    /** isl tag */
    public static final String ATTR_OFFSCREEN = "offscreen";

    /** isl tag */
    public static final String ATTR_TIMES = "times";

    /** isl tag */
    public static final String ATTR_ENSEMBLES = "ensembles";

    /** isl tag */
    public static final String ATTR_DIR = "dir";

    /** isl tag */
    public static final String ATTR_PATTERN = "pattern";


    /** isl attribute */
    public static final String ATTR_WAIT = "wait";

    /** isl tag */
    public static final String ATTR_PROPERTY = "property";

    /** isl tag */
    public static final String ATTR_QUALITY = "quality";

    /** isl tag */
    public static final String ATTR_LOOP = "loop";

    /** isl tag */
    public static final String ATTR_ENTRY = "entry";

    /** isl tag */
    public static final String ATTR_ID = "id";

    /** isl tag */
    public static final String ATTR_IMAGE = "image";

    /** isl tag */
    public static final String ATTR_INTERVAL = "interval";

    /** isl tag */
    public static final String ATTR_LEFT = "left";

    /** isl tag */
    public static final String ATTR_MESSAGE = "message";

    /** isl tag */
    public static final String ATTR_MATTEBG = "mattebg";

    /** isl tag */
    public static final String ATTR_NAME = "name";

    /** isl tag */
    public static final String ATTR_RIGHT = "right";

    /** isl tag */
    public static final String ATTR_TICKMARKS = "tickmarks";

    /** isl tag */
    public static final String ATTR_SPACE = "space";

    /** isl tag */
    public static final String ATTR_HSPACE = "hspace";

    /** isl tag */
    public static final String ATTR_VSPACE = "vspace";

    /** isl tag */
    public static final String ATTR_BOTTOM = "bottom";

    /** the valign attribute */
    public static final String ATTR_VALIGN = "valign";

    /** isl tag */
    public static final String ATTR_TEXT = "text";

    /** isl tag */
    public static final String ATTR_TEMPLATE = "template";

    /** isl tag */
    public static final String ATTR_TYPE = "type";

    /** isl tag */
    public static final String ATTR_EVERY = "every";

    /** isl tag */
    public static final String ATTR_VALUE = "value";

    /** isl tag */
    public static final String ATTR_VALUES = "values";

    /** isl tag */
    public static final String ATTR_ORIENTATION = "orientation";


    /** isl tag */
    public static final String ATTR_PARAM = "param";

    /** isl tag */
    public static final String ATTR_PLACE = "place";

    /** isl tag */
    public static final String ATTR_VIEW = "view";

    /** the view dir attribute */
    public static final String ATTR_VIEWDIR = "viewdir";

    /** isl tag */
    public static final String ATTR_URL = "url";

    /** isl tag */
    public static final String ATTR_FILE = "file";

    /** isl tag */
    public static final String ATTR_FROMFILE = "fromfile";

    /** isl tag */
    public static final String ATTR_NORTH = "north";

    /** isl tag */
    public static final String ATTR_SOUTH = "south";

    /** isl tag */
    public static final String ATTR_EAST = "east";

    /** isl tag */
    public static final String ATTR_WEST = "west";

    /** isl tag */
    public static final String ATTR_WIDTH = "width";

    /** isl tag */
    public static final String ATTR_HEIGHT = "height";

    /** isl tag */
    public static final String ATTR_SLEEP = "sleep";

    /** isl tag */
    public static final String ATTR_SECONDS = "seconds";

    /** isl tag */
    public static final String ATTR_MINUTES = "minutes";

    /** isl tag */
    public static final String ATTR_HOURS = "hours";

    /** isl tag */
    public static final String ATTR_CLEAR = "clear";

    /** isl tag */
    public static final String ATTR_CODE = "code";

    /** isl tag */
    public static final String ATTR_LAT = "lat";

    /** isl tag */
    public static final String ATTR_LON = "lon";

    /** isl attribute */
    public static final String ATTR_WHAT = "what";

    /** isl attribute */
    public static final String ATTR_COMBINE = "combine";

    /** isl attribute */
    public static final String ATTR_ANIMATION_INDEX = "animation_index";

    /** isl attribute */
    private static final String ATTR_SUFFIXFREQUENCY = "suffixfrequency";

    /** Show debug messages */
    private boolean debug = false;

    /** Stack of properties hashtables */
    private List propertiesStack = new ArrayList();

    /** Maps id to data source */
    private Hashtable idToDataSource = new Hashtable();

    /** The interpreter */
    private PythonInterpreter interpreter;

    /** Stack of active OutputInfo objects */
    private List outputStack = new ArrayList();

    /** When we are looping this gets set so we can use in as a macro value */
    private int currentLoopIndex = 0;

    /** Holds the procedure elements. Maps procedure name to element. */
    private Hashtable procs;

    /** Holds the tag name to method */
    private Hashtable methods = new Hashtable();

    /** current xml node we are processing */
    private Element currentNode;


    /** Keep around the last image captured */
    private Image lastImage;

    /** an object map */
    private Hashtable objectMap;



    /**
     * Create me with the IDV
     *
     * @param idv The IDV
     */
    public ImageGenerator(IntegratedDataViewer idv) {
        super(idv);
    }

    /**
     * Create me with the IDV and start processing files
     *
     * @param idv The IDV
     * @param scriptFiles List of isl files
     */
    public ImageGenerator(IntegratedDataViewer idv, List scriptFiles) {
        super(idv);
        processScriptFiles(scriptFiles);
    }


    /**
     * Process the list of isl files
     *
     * @param scriptFiles isl files
     */
    public void processScriptFiles(List scriptFiles) {
        for (int fileIdx = 0; fileIdx < scriptFiles.size(); fileIdx++) {
            String filename = (String) scriptFiles.get(fileIdx);
            if ( !processScriptFile(filename)) {
                return;
            }
        }

    }


    /**
     * Process the  isl files
     *
     * @param islFile file
     *
     * @return Was it successful
     */
    public synchronized boolean processScriptFile(String islFile) {
        return processScriptFile(islFile, new Hashtable());
    }


    /**
     * Process the script file
     *
     * @param islFile  the ISL file
     * @param properties optional properties
     *
     * @return true if successful
     */
    public synchronized boolean processScriptFile(String islFile,
            Hashtable properties) {
        procs           = new Hashtable();
        idToDataSource  = new Hashtable();
        propertiesStack = new ArrayList();
        pushProperties();

        if (islFile.endsWith(".jy") || islFile.endsWith(".py")) {
            try {
                String islPath = IOUtil.getFileRoot(islFile);
                putProperty("islpath", islPath);
                String            jythonCode = IOUtil.readContents(islFile);
                PythonInterpreter interp     = getInterpreter();
                if (getIdv().getJythonManager().getInError()) {
                    return false;
                }

                interp.exec(jythonCode);
                popProperties();
                return true;
            } catch (Exception exc) {
                exc.printStackTrace();
                return error("Error running jython script:" + islFile + "\n"
                             + exc);
            }

        }


        Element root = null;
        try {
            InputStream is = null;
            try {
                if (islFile.startsWith("xml:")) {
                    String xml = islFile.substring(4);
                    is      = new ByteArrayInputStream(xml.getBytes());
                    islFile = "Inline isl";
                } else if (islFile.startsWith("b64:")) {
                    is = new ByteArrayInputStream(
                        XmlUtil.decodeBase64(islFile.substring(4)));
                    islFile = "Inline base 64 encoded isl";
                } else {
                    is = IOUtil.getInputStream(islFile, getClass());
                }
            } catch (FileNotFoundException fnfe) {}
            catch (IOException ioe) {}
            if (is == null) {
                return error(
                    "Given script file does not exist or could not be read: "
                    + islFile);
            }
            root = XmlUtil.getRoot(is);
        } catch (Exception exc) {
            exc.printStackTrace();
            return error("Could not load script file:" + islFile + "\n"
                         + exc);
        }
        if (root == null) {
            return error("Could not load script file:" + islFile);
        }
        try {
            String islPath = IOUtil.getFileRoot(islFile);
            putProperty("islpath", islPath);
            objectMap = properties;
            processNode(root);
            popProperties();
        } catch (InvocationTargetException ite) {
            Throwable inner = ite.getTargetException();
            while (inner instanceof InvocationTargetException) {
                inner =
                    ((InvocationTargetException) inner).getTargetException();
            }
            return handleError(inner);
        } catch (Throwable exc) {
            return handleError(exc);
        } finally {
            objectMap = null;
        }
        return true;
    }

    /**
     * Handle the error
     *
     * @param exc The error
     *
     * @return Just return false
     */
    private boolean handleError(Throwable exc) {
        if ( !(exc instanceof IllegalStateException)
    }
                && !(exc instanceof IllegalArgumentException)) {
            exc.printStackTrace();
        } else {
            exc.printStackTrace();
        }
        return error("An error occurred:" + exc);
    }

    /**
     * Find the inner most non InvocationTargetException exception
     *
     * @param ite The exception
     *
     * @return First non InvocationTargetException exception
     */
    private Throwable getInnerException(InvocationTargetException ite) {
        Throwable inner = ite.getTargetException();
        while (inner instanceof InvocationTargetException) {
            inner = ((InvocationTargetException) inner).getTargetException();
        }
        return inner;
    }


    /**
     * Process the node
     *
     * @param node The node
     * @return ok
     * @throws Throwable On badness
     */
    private boolean processNode(Element node) throws Throwable {
        String tagName = node.getTagName();
        //        System.err.println("tag:" + tagName);

        String methodName = "processTag"
                            + tagName.substring(0, 1).toUpperCase()
                            + tagName.substring(1).toLowerCase();

        //Look to see if this is a isl procedure
        Element procNode = (Element) procs.get(tagName);
        if (procNode != null) {
            return processTagCall(node, procNode);
        }


        Object tmp = methods.get(methodName);
        if (tmp == null) {
            try {
                tmp = getClass().getDeclaredMethod(methodName,
                        new Class[] { Element.class });
            } catch (Exception exc) {}
            if (tmp == null) {
                tmp = "no method";
            }
            methods.put(methodName, tmp);
        }

        if (tmp instanceof Method) {
            try {
                currentNode = node;
                Object result = ((Method) tmp).invoke(this,
                                    new Object[] { node });
                if (result.equals(Boolean.TRUE)) {
                    return true;
                }
            } catch (InvocationTargetException ite) {
                Throwable inner = getInnerException(ite);
                if (inner instanceof BadIslException) {
                    return error("Error handling ISL node:"
                                 + XmlUtil.toStringNoChildren(currentNode)
                                 + "\t" + inner.toString());
                }
                throw inner;
            }
            return false;
        }
        return error("Unknown tag:" + tagName);
    }


    /**
     * Recursively call processNode on each of the children elements
     *
     * @param node The parent node.
     *
     * @return Success.
     *
     * @throws Throwable On badness
     */
    private boolean processChildren(Element node) throws Throwable {
        NodeList elements = XmlUtil.getElements(node);
        for (int childIdx = 0; childIdx < elements.getLength(); childIdx++) {
            Element child = (Element) elements.item(childIdx);
            try {
                if ( !processNode(child)) {
                    return false;
                }
            } catch (Throwable thr) {
                String onerror = applyMacros(node, ATTR_ONERROR,
        getIdv().removeAllDisplays(true);
                                             (String) null);
                if (onerror == null) {
                    throw thr;
                }
                if (onerror.equals("ignore")) {}
                else {
                    System.err.println("Error occured");
                    thr.printStackTrace();
                }
            }
        }
        return true;
    }





    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagFtp(Element node) throws Throwable {
        String file        = applyMacros(node, ATTR_FILE);
        String server      = applyMacros(node, ATTR_SERVER);
        String destination = applyMacros(node, ATTR_DESTINATION);
        String user        = applyMacros(node, ATTR_USER, "anonymous");
        String password = applyMacros(node, ATTR_PASSWORD,
                                      "idvuser@unidata.ucar.edu");
        byte[] bytes = IOUtil.readBytes(IOUtil.getInputStream(file));
        ftpPut(server, user, password, destination, bytes);
        return true;
    }


    /**
     * Process the export tag. This allows one to export data from a display. It requires
     * a display identifier
     *
     * @param node isl xml node
     *
     * @return everything is cool
     *
     * @throws Throwable On badness
     */
    protected boolean processTagExport(Element node) throws Throwable {
        DisplayControlImpl display = findDisplayControl(node);
        if (display == null) {
            throw new IllegalArgumentException("Could not find display:"
                    + XmlUtil.toString(node));
        }
        String what     = applyMacros(node, ATTR_WHAT, (String) null);

        String filename = XmlUtil.getAttribute(node, ATTR_FILE);
        display.doExport(what, filename);
        return true;
    }


    /**
     * Process the tag trace
     *
     * @param node  the node
     *
     * @return true if processed
     *
     * @throws Throwable  on badness
     */
    protected boolean processTagTrace(Element node) throws Throwable {
        String pattern = applyMacros(node, ATTR_PATTERN);
        debug("turning trace on for:" + pattern);
        Trace.startTrace();
        Trace.clearOnly();
        Trace.addOnly(pattern);
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagRemovedisplays(Element node)
            throws Throwable {

        if (XmlUtil.hasAttribute(node, ATTR_DISPLAY)) {
            debug("Removing display");
            DisplayControlImpl display = findDisplayControl(node);
            if (display == null) {
                return error("Could not find display:"
                             + XmlUtil.toString(node));
            }
            display.doRemove();
            return true;
        }
        debug("Removing all displays");
        return true;






    /**
     * Process the print cache tag
     *
     * @param node  the XML node
     *
     * @return  true if successful
     *
     * @throws Throwable  on badness
     */
    protected boolean processTagPrintcache(Element node) throws Throwable {
        visad.data.DataCacheManager.getCacheManager().printStats();
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagRemoveall(Element node) throws Throwable {
        debug("Removing all displays and data");
        getIdv().removeAllDisplays(false);
        getIdv().removeAllDataSources();
        idToDataSource = new Hashtable();
        return true;
    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagSetfiles(Element node) throws Throwable {
        DataSource dataSource = findDataSource(node);
        if (dataSource == null) {
            return error("Could not find data source");
        }
        List files = new ArrayList();
        if (XmlUtil.hasAttribute(node, ATTR_FILE)) {
            files.add(applyMacros(node, ATTR_FILE));
        } else {
            List filesetFiles = findFiles(node);
            if (filesetFiles != null) {
                files.addAll(filesetFiles);
            }
        }
        if (files.size() == 0) {
            return error("Could not find files");
        }
        dataSource.setNewFiles(files);
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagExists(Element node) throws Throwable {
        List    files  = findFiles(node);
        boolean exists = ((files != null) && (files.size() > 0));
        putProperty(applyMacros(node, ATTR_PROPERTY), (exists
                ? "1"
                : "0"));
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagAsk(Element node) throws Throwable {
        String  property = applyMacros(node, ATTR_PROPERTY);
        boolean result;
        if (getIdv().getArgsManager().getIsOffScreen()) {
            if ( !XmlUtil.hasAttribute(node, ATTR_DEFAULT)) {
                throw new IllegalStateException(
                    "Running in offscreen mode and the 'ask' node does not have a 'default' attribute");
            }
            result = applyMacros(node, ATTR_DEFAULT, true);
        } else {
            result = GuiUtils.showYesNoDialog(null,
    }
                    applyMacros(node, ATTR_MESSAGE), "");
        }
        putProperty(property, (result
                               ? "1"
                               : "0"));
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagEcho(Element node) throws Throwable {
        String message = applyMacros(node, ATTR_MESSAGE, (String) null);
        if (message == null) {
            message = applyMacros(XmlUtil.getChildText(node));
        }
        System.out.println(message);
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagAsktocontinue(Element node) throws Throwable {
        String message = applyMacros(node, ATTR_MESSAGE, (String) null);
        if (message == null) {
            message = applyMacros(XmlUtil.getChildText(node));
        }
        return GuiUtils.askOkCancel("ISL", message);
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagGc(Element node) throws Throwable {
        Runtime.getRuntime().gc();
        return true;
    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagBreak(Element node) throws Throwable {
        throw new MyBreakException();
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagContinue(Element node) throws Throwable {
        throw new MyContinueException();
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagReturn(Element node) throws Throwable {
        throw new MyReturnException();
    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagProcedure(Element node) throws Throwable {
        procs.put(applyMacros(node, ATTR_NAME), node);
        return true;

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagMkdir(Element node) throws Throwable {
        IOUtil.makeDir(applyMacros(node, ATTR_FILE));
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagStop(Element node) throws Throwable {
        return false;
    }


    /**
     * Get the property value
     *
     * @param node the XML node
     *
     * @return  the values
     *
     * @throws Throwable on badness
     */
    protected String[] getPropertyValue(Element node) throws Throwable {
        String name  = (String) applyMacros(node, ATTR_NAME);
        String value = null;
        if (XmlUtil.hasAttribute(node, ATTR_VALUE)) {
            value = (String) applyMacros(node, ATTR_VALUE);
        } else if (XmlUtil.hasAttribute(node, ATTR_FROMFILE)) {
            String filename = applyMacros(node, ATTR_FROMFILE);
            value = applyMacros(IOUtil.readContents(filename));
        } else {
            value = XmlUtil.getChildText(node);
            if ((value == null) || (value.trim().length() == 0)) {
                throw new IllegalArgumentException(
                    "No value in property tag: " + XmlUtil.toString(node));
            }
            value = applyMacros(value);
        }
        return new String[] { name, value };
    }

    /**
     * Process IDV property tag
     *
     * @param node  the XML node
     *
     * @return  true if successful
     *
     * @throws Throwable  on badness
     */
    protected boolean processTagIdvproperty(Element node) throws Throwable {
        String[] tuple = getPropertyValue(node);
        debug("setting idv property: " + tuple[0] + " =" + tuple[1]);
        getIdv().getStateManager().putProperty(applyMacros(tuple[0]),
                applyMacros(tuple[1]));
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagProperty(Element node) throws Throwable {
        String[] tuple = getPropertyValue(node);
        putProperty(applyMacros(tuple[0]), tuple[1],
                    applyMacros(node, ATTR_GLOBAL, false));
        return true;
    }




    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagMove(Element node) throws Throwable {
        List files = findFiles(node);
        if (files != null) {
            File dir = new File(applyMacros(node, ATTR_DIR));
            debug("moving files to: " + dir + " files=" + files);
            for (int i = 0; i < files.size(); i++) {
                IOUtil.moveFile(new File(files.get(i).toString()), dir);
            }
        }
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagRename(Element node) throws Throwable {
        String from = applyMacros(node, ATTR_FROM);
        String to   = applyMacros(node, ATTR_TO);
        IOUtil.moveFile(new File(from), new File(to));
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagDelete(Element node) throws Throwable {
        List files = findFiles(node);
        if (files != null) {
            debug("deleting files:" + files);
            for (int i = 0; i < files.size(); i++) {
                ((File) files.get(i)).delete();
            }
        }
        return true;
    }

    /**
     * Handle the clear tag
     *
     * @param node node
     *
     * @return ok
     *
     * @throws Throwable On badness
     */
    protected boolean processTagClear(Element node) throws Throwable {
        String    name = applyMacros(node, ATTR_NAME);
        Hashtable ht   = (Hashtable) propertiesStack.get(0);
        ht.remove(name);
        return true;
    }

    /**
     * Handle the append tag
     *
     * @param node node
     *
     * @return ok
     *
     * @throws Throwable On badness
     */
    protected boolean processTagAppend(Element node) throws Throwable {
        String    name  = applyMacros(node, ATTR_NAME);
        Hashtable ht    = findTableFor(name);
        String    value = (String) ht.get(name);
        if (value == null) {
            value = "";
        }
        if (XmlUtil.hasAttribute(node, ATTR_VALUE)) {
            value = value + applyMacros(node, ATTR_VALUE);
        } else if (XmlUtil.hasAttribute(node, ATTR_FROMFILE)) {
            String filename = applyMacros(node, ATTR_FROMFILE);
            value = value + applyMacros(IOUtil.readContents(filename));
        } else {
            value = value + applyMacros(XmlUtil.getChildText(node)).trim();
        }
        ht.put(name, value);
        return true;
    }


    /**
     * Handle the append tag
     *
     * @param node node
     *
     * @return ok
     *
     * @throws Throwable On badness
     */
    protected boolean processTagIncrement(Element node) throws Throwable {
        String    name  = applyMacros(node, ATTR_NAME);
        Hashtable ht    = findTableFor(name);
        String    value = (String) ht.get(name);
        if (value == null) {
            value = "0";
        }
        String by = "1";
        if (XmlUtil.hasAttribute(node, ATTR_VALUE)) {
            by = applyMacros(node, ATTR_VALUE);
        }
        double num = new Double(value).doubleValue()
                     + new Double(by).doubleValue();
        ht.put(name, "" + num);
        return true;
    }

    /**
     * Handle the append tag
     *
     * @param node node
     *
     * @return ok
     *
     * @throws Throwable On badness
     */
    protected boolean processTagReplace(Element node) throws Throwable {
        String    name = applyMacros(node, ATTR_NAME);
        Hashtable ht   = findTableFor(name);
        ht.put(name, applyMacros(node, ATTR_VALUE));
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagCopy(Element node) throws Throwable {
        List files = findFiles(node);
        if (files != null) {
            File dir = new File(applyMacros(node, ATTR_DIR));
            IOUtil.makeDir(dir);
            if ( !dir.isDirectory()) {
                return error("Specified file:" + dir + " is not a directory");
            }
            debug("copying files to: " + dir + " files=" + files);
            for (int i = 0; i < files.size(); i++) {
                IOUtil.copyFile(new File(files.get(i).toString()), dir);
            }
        }
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagReload(Element node) throws Throwable {
        List dataSources = getIdv().getDataSources();
        for (int i = 0; i < dataSources.size(); i++) {
            DataSource dataSource = (DataSource) dataSources.get(i);
            dataSource.reloadData();
        }
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
                }
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagExec(Element node) throws Throwable {
        String command = applyMacros(node, ATTR_COMMAND);
        debug("Calling exec:" + command);
        Process process = Runtime.getRuntime().exec(command);
        //This seems to hang?
        process.waitFor();
        if (process.exitValue() != 0) {
            String result = IOUtil.readContents(process.getInputStream());
            System.err.println("Exec:\n\t" + command + "\nreturned:"
                               + process.exitValue() + "\n" + result);
        }
        return true;

    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagJython(Element node) throws Throwable {
        String jythonFile = applyMacros(node, ATTR_FILE, (String) null);
        if (jythonFile != null) {
            InputStream is = IOUtil.getInputStream(jythonFile, getClass());
            if (is == null) {
                return error("Could not open jython file:" + jythonFile);
            } else {
                getInterpreter().execfile(is, jythonFile);
            }
        } else {
            String jython = applyMacros(node, ATTR_CODE, (String) null);
            if (jython == null) {
                jython = XmlUtil.getChildText(node);
            }
            if (jython != null) {
                getInterpreter().exec(jython);
            }
        }
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagFileset(Element node) throws Throwable {
        List files = findFiles(Misc.newList(node));
        pushProperties();
        for (int i = 0; i < files.size(); i++) {
            try {
                String filePath = files.get(i).toString();
                String tail     = IOUtil.getFileTail(filePath);
                putProperty(PROP_FILE, filePath);
                putProperty(PROP_FILEPREFIX, IOUtil.stripExtension(filePath));
                putProperty(PROP_FILENOSUFFIX,
                            IOUtil.stripExtension(filePath));
                putProperty(PROP_FILETAIL, tail);
                putProperty(PROP_FILETAILNOSUFFIX,
                            IOUtil.stripExtension(tail));
                if ( !processChildren(node)) {
                    return false;
                }
            } catch (MyBreakException be) {
                break;
            } catch (MyContinueException ce) {}
        }
        popProperties();
        return true;

    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagImport(Element node) throws Throwable {
        Element parent  = (Element) node.getParentNode();
        String  file    = applyMacros(node, ATTR_FILE);
        Element root    = XmlUtil.findRoot(node);
        Element newRoot = XmlUtil.getRoot(file, getClass());


        newRoot = (Element) root.getOwnerDocument().importNode(newRoot, true);
        parent.insertBefore(newRoot, node);
        parent.removeChild(node);
        if ( !processNode(newRoot)) {
            return false;
        }
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagDatasource(Element node) throws Throwable {
        debug("Creating data source");

        DataSource dataSource = null;
        String     id         = applyMacros(node, ATTR_ID, (String) null);
        if ((id != null) && (objectMap != null)) {
            dataSource = (DataSource) objectMap.get(id);
        }


        if (dataSource == null) {
            Object dataObject = applyMacros(node, ATTR_URL, (String) null);
            if (dataObject == null) {
                dataObject = StringUtil.toString(findFiles(node));
            }
            String bundle = applyMacros(node, ATTR_BUNDLE, (String) null);
            String type   = applyMacros(node, ATTR_TYPE, (String) null);
            if ((bundle == null) && (dataObject == null)) {
                return error(

                    "datasource tag requires either a url, fileset or a bundle");
            }

            if (dataObject != null) {
                dataSource = getIdv().makeOneDataSource(dataObject, type,
                        null);
                if (dataSource == null) {
                    return error("Failed to create data source:" + dataObject
                                 + " " + type);
                }
            } else {
                try {
                    String bundleXml = IOUtil.readContents(bundle);
                    Object obj =
                        getIdv().getEncoderForRead().toObject(bundleXml);
                    if ( !(obj instanceof DataSource)) {
                        return error("datasource bundle is not a DataSource:"
                                     + obj.getClass().getName());
                    }
                    dataSource = (DataSource) obj;
                } catch (Exception exc) {
                    return error("Error loading data source bundle: "
                                 + bundle, exc);
                }
            }
        }


        if (XmlUtil.hasAttribute(node, ATTR_TIMES)) {
            List timesList =
                StringUtil.parseIntegerListString(applyMacros(node,
                    ATTR_TIMES, (String) null));
            dataSource.setDateTimeSelection(timesList);
        }

        if (XmlUtil.hasAttribute(node, ATTR_ENSEMBLES)) {
            List ensList =
                StringUtil.parseIntegerListString(applyMacros(node,
                    ATTR_ENSEMBLES, (String) null));
            if (dataSource instanceof GridDataSource) {
                ((GridDataSource) dataSource).setEnsembleSelection(ensList);
            }
        }

        processGeoSelectionTags(node, dataSource.getDataSelection());


        Hashtable properties = getProperties(node);
        dataSource.setObjectProperties(properties);
        if (id != null) {
            idToDataSource.put(id, dataSource);
        }

        NodeList elements = XmlUtil.getElements(node);
        for (int childIdx = 0; childIdx < elements.getLength(); childIdx++) {
            Element child = (Element) elements.item(childIdx);
            if (child.getTagName().equals(TAG_DISPLAY)) {
                if ( !processDisplayNode(child, dataSource)) {
                    return false;
                }
            }
        }
        //        getIdv().getVMManager().setDisplayMastersActive();
        updateViewManagers();

        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagJoin(Element node) throws Throwable {
        List files = findFiles(node);
        if (files != null) {
            List images = new ArrayList();
            int  cols   = applyMacros(node, ATTR_COLUMNS, 0);
            int  rows   = applyMacros(node, ATTR_ROWS, 0);
            if ((cols == 0) && (rows == 0)) {
                cols = 1;
            }
            if ((cols != 0) && (rows != 0)) {
                cols = 0;
            }
            int colNum = 0;
            int rowNum = 0;
            for (int i = 0; i < files.size(); i++) {
                Image theImage =
                    ImageUtils.readImage(files.get(i).toString());
                if (theImage == null) {
                    continue;
                }
                images.add(theImage);
            }
            if (images.size() > 0) {
                if (cols == 0) {
                    cols = images.size() / rows;
                } else {
                    rows = images.size() / cols;
                }

                int maxWidth  = 0;
                int maxHeight = 0;
                int colCnt    = 0;
                for (int i = 0; i < images.size(); i++) {
                    Image theImage = (Image) images.get(i);
                    int   width    = theImage.getWidth(null);
                    int   height   = theImage.getHeight(null);
                }
            }
        }
        return true;
    }

    /**
     * Process the view tag
     *
     * @param node the element
     *
     * @return  true if it was processed
     *
     * @throws Throwable  problems
     */
    protected boolean processTagView(Element node) throws Throwable {
        List   vms    = getViewManagers(node);

        String width  = applyMacros(node, ATTR_WIDTH, (String) null);
        String height = applyMacros(node, ATTR_HEIGHT, (String) null);
        if ((width != null) && (height != null)) {
            getIdv().getStateManager().setViewSize(
                new Dimension(
                    new Integer(width).intValue(),
                    new Integer(height).intValue()));
        }

        if (vms.size() == 0) {
            StringBuffer properties = new StringBuffer();
            List         nodes      = XmlUtil.findChildren(node,
                                          TAG_PROPERTY);
            for (int childIdx = 0; childIdx < nodes.size(); childIdx++) {
                Element child = (Element) nodes.get(childIdx);
                properties.append(applyMacros(child, ATTR_NAME) + "="
                                  + applyMacros(child, ATTR_VALUE) + ";");
            }
            vms.add(getIdv().getViewManager(ViewDescriptor.LASTACTIVE, false,
                                            properties.toString()));
            return true;
        }

        for (int i = 0; i < vms.size(); i++) {
            ViewManager vm    = (ViewManager) vms.get(i);
     * @param node node
            List        nodes = XmlUtil.findChildren(node, TAG_PROPERTY);
            for (int childIdx = 0; childIdx < nodes.size(); childIdx++) {
                Element child = (Element) nodes.get(childIdx);
                vm.setProperty(applyMacros(child, ATTR_NAME),
                               applyMacros(child, ATTR_VALUE), false);
            }
        }
        return true;
    }


    /**
     * Handle the animation tag. The index attribute can either be a number or be "end"
     *
     * @param node  the node
     *
     * @return  true if successful
     *
     * @throws Throwable  problems
     */
    protected boolean processTagAnimation(Element node) throws Throwable {
        String  indexString = applyMacros(node, ATTR_INDEX, "0");
        int     index       = -1;
        boolean end         = indexString.equals("end");
        boolean step        = indexString.equals("step");
        if ( !end && !step) {
            index = new Integer(indexString).intValue();
        }
        for (ViewManager viewManager : getViewManagers(node)) {
            AnimationWidget animationWidget =
                viewManager.getAnimationWidget();
            if (animationWidget == null) {
                continue;
            }
            if (end) {
                animationWidget.gotoEnd();
            } else if (step) {
    }
                animationWidget.stepForward();
            } else {
                animationWidget.gotoIndex(index);
            }
        }
        return true;
    }



    /**
     * Process the viewpoint tag
     *
     * @param node  the node
     *
     * @return  true if successful
     *
     * @throws Throwable  problems
     */
    protected boolean processTagViewpoint(Element node) throws Throwable {

        List vms = getViewManagers(node);
        if (vms.size() == 0) {
            debug("Could not find view managers processing:"
                  + XmlUtil.toString(node));
        }
        ViewpointInfo viewpointInfo = null;


        for (int i = 0; i < vms.size(); i++) {
            ViewManager vm = (ViewManager) vms.get(i);
            if (XmlUtil.hasAttribute(node, ATTR_VIEWDIR)) {
                vm.setView(XmlUtil.getAttribute(node, ATTR_VIEWDIR));
            }

            if (XmlUtil.hasAttribute(node, ATTR_AZIMUTH)
                    || XmlUtil.hasAttribute(node, ATTR_TILT)) {
                viewpointInfo = new ViewpointInfo(toDouble(node,
                        ATTR_AZIMUTH, 0.0), toDouble(node, ATTR_TILT, 0.0));
                if ( !(vm instanceof MapViewManager)) {
                    continue;
                }
                MapViewManager mvm = (MapViewManager) vm;
                mvm.setViewpointInfo(viewpointInfo);
            }

            if (XmlUtil.hasAttribute(node, ATTR_ASPECTX)
                    || XmlUtil.hasAttribute(node, ATTR_ASPECTY)
                    || XmlUtil.hasAttribute(node, ATTR_ASPECTZ)) {
                double[] a = vm.getMaster().getDisplayAspect();
                a = new double[] { toDouble(node, ATTR_ASPECTX, a[0]),
                                   toDouble(node, ATTR_ASPECTY, a[1]),
                                   toDouble(node, ATTR_ASPECTZ, a[2]) };
                vm.getMaster().setDisplayAspect(a);
                vm.setAspectRatio(a);
            }

            if (XmlUtil.hasAttribute(node, ATTR_ROTX)
                    || XmlUtil.hasAttribute(node, ATTR_ROTY)
                    || XmlUtil.hasAttribute(node, ATTR_ROTZ)
                    || XmlUtil.hasAttribute(node, ATTR_TRANSX)
                    || XmlUtil.hasAttribute(node, ATTR_TRANSY)
                    || XmlUtil.hasAttribute(node, ATTR_TRANSZ)
                    || XmlUtil.hasAttribute(node, ATTR_SCALE)) {
                double[]      a = vm.getMaster().getDisplayAspect();
                double[]      currentMatrix = vm.getDisplayMatrix();
                double[]      trans         = { 0.0, 0.0, 0.0 };
                double[]      rot           = { 0.0, 0.0, 0.0 };
                double[]      scale         = { 0.0, 0.0, 0.0 };

                MouseBehavior mb = vm.getMaster().getMouseBehavior();
                mb.instance_unmake_matrix(rot, scale, trans, currentMatrix);
                double scaleValue = applyMacros(node, ATTR_SCALE, 0.0);
                if (scaleValue != 0) {
                    double scaleX = scaleValue * a[0];
                    double scaleY = scaleValue * a[1];
                    double scaleZ = scaleValue * a[2];
                    double[] scaleMatrix = mb.make_matrix(0.0, 0.0, 0.0,
                                               scaleX / scale[0],
                                               scaleY / scale[1],
                                               scaleZ / scale[2], 0.0, 0.0,
                                               0.0);
                    currentMatrix = mb.multiply_matrix(scaleMatrix,
                            currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }
                double[] tmp;
                double   dummy = 0.0;
                //TODO: the rotation maybe doesn't work
                if (XmlUtil.hasAttribute(node, ATTR_ROTX)) {
                    tmp = mb.make_matrix(applyMacros(node, ATTR_ROTX, dummy),
                                         0.0, 0.0, 1.0, 0.0, 0.0, 0.0);
                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }
                if (XmlUtil.hasAttribute(node, ATTR_ROTY)) {
                    tmp = mb.make_matrix(0.0,
                                         applyMacros(node, ATTR_ROTY, dummy)
                                         - rot[1], 0.0, 1.0, 0.0, 0.0, 0.0);

                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }
                if (XmlUtil.hasAttribute(node, ATTR_ROTZ)) {
                    tmp = mb.make_matrix(0.0, 0.0,
                                         applyMacros(node, ATTR_ROTZ, dummy)
                                         - rot[2], 1.0, 0.0, 0.0, 0.0);

                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }

                if (XmlUtil.hasAttribute(node, ATTR_TRANSX)) {
                    tmp = mb.make_translate(applyMacros(node, ATTR_TRANSX,
                            dummy) - trans[0], 0.0, 0.0);
                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }
                if (XmlUtil.hasAttribute(node, ATTR_TRANSY)) {
                    tmp = mb.make_translate(0.0,
                                            applyMacros(node, ATTR_TRANSY,
                                                dummy) - trans[1], 0.0);
                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                }

                if (XmlUtil.hasAttribute(node, ATTR_TRANSZ)) {
                    tmp = mb.make_translate(0.0, 0.0,
                                            applyMacros(node, ATTR_TRANSZ,
                                                dummy) - trans[2]);
                    currentMatrix = mb.multiply_matrix(tmp, currentMatrix);
                    mb.instance_unmake_matrix(rot, scale, trans,
                            currentMatrix);
                vm.setDisplayMatrix(currentMatrix);

            }
        }
        return true;

    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagCenter(Element node) throws Throwable {
        List vms = getViewManagers(node);
        if (XmlUtil.hasAttribute(node, ATTR_LAT)) {
            getVMManager().center(
                ucar.visad.Util.makeEarthLocation(
                    toDouble(node, ATTR_LAT), toDouble(node, ATTR_LON)), vms);
            return true;
        }


        if (XmlUtil.hasAttribute(node, ATTR_NORTH)) {
            ProjectionRect projRect = new ProjectionRect(toDouble(node,
                                          ATTR_WEST), toDouble(node,
                                              ATTR_NORTH), toDouble(node,
                                                  ATTR_EAST), toDouble(node,
                                                      ATTR_SOUTH));
            getVMManager().center(projRect, vms);
            return true;
        }

        if (XmlUtil.hasAttribute(node, ATTR_DISPLAY)) {
            DisplayControlImpl display = findDisplayControl(node);
            if (display == null) {
                throw new IllegalArgumentException("Could not find display:"
                        + XmlUtil.toString(node));
            }
            if (XmlUtil.getAttribute(node, ATTR_USEPROJECTION, false)) {
                MapProjection mp = display.getDataProjection();
                if (mp != null) {
                    getVMManager().center(mp, vms);
                }
            } else {
                LatLonPoint llp = display.getDisplayCenter();
                if (llp != null) {
                    getVMManager().center(
                        ucar.visad.Util.makeEarthLocation(llp), vms);
                }
            }
            return true;
        }

        getVMManager().center(vms);
        return true;
    }


    /**
     * Find the data source that is identified by the given xml node
     *
     * @param node node
     *
     * @return The data source or null
     */
    private DataSource findDataSource(Element node) {
        String id = XmlUtil.getAttribute(node, ATTR_DATASOURCE);
        return findDataSource(id);
    }

    /**
     * Find the data source with the given id
     *
     * @param id the id we pass to datasource.identifiedByName
     *
     * @return The data source or null if none found
     */
    private DataSource findDataSource(String id) {
        List       dataSources = getIdv().getDataSources();
        DataSource dataSource  = (DataSource) idToDataSource.get(id);
        if (dataSource != null) {
            return dataSource;
        }
        for (int i = 0; i < dataSources.size(); i++) {
            dataSource = (DataSource) dataSources.get(i);
            if (dataSource.identifiedByName(id)) {
                return dataSource;
            }
        }
        return null;
    }




    /**
     * Find the display control that is identified by the given xml node
     *
     *
     * @return The display control source or null
     */
    private DisplayControlImpl findDisplayControl(Element node) {
        String id = XmlUtil.getAttribute(node, ATTR_DISPLAY);
        return findDisplayControl(id);
    }


    /**
     * Find the display control identified by the given id
     *
     * @param id The id of the display control. This can be the id or it can be a 'class:class name'
     *
     * @return The display control or null
     */
    public DisplayControlImpl findDisplayControl(String id) {
        List controls = getIdv().getDisplayControls();
        return findDisplayControl(id, controls);
    }


    /**
     * Find the display control
     *
     * @param id  the control id
     * @param controls  the list of controls
     *
     * @return  the control or null
     */
    public DisplayControlImpl findDisplayControl(String id,
            List controls) {
        for (int i = 0; i < controls.size(); i++) {
            DisplayControlImpl control = (DisplayControlImpl) controls.get(i);

            if (id.startsWith("class:")) {
                if (StringUtil.stringMatch(control.getClass().getName(),
                                           id.substring(6), true, true)) {
                    return control;
                }
            }
            if (control.getId() != null) {
                if (StringUtil.stringMatch(control.getId(), id, true, true)) {
                    return control;
                }
            }
        }
        return null;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagBundle(Element node) throws Throwable {

        List timesList = null;
        List ensList   = null;
        if (XmlUtil.hasAttribute(node, ATTR_TIMES)) {
            timesList = StringUtil.parseIntegerListString(applyMacros(node,
                    ATTR_TIMES, (String) null));
        }
        if (XmlUtil.hasAttribute(node, ATTR_ENSEMBLES)) {
            ensList = StringUtil.parseIntegerListString(applyMacros(node,
                    ATTR_ENSEMBLES, (String) null));
        }


        List nodes    = XmlUtil.findChildren(node, TAG_SETFILES);
        List ids      = new ArrayList();
        List fileList = new ArrayList();
        getPersistenceManager().clearFileMapping();
        for (int i = 0; i < nodes.size(); i++) {
            Element child       = (Element) nodes.get(i);
            String dataSourceId = XmlUtil.getAttribute(child,
                                      ATTR_DATASOURCE);
            ids.add(dataSourceId);
            List files = new ArrayList();
            if (XmlUtil.hasAttribute(child, ATTR_FILE)) {
                String file = applyMacros(child, ATTR_FILE);
                debug("For data source: " + dataSourceId + " Using file: "
                      + file);
                files.add(file);
            } else {
                List filesetFiles = findFiles(child);
                if (filesetFiles != null) {
                    debug("For data source: " + dataSourceId
                          + " Using file: " + filesetFiles);
                    files.addAll(filesetFiles);
                } else {
                    debug("For data source: " + dataSourceId
                          + " Could not find any files");
                }
            }
            fileList.add(files);
            debug("Adding a file override id=" + dataSourceId + " files="
                  + files);
        }
        if (ids.size() > 0) {
            getPersistenceManager().setFileMapping(ids, fileList);
        }


        String width  = applyMacros(node, ATTR_WIDTH, (String) null);
        String height = applyMacros(node, ATTR_HEIGHT, (String) null);
        if ((width != null) && (height != null)) {
            getIdv().getStateManager().setViewSize(
                new Dimension(
                    new Integer(width).intValue(),
                    new Integer(height).intValue()));
        }
        String  bundleFile = applyMacros(node, ATTR_FILE, (String) null);
        boolean doRemove   = applyMacros(node, ATTR_CLEAR, true);
        if (doRemove) {
            //            try {
            cleanup();
            //            } catch(Exception exc) {
            //                System.err.println ("Error cleanup");
            //                System.exit(1);
            //            }
        }
        getIdv().getStateManager().setAlwaysLoadBundlesSynchronously(true);
        Hashtable bundleProperties = new Hashtable();
        if (timesList != null) {
            bundleProperties.put(IdvPersistenceManager.PROP_TIMESLIST,

                                 timesList);
        }
        if (ensList != null) {
            bundleProperties.put(IdvPersistenceManager.PROP_ENSLIST, ensList);
        }


        if (bundleFile != null) {
            debug("Loading bundle: " + bundleFile);
            if (bundleFile.endsWith(".jnlp")) {
                String xml =
                    getPersistenceManager().extractBundleFromJnlp(bundleFile);
                getPersistenceManager().decodeXml(xml, false, bundleFile,
                        null, false, true, bundleProperties, true, false);
            } else if (getArgsManager().isZidvFile(bundleFile)) {
                Hashtable properties = new Hashtable();
                boolean   ask        = getStore().get(PREF_ZIDV_ASK, true);
                getStore().put(PREF_ZIDV_ASK, false);
                getPersistenceManager().decodeXmlFile(bundleFile, "", false,
                        false, properties);
                getStore().put(PREF_ZIDV_ASK, ask);
            } else {
                String xml = IOUtil.readContents(bundleFile);
                xml = applyMacros(xml, null, false);
                getPersistenceManager().decodeXml(xml, false, bundleFile,
                        null, false, true, bundleProperties, true, false);
                //                getPersistenceManager().decodeXmlFile(bundleFile, false,
                //                        timesList);
            }
        } else {
            String b64Bundle = XmlUtil.getChildText(node).trim();
            if (b64Bundle.length() == 0) {
                return error("Could not bundle");
            }
            String xml = new String(XmlUtil.decodeBase64(b64Bundle));
            getPersistenceManager().decodeXml(xml, false, "", null, false,
                    true, bundleProperties, true, false);

        }

        if (applyMacros(node, ATTR_WAIT, true)) {
            getIdv().getIdvUIManager().waitUntilDisplaysAreDone(
                getIdv().getIdvUIManager());
        }
        getPersistenceManager().clearFileMapping();
        Color c            = applyMacros(node, ATTR_COLOR, (Color) null);
        List  viewManagers = getVMManager().getViewManagers();
        for (int i = 0; i < viewManagers.size(); i++) {
            ViewManager viewManager = (ViewManager) viewManagers.get(i);
            if (c != null) {
                viewManager.setColors(null, c);
            }
            viewManager.updateDisplayList();
        }

            }
        //One more pause for the display lists
        updateViewManagers();
        getIdv().getIdvUIManager().waitUntilDisplaysAreDone(
            getIdv().getIdvUIManager());
        return true;
    }

    /**
     * remove data and displays, etc
     */
    private void cleanup() {
        getIdv().removeAllDisplays(false);
        getIdv().removeAllDataSources();
        idToDataSource = new Hashtable();
        ucar.unidata.util.CacheManager.clearCache();

        //        getIdv().getIdvUIManager().disposeAllWindows();
        if (getIdv().getArgsManager().getIsOffScreen()) {
            getIdv().getVMManager().removeAllViewManagers(true);
        }
        getIdv().getIdvUIManager().clearWaitCursor();

        double totalMemory   = (double) Runtime.getRuntime().maxMemory();
        double highWaterMark = (double) Runtime.getRuntime().totalMemory();
        double freeMemory    = (double) Runtime.getRuntime().freeMemory();
        double usedMemory    = (highWaterMark - freeMemory);
        totalMemory   = totalMemory / 1000000.0;
        usedMemory    = usedMemory / 1000000.0;
        highWaterMark = highWaterMark / 1000000.0;

        /*            System.err.println(
                      "MEM:" + ((int) usedMemory) + "/" + ((int) highWaterMark)
                      + " vms:" + getIdv().getVMManager().getViewManagers().size());
        */
    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagCall(Element node) throws Throwable {
        String  name     = applyMacros(node, ATTR_NAME);
        Element procNode = (Element) procs.get(name);
        return processTagCall(node, procNode);
    }

    /**
     * process the given node
     *
     * @param node Node to process
     * @param procNode The procedure node
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagCall(Element node, Element procNode)
            throws Throwable {
        if (procNode == null) {
            return error("Could not find procedure node for call:"
                         + XmlUtil.toString(node));
        }

        pushProperties();
        String cdata = XmlUtil.getChildText(node);
        if ((cdata != null) && (cdata.trim().length() > 0)) {
            putProperty("paramtext", cdata);
        } else {
            putProperty("paramtext", "");
        }

        NamedNodeMap procnnm = procNode.getAttributes();
        if (procnnm != null) {
            for (int i = 0; i < procnnm.getLength(); i++) {
                Attr attr = (Attr) procnnm.item(i);
                if ( !ATTR_NAME.equals(attr.getNodeName())) {
                    putProperty(attr.getNodeName(),
                                applyMacros(attr.getNodeValue()));
                }
            }
        }


        NamedNodeMap nnm = node.getAttributes();
        if (nnm != null) {
            for (int i = 0; i < nnm.getLength(); i++) {
                Attr attr = (Attr) nnm.item(i);
                if ( !ATTR_NAME.equals(attr.getNodeName())) {
                    putProperty(attr.getNodeName(),
                                applyMacros(attr.getNodeValue()));
                }
            }
        }
        try {
            if ( !processChildren(node)) {
                return false;
            try {
                if ( !processChildren(procNode)) {
                    return false;
                }
            } catch (MyReturnException mre) {
                //noop
            }
        } catch (Throwable throwable) {
            popProperties();
            throw throwable;
        }
        popProperties();
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagIf(Element node) throws Throwable {
        String expr = applyMacros(node, ATTR_EXPR, (String) null);
        if (expr == null) {
            expr = applyMacros(XmlUtil.getChildText(node));
        }
        if ((expr == null) || (expr.trim().length() == 0)) {
            return error("Could not find if expression");
        }
        expr = expr.trim();
        boolean result = getInterpreter().eval(expr).toString().equals("1");

        Element thenNode      = XmlUtil.findChild(node, TAG_THEN);
        Element elseNode      = XmlUtil.findChild(node, TAG_ELSE);
                }
        Element statementNode = (result
                                 ? thenNode
                                 : elseNode);
        if (statementNode == null) {
            if (result && (thenNode == null) && (elseNode == null)) {
                statementNode = node;
            } else {
                return true;
            }
        }
        if (statementNode != null) {
            //            pushProperties();
            try {
                if ( !processChildren(statementNode)) {
                    return false;
                }
            } catch (Throwable throwable) {
                //                popProperties();
                throw throwable;
            }
            //            popProperties();
        }
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagOutput(Element node) throws Throwable {
        if ( !XmlUtil.hasAttribute(node, ATTR_FILE)) {
            for (int i = 0; i < outputStack.size(); i++) {
                OutputInfo oi = (OutputInfo) outputStack.get(i);
                oi.process(node);
            }
            return true;
        }
        OutputInfo outputInfo = new OutputInfo(node);
        outputStack.add(outputInfo);
        pushProperties();
        try {
            if ( !processChildren(node)) {
                return false;
            }
        } catch (Throwable throwable) {
            popProperties();
            throw throwable;
        }
        popProperties();
        outputStack.remove(outputStack.size() - 1);
        outputInfo.write();
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagIsl(Element node) throws Throwable {
        debug = applyMacros(node, ATTR_DEBUG, false);
        boolean offScreen = applyMacros(node, ATTR_OFFSCREEN, true);
        //        System.err.println ("offscreen:" + offScreen);
        if ( !getIdv().getArgsManager().getIslInteractive()) {
            //            System.err.println ("setting offscreen:" + offScreen);
            getIdv().getArgsManager().setIsOffScreen(offScreen);
        }
        putProperty(PROP_OFFSCREEN,
                    (getIdv().getArgsManager().getIsOffScreen()
                     ? "1"
                     : "0"));

        //        System.err.println("setting offScreen " +         getIdv().getArgsManager().getIsOffScreen());

        return processTagGroup(node);
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagGroup(Element node) throws Throwable {
        pushProperties();
        int    loopTimes   = applyMacros(node, ATTR_LOOP, 1);
        String sleepString = applyMacros(node, ATTR_SLEEP, (String) null);
        long   sleepTime   = 0;
        if (sleepString != null) {
            sleepString = sleepString.trim();
            long   multiplier = 1000;
            String unit = StringUtil.findPattern(sleepString, "[0-9.]+(.*)$");

            if ((unit != null) && (unit.trim().length() > 0)) {
                sleepString = sleepString.substring(0,
                        sleepString.length() - unit.length());
                if (unit.equals("s")) {}
                else if (unit.equals("seconds")) {}
                else if (unit.equals("minutes")) {
                    multiplier = 60 * 1000;
                } else if (unit.equals("m")) {
                    multiplier = 60 * 1000;
                } else if (unit.equals("hours")) {
                    multiplier = 60 * 60 * 1000;
                } else if (unit.equals("h")) {
                    multiplier = 60 * 60 * 1000;
                } else {
                    return error("Unknown sleep time unit:" + unit);
                }
            }
            sleepTime = (long) (multiplier
                                * new Double(sleepString).doubleValue());
        }
        for (int i = 0; i < loopTimes; i++) {
            currentLoopIndex = i;
            try {
                if ( !processChildren(node)) {
                    return false;
                }
            } catch (MyBreakException be) {
                break;
            } catch (MyContinueException ce) {}
            if ((loopTimes > 1) && (sleepTime > 0)) {
                Misc.sleep(sleepTime);
            }
        }
        popProperties();

        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagForeach(Element node) throws Throwable {
        pushProperties();
        List         allValues   = new ArrayList();
        int          numElements = 0;
        int          cnt         = 1;
        NamedNodeMap attrs       = node.getAttributes();
        if (attrs == null) {
            return error("No values in foreach tag");
        }

        for (int i = 0; i < attrs.getLength(); i++) {
            Attr   attr   = (Attr) attrs.item(i);
    /**
            String var    = attr.getNodeName();
            String values = applyMacros(attr.getNodeValue());
            List   tokens;
            //Check if it starts with file:, if so read the contents and split on new line
            if (values.startsWith("file:")) {
                String filename =
                    applyMacros(values.substring("file:".length()));
                values = IOUtil.readContents(filename, getClass()).trim();
                tokens = StringUtil.split(values, "\n");
            } else {
                tokens = StringUtil.split(values, ",");
            }

            if (allValues.size() == 0) {
                numElements = tokens.size();
            } else if (numElements != tokens.size()) {
                return error("Bad number of tokens (" + tokens.size()
                             + " should be:" + numElements
                             + ") in foreach argument:\n" + var + "="
                             + tokens);
            }
            allValues.add(new Object[] { var, tokens });
            cnt++;
        }
        for (int tokIdx = 0; tokIdx < numElements; tokIdx++) {
            for (int valueIdx = 0; valueIdx < allValues.size(); valueIdx++) {
                Object[] tuple = (Object[]) allValues.get(valueIdx);
                putProperty(tuple[0], ((List) tuple[1]).get(tokIdx));
            }
            try {
                if ( !processChildren(node)) {
                    return false;
            } catch (MyBreakException be) {
                break;
            } catch (MyContinueException ce) {}
        }
        popProperties();
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagMovie(Element node) throws Throwable {
        pushProperties();
        captureMovie(null, node);
        popProperties();
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagHtml(Element node) throws Throwable {

        String html = null;
        if (XmlUtil.hasAttribute(node, ATTR_FROMFILE)) {
            html = IOUtil.readContents(applyMacros(node, ATTR_FROMFILE));
        } else {
            html = XmlUtil.getChildText(node);
        }
        html = applyMacros(html);
        int   width = XmlUtil.getAttribute(node, ATTR_WIDTH, -1);
        Image image = ImageUtils.renderHtml(html, width, null, null);
        image = processImage(ImageUtils.toBufferedImage(image),
                             XmlUtil.getAttribute(node, ATTR_FILE), node,
                             getAllProperties(), null, new Hashtable());

        //        writeImageToFile(image, XmlUtil.getAttribute(node, ATTR_FILE));
        return true;
    }

    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagPanel(Element node) throws Throwable {
        pushProperties();
        captureMovie(null, node);
        popProperties();
        return true;
    }


     * Parse the xml
     *
     * @param xml the xml
     *
     * @return the root
     *
     * @throws Exception On badness
     */
    private Element makeElement(String xml) throws Exception {
        return XmlUtil.getRoot(xml);
    }


    /**
     * Capture a movie and write it out. This is typically called by the jython scripting
     *
     * @param filename Movie filename
     * @param params xml parameters of the the form:  "task arg=val arg2=val; task2 arg3=val"
     *
     * @throws Exception On badness
     */
    public void writeMovie(String filename, String params) throws Exception {
        String isl = makeXmlFromString(params);

        String xml = XmlUtil.getHeader()+"\n"
                     + isl + "";
        captureMovie(applyMacros(filename), makeElement(xml));
    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagImage(Element node) throws Throwable {
        captureImage(XmlUtil.getAttribute(node, ATTR_FILE), node);
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagWait(Element node) throws Throwable {
        File f = null;
        if (XmlUtil.hasAttribute(node, ATTR_FILE)) {
            f = new File(applyMacros(node, ATTR_FILE));
        }
        double seconds = applyMacros(node, ATTR_SECONDS, 60.0);
        if ((f != null) && f.isDirectory()) {
            String patternStr = applyMacros(applyMacros(node, ATTR_PATTERN,
                                    (String) null));
            IOUtil.wait(f, patternStr, seconds);
        } else {
            if (f != null) {
                IOUtil.wait(Misc.newList(f), seconds);
            } else {
                IOUtil.wait(findFiles(node), seconds);
            }
        }
        return true;
    }



    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     *
     * @throws Throwable On badness
     */
    protected boolean processTagPause(Element node) throws Throwable {
        if (XmlUtil.hasAttribute(node, ATTR_EVERY)) {
            Misc.pauseEvery((int) (60 * toDouble(node, ATTR_EVERY)));
            return true;
        }
        if (XmlUtil.hasAttribute(node, ATTR_SECONDS)) {
            Misc.sleep((long) (1000 * toDouble(node, ATTR_SECONDS)));
        } else if (XmlUtil.hasAttribute(node, ATTR_MINUTES)) {
            Misc.sleep((long) (60 * 1000 * toDouble(node, ATTR_MINUTES)));
        } else if (XmlUtil.hasAttribute(node, ATTR_HOURS)) {
            Misc.sleep((long) (60 * 60 * 1000 * toDouble(node, ATTR_HOURS)));
        } else {
            updateViewManagers();
            getIdv().getIdvUIManager().waitUntilDisplaysAreDone(
                getIdv().getIdvUIManager());
        }
        return true;

    }

    /**
     * Update the view managers
     */
    protected void updateViewManagers() {
        try {
            List viewManagers = getVMManager().getViewManagers();
            for (int i = 0; i < viewManagers.size(); i++) {
                ViewManager viewManager = (ViewManager) viewManagers.get(i);
                viewManager.updateDisplayIfNeeded();
            }
        } catch (Exception exc) {
            logException("Updating view manager", exc);
        }
    }

    /**
     * Process tag display properties
     *
     * @param node  the node
     *
     * @return true if successful
     */
    protected boolean processTagDisplayproperties(Element node) {
        DisplayControlImpl display = findDisplayControl(node);
        if (display == null) {
            throw new IllegalArgumentException("Could not find display:"
                    + XmlUtil.toString(node));
        }
        Hashtable properties = getProperties(node);
        display.applyProperties(properties);
        return true;
    }


    /**
     * process the given node
     *
     * @param node Node to process
     *
     * @return keep going
     */

    protected boolean processTagDisplay(Element node) {
        if ( !processDisplayNode(node, null)) {
            return false;
        }
        if (applyMacros(node, ATTR_WAIT, true)) {
            pause();
        }
        updateViewManagers();
        return true;
    }



    /**
     * Process the display node. If data source is not null then use that
     * to find data choices. If null then use all loaded data sources to find
     * data choice.
     *
     * @param node Node to process
     * @param dataSource The data source. May be null.
     *
     * @return keep going
     */
    private boolean processDisplayNode(Element node, DataSource dataSource) {

        //TODO:
        String     type       = applyMacros(node, ATTR_TYPE, (String) null);
        String     param      = applyMacros(node, ATTR_PARAM, (String) null);
        DataChoice dataChoice = null;
        debug("Creating display: " + type + " param:" + param);

        if ((dataSource == null)
                && XmlUtil.hasAttribute(node, ATTR_DATASOURCE)) {
            dataSource = findDataSource(node);
            if (dataSource == null) {
                return error("Failed to to find data source for display tag:"
                             + XmlUtil.toString(node));
            }
        }

        if (param != null) {
            if (dataSource != null) {
                dataChoice = dataSource.findDataChoice(param);
            } else {
                List dataSources = getIdv().getDataSources();
                for (int i = 0;
                        (i < dataSources.size()) && (dataChoice == null);
                        i++) {
                    dataSource = (DataSource) dataSources.get(i);
                    dataChoice = dataSource.findDataChoice(param);
                }
            }
            if (dataChoice == null) {
                return error("Failed to find parameter:" + param);
            }
        }
        List dataChoices = new ArrayList();
        if (dataChoice != null) {
            dataChoice = dataChoice.createClone();
            DataSelection dataSelection =
                new DataSelection(applyMacros(node, ATTR_LEVEL_FROM,
                    (String) null), applyMacros(node, ATTR_LEVEL_TO,
                        (String) null));


            processGeoSelectionTags(node, dataSelection);

            String timeString = applyMacros(node, ATTR_TIMES, (String) null);
            if (timeString != null) {
                List times = new ArrayList();
                for (String tok :
                        StringUtil.split(timeString, ",", true, true)) {
                    times.add(new Integer(tok));
                }
                dataSelection.setTimes(times);
            }
            if (XmlUtil.hasAttribute(node, ATTR_ENSEMBLES)) {
                List ensList =
                    StringUtil.parseIntegerListString(applyMacros(node,
                        ATTR_ENSEMBLES, (String) null));
                dataSelection.putProperty(
                    GridDataSource.PROP_ENSEMBLEMEMBERS, ensList);
            }
            dataChoice.setDataSelection(dataSelection);
            dataChoices.add(dataChoice);
        }

        if (type == null) {
            String bundleXml = null;
            if (XmlUtil.hasAttribute(node, ATTR_TEMPLATE)) {
                String filename = applyMacros(node, ATTR_TEMPLATE);
                try {
                    bundleXml = IOUtil.readContents(filename);

                } catch (IOException exc) {
                    return error("Could not find file: " + filename);
                }
            } else {
                Element templateNode = XmlUtil.findChild(node, TAG_TEMPLATE);
                if (templateNode != null) {
                    bundleXml = XmlUtil.getChildText(templateNode);
                }
            }
            if (bundleXml == null) {
                return error(
                    " tag does not contain type attribute or template attribute/tag");
            }
            try {
                Object obj = getIdv().getEncoderForRead().toObject(bundleXml);

                if ( !(obj instanceof DisplayControl)) {
                    return error("display template is not a DisplayControl:"
                                 + obj.getClass().getName());
                }
                DisplayControl displayControl = (DisplayControl) obj;
                displayControl.initAfterUnPersistence(getIdv(),
                        getProperties(node), dataChoices);
                getIdv().addDisplayControl(displayControl);
            } catch (Exception exc) {
                return error("Creating display", exc);
            }
        } else {
            ControlDescriptor cd = getIdv().getControlDescriptor(type);
            if (cd == null) {
                return error("Failed to find display control:" + type);
            }
            Trace.call1("ImageGenerator making display");
            getIdv().doMakeControl(dataChoices, cd, getProperties(node),
                                   null, false);
            Trace.call2("ImageGenerator making display");
        }
        return true;

    }

    /**
     * Process geo selection tags.
     *
     * @param node the node
     * @param dataSelection the data selection
     * @return true, if successful
     */
    private boolean processGeoSelectionTags(Element node,
                                            DataSelection dataSelection) {
        String strideString = applyMacros(node, ATTR_STRIDE, (String) null);
        if (strideString != null) {
            dataSelection.getGeoSelection(true).setXStride(applyMacros(node,
                    ATTR_STRIDE, 1));
            dataSelection.getGeoSelection(true).setYStride(applyMacros(node,
                    ATTR_STRIDE, 1));

        }


        String strideXString = applyMacros(node, ATTR_STRIDEX, (String) null);
        if (strideXString != null) {
            dataSelection.getGeoSelection(true).setXStride(applyMacros(node,
                    ATTR_STRIDEX, 1));

        }

        String strideYString = applyMacros(node, ATTR_STRIDEY, (String) null);
        if (strideYString != null) {
            dataSelection.getGeoSelection(true).setYStride(applyMacros(node,
                    ATTR_STRIDEY, 1));

        }
        String strideZString = applyMacros(node, ATTR_STRIDEZ, (String) null);
        if (strideZString != null) {
            dataSelection.getGeoSelection(true).setZStride(applyMacros(node,
                    ATTR_STRIDEX, 1));

        }

        String bboxString = applyMacros(node, ATTR_BBOX, (String) null);
        if (bboxString != null) {
            List toks = StringUtil.split(bboxString, ",", true, true);
            if (toks.size() != 4) {
                return error("Bad idv.data.geosubset property:" + bboxString);
            } else {
                GeoLocationInfo boundingBox =
                    new GeoLocationInfo(
                        Misc.decodeLatLon((String) toks.get(0)),
                        Misc.decodeLatLon((String) toks.get(1)),
                        Misc.decodeLatLon((String) toks.get(2)),
                        Misc.decodeLatLon((String) toks.get(3)));
                dataSelection.getGeoSelection(true).setBoundingBox(
                    boundingBox);
            }
        }
        return true;
    }


    /**
     * Process the property tag children of the given node
     *
     * @param node parent node that holds property tags
     *
     * @return properties
     */
    private Hashtable getProperties(Element node) {
        Hashtable properties = new Hashtable();
        List      nodes      = XmlUtil.findChildren(node, TAG_PROPERTY);
        for (int i = 0; i < nodes.size(); i++) {
            Element child = (Element) nodes.get(i);
            properties.put(applyMacros(child, ATTR_NAME),
                           applyMacros(child, ATTR_VALUE));
        }
        return properties;
    }


    /**
     * Utility to print a message and return false.
     *
     * @param msg message
     *
     * @return false
     */
    protected boolean error(String msg) {
        if ( !getIdv().getArgsManager().getIsOffScreen()) {
            LogUtil.userErrorMessage(msg);
        } else {
            System.err.println(msg);
        }
        return false;
    }



    /**
     * Utility to print a message and return false.
     *
     * @param msg message
     * @param exc exception
     *
     * @return false
     */
    protected boolean error(String msg, Exception exc) {
        if ( !getIdv().getArgsManager().getIsOffScreen()) {
            LogUtil.logException(msg, exc);
        } else {
            exc.printStackTrace();
            System.err.println(msg);
        }
        return false;
    }


    /**
     * Find all of the files that are defined by contained fileset nodes.
     *
     * @param parentNode Node to process
     *
     * @return List of files
     */

    private List findFiles(Element parentNode) {
        List resultFiles = null;
        List filesets    = XmlUtil.findChildren(parentNode, TAG_FILESET);
        if (filesets.size() > 0) {
            if (resultFiles == null) {
                resultFiles = new ArrayList();
            }
            resultFiles.addAll(findFiles(filesets));
        }
        if (resultFiles == null) {
            return null;
        }
        return resultFiles;
    }


    /**
     * Find all of the files that are defined by any fileset nodes in the nodes list..
     *
     *
     * @param nodes List of nodes
     *
     * @return List of files
     */
    private List findFiles(List nodes) {
        List files = new ArrayList();
        for (int i = 0; i < nodes.size(); i++) {
            Element node = (Element) nodes.get(i);
            if (node.getTagName().equals(TAG_FILESET)) {
                String filename = applyMacros(node, ATTR_FILE, (String) null);
                if (filename != null) {
                    files.add(new File(filename));
                    continue;
                }
                File dir = new File(applyMacros(node, ATTR_DIR, "."));
                String pattern = applyMacros(applyMacros(node, ATTR_PATTERN,
                                     (String) null));
                File[] allFiles = ((pattern == null)
                                   ? dir.listFiles()
                                   : dir.listFiles(
                                       (java.io
                                           .FileFilter) new PatternFileFilter(
                                               pattern)));
                if (allFiles == null) {
                    continue;
                }
                List tmpFiles = new ArrayList();
                for (int fileIdx = 0; fileIdx < allFiles.length; fileIdx++) {
                    if ( !allFiles[fileIdx].isDirectory()) {
                        if ( !files.contains(allFiles[fileIdx])) {
                            tmpFiles.add(allFiles[fileIdx]);
                        }
                    }
                }

                String sort = applyMacros(node, ATTR_SORT, (String) null);
                String sortDir = applyMacros(node, ATTR_SORTDIR,
                                             VALUE_ASCENDING);
                if (sort != null) {
                    if (sort.equals(VALUE_TIME)) {
                        if (sortDir.equals(VALUE_ASCENDING)) {
                            tmpFiles = Misc.toList(
                                IOUtil.sortFilesOnAge(
                                    IOUtil.toFiles(tmpFiles), false));
                        } else if (sortDir.equals(VALUE_DESCENDING)) {
                            tmpFiles = Misc.toList(
                                IOUtil.sortFilesOnAge(
                                    IOUtil.toFiles(tmpFiles), true));
                        } else {
                            System.err.println("unknown sort direction:"
                                    + sortDir);
                        }
                    } else {
                        System.err.println("unknown sort type:" + sort);
                    }
                }


                if (XmlUtil.hasAttribute(node, ATTR_FIRST)) {
                    int first = applyMacros(node, ATTR_FIRST, 0);
                    if (first < tmpFiles.size()) {
                        List tmp = new ArrayList();
                        for (int fileIdx = 0; fileIdx < first; fileIdx++) {
                            tmp.add(tmpFiles.get(fileIdx));
                        }
                        tmpFiles = tmp;

                    }
                } else if (XmlUtil.hasAttribute(node, ATTR_LAST)) {
                    int last = applyMacros(node, ATTR_LAST, 0);
                    if (last < tmpFiles.size()) {
                        List tmp = new ArrayList();
                        for (int fileIdx = tmpFiles.size() - 1; fileIdx >= 0;
                                fileIdx--) {
                            tmp.add(0, tmpFiles.get(fileIdx));
                            if (tmp.size() >= last) {
                                break;
                            }
                        }
                        tmpFiles = tmp;
                    }
                }
                files.addAll(tmpFiles);


            }
        }
        return files;
    }


    /**
     * Put the property in the current properties hashtable
     *
     * @param key key
     * @param value value
     */
    private void putProperty(Object key, Object value) {
        putProperty(key, value, false);
    }

    /**
     * Put the property in the current properties hashtable
     *
     * @param key key
     * @param value value
     * @param global If true put it in the base stack frame
     */
    private void putProperty(Object key, Object value, boolean global) {

        Hashtable properties = (global
                                ? (Hashtable) propertiesStack.get(0)
                                : getProperties());
        properties.put(key, value);
    }


    /**
     * Find the property table for the given key
     *
     * @param key The key
     *
     * @return The properties table. If none found then it returns the top of the stack
     */
    private Hashtable findTableFor(Object key) {
        for (int i = propertiesStack.size() - 1; i >= 0; i--) {
            Hashtable properties = (Hashtable) propertiesStack.get(i);
            if (properties.get(key) != null) {
                return properties;
            }
        }
        return getProperties();
    }

    /**
     * Find the table that contains the given property and replace it with the new value
     *
     * @param key key
     * @param value new value
     */
    private void replaceProperty(Object key, Object value) {
        findTableFor(key).put(key, value);
    }


    /**
     * Get the top most hashtable in the properties stack.
     *
     * @return Current properties hashtable.
     */
    private Hashtable getProperties() {
        if (propertiesStack.size() == 0) {
            return new Hashtable();
        }
        return (Hashtable) propertiesStack.get(propertiesStack.size() - 1);
    }


    /**
     * Add a Hashtable to the properties stack.
     *
     * @return The newly created hashtable.
     */
    private Hashtable pushProperties() {
        Hashtable properties = new Hashtable();
        propertiesStack.add(properties);
        return properties;
    }


    /**
     * Remove the top most hashtable in the properties stack
     */
    private void popProperties() {
        propertiesStack.remove(propertiesStack.size() - 1);
    }


    /**

     * utility to convert a string to a double. If the string ends with '%'
     * then return the percentage of the given base value
     *
     *
     * @param node Node to process
     * @param attr The attribute to look up
     * @param baseValue Used to handle '%'
     * @return The value
     */
    private double toDouble(Element node, String attr, double baseValue) {
        String s = applyMacros(node, attr);
        return toDouble(s, baseValue);
    }


    /**
     * utility to make a double. If the string begins with '%' then we take a percentage of the baseValue
     *
     * @param s string
     * @param baseValue Used if s is a percentage
     *
     * @return double value
     */
    private double toDouble(String s, double baseValue) {
        if (s.endsWith("%")) {
            double percent = Misc.toDouble(s.substring(0, s.length() - 1));
            return (percent / 100.0) * baseValue;
        }
        return new Double(s).doubleValue();
    }


    /**
     * Convert the attribute value of the given node to a double
     *
     * @param node Node to process
     * @param attr Attribute name
     *
     * @return double value
     */
    private double toDouble(Element node, String attr) {
        return Misc.toDouble(applyMacros(node, attr));
    }


    /**
     * Find the attribute  value of the given node. Apply the macros to it.
     *
     * @param node Node to process
     * @param attr Attribute name
     *
     * @return The value
     */
    public String applyMacros(Element node, String attr) {
        return applyMacros(XmlUtil.getAttribute(node, attr));
    }


    /**
     * If the attribute does not exist return the dflt. Else return the value.
     *
     * @param node Node to process
     * @param attr Attribute name
     * @param dflt The default value to use if the attribute does not exist
     *
     * @return The value
     */
    public String applyMacros(Element node, String attr, String dflt) {
        return applyMacros(XmlUtil.getAttribute(node, attr, dflt));
    }


    /**
     * If the attribute does not exist return the dflt. Else return the value.
     *
     * @param node Node to process
     * @param attr Attribute name
     * @param dflt The default value to use if the attribute does not exist
     *
     * @return The value
     */
    public int applyMacros(Element node, String attr, int dflt) {
        String value = XmlUtil.getAttribute(node, attr, (String) null);
        if (value == null) {
            return dflt;
        }
        return (int) Misc.toDouble(applyMacros(value));
    }

    /**
     * If the attribute does not exist return the dflt. Else return the value.
     *
     * @param node Node to process
     * @param attr Attribute name
     * @param dflt The default value to use if the attribute does not exist
     *
     * @return The value
     */
    public boolean applyMacros(Element node, String attr, boolean dflt) {
        String value = XmlUtil.getAttribute(node, attr, (String) null);
        if (value == null) {
            return dflt;
        }
        return new Boolean(applyMacros(value)).booleanValue();
    }


    /**
     * If the attribute does not exist return the dflt. Else return the value.
     *
     * @param node Node to process
     * @param attr Attribute name
     * @param dflt The default value to use if the attribute does not exist
     *
     * @return The value
     */
    public Color applyMacros(Element node, String attr, Color dflt) {
        String value = XmlUtil.getAttribute(node, attr, (String) null);
        if (value == null) {
            return dflt;
        }
        String result = applyMacros(value);
        if (result.equals("none")) {
            return null;
        }
        return GuiUtils.decodeColor(result, dflt);
    }


    /**
     * If the attribute does not exist return the dflt. Else return the value.
     *
     * @param node Node to process
     * @param attr Attribute name
     * @param dflt The default value to use if the attribute does not exist
     *
     * @return The value
     */
    public double applyMacros(Element node, String attr, double dflt) {
        String value = XmlUtil.getAttribute(node, attr, (String) null);
        if (value == null) {
            return dflt;
        }
        return Misc.toDouble(applyMacros(value));
    }



    /**
     * Do the macro substitution
     *
     * @param s The string
     *
     * @return The expanded string
     */
    public String applyMacros(String s) {
        return applyMacros(s, null);
    }

    /**
     * Merge all of the proeprties together
     *
     * @return The properties
     */
    private Hashtable getAllProperties() {
        Hashtable props = new Hashtable();
        for (int i = 0; i < propertiesStack.size(); i++) {
            Hashtable properties = (Hashtable) propertiesStack.get(i);
            props.putAll(properties);
        }
        return props;
    }


    /**
     * Do the macro substitution
     *
     * @param s The string
     * @param props Properties
     *
     * @return The expanded string
     */
    private String applyMacros(String s, Hashtable props) {
        return applyMacros(s, props, true);
    }

    /**
     * Do the macro substitution
     *
     * @param s The string
     * @param props Properties
     * @param doTime  process time macros
     *
     * @return The expanded string
     */
    private String applyMacros(String s, Hashtable props, boolean doTime) {
        if (s == null) {
            return null;
        }
        if (props == null) {
            props = new Hashtable();
        } else {
            Hashtable tmp = props;
            props = new Hashtable();
            props.putAll(tmp);
        }
        props.putAll(getAllProperties());

        putIndex(props, PROP_LOOPINDEX, currentLoopIndex);
        props.put(PROP_LOOPINDEX_PAD2,
                  StringUtil.padLeft("" + currentLoopIndex, 2, "0"));
        props.put(PROP_LOOPINDEX_PAD3,
                  StringUtil.padLeft("" + currentLoopIndex, 3, "0"));
        props.put(PROP_LOOPINDEX_PAD4,
                  StringUtil.padLeft("" + currentLoopIndex, 4, "0"));

        Date now = new Date(Misc.getCurrentTime());


        if (DATE_FORMATS == null) {
            TimeZone timeZone = TimeZone.getTimeZone("GMT");
            DATE_FORMATS = new ArrayList();
            for (int i = 0; i < DATE_PROPS.length; i++) {
                SimpleDateFormat sdf = new SimpleDateFormat(DATE_PROPS[i]);
                sdf.setTimeZone(timeZone);
                DATE_FORMATS.add(sdf);
            }
        }

        for (int i = 0; i < DATE_FORMATS.size(); i++) {
            SimpleDateFormat sdf = (SimpleDateFormat) DATE_FORMATS.get(i);
            props.put(DATE_PROPS[i], sdf.format(now));
        }


        props.put("memory", "" + Misc.usedMemory());


        /*
        if (s.indexOf("${anim:") >= 0) {
            now = getAnimationTime();
            if (now != null) {
                for (int i = 0; i < DATE_FORMATS.size(); i++) {
                    SimpleDateFormat sdf =
                        (SimpleDateFormat) DATE_FORMATS.get(i);
                    props.put("anim:" + DATE_PROPS[i], sdf.format(now));
                }

            }
            }*/

        TimeZone tz = getIdv().getPreferenceManager().getDefaultTimeZone();


        s = StringUtil.replaceDate(s, "now:", now, tz);

        if ((s.indexOf("anim:") >= 0) || (s.indexOf("time:") >= 0)) {
            Date animationTime = getAnimationTime();
            if (animationTime == null) {
                animationTime = now;
            }
            if (doTime) {
                s = StringUtil.replaceDate(s, "anim:", animationTime, tz);
                s = StringUtil.replaceDate(s, "time:", animationTime, tz);
                s = StringUtil.replaceDate(s, "now:", now, tz);
            }
        }
        s = StringUtil.applyMacros(s, props, false);
        //Now use the idv properties
        s = StringUtil.applyMacros(s, getStateManager().getProperties(),
                                   false);
        if (s.indexOf("${") >= 0) {
            throw new BadIslException("Undefined macro in: " + s);
        }

        if (s.startsWith("jython:")) {
            Object result = getInterpreter().eval(s.substring(7));
            s = result.toString();
        }

        if (s.startsWith("interp.")) {
            Object result = getInterpreter().eval(s);
            s = result.toString();
        }
        if (s.startsWith("islInterpreter.")) {
            Object result = getInterpreter().eval(s);
            s = result.toString();
        }
        s = s.replace("\\n", "\n");
        return s;
    }




    /**
     * Capture an image from the first active view managers
     *
     * @param filename The image filename
     */
    public void captureImage(String filename) {
        try {
            captureImage(filename, null);
        } catch (Throwable exc) {
            logException("Capturing image", exc);
        }
    }


    /**
     * Put the index
     *
     * @param props  the properties
     * @param name the name
     * @param v  the index
     */
    public void putIndex(Hashtable props, String name, int v) {
     *
        props.put(name, new Integer(v));
        props.put(name + "_alpha", getLetter(v).toLowerCase());
        props.put(name + "_ALPHA", getLetter(v).toUpperCase());
        
        props.put(name + "_ROMAN", getRoman(v).toUpperCase());
        props.put(name + "_roman", getRoman(v).toLowerCase());


    }


    /**
     * Find all view managers that are identified by the given xml node. If the node
     * does not have a "view" attribute return all view managers. Else use the view
     * attribute to find the vms. The view can be class:class name or just a name. If a name can
     * also be a regular expression
     *
     * @param node node
     *
     * @return List of view managers
     */
    private List getViewManagers(Element node) {
        List viewManagers =
            (List) getVMManager().getViewManagers();
        if ((node == null) || !XmlUtil.hasAttribute(node, ATTR_VIEW)) {
            return viewManagers;
        }

        List   goodOnes = new ArrayList();
        String viewId   = applyMacros(node, ATTR_VIEW);
        //        System.err.println ("viewManagers:" + viewManagers);
        if (viewId.startsWith("name:")) {
            viewId = viewId.substring(5);
        }

        for (int i = 0; i < viewManagers.size(); i++) {
            ViewManager viewManager = (ViewManager) viewManagers.get(i);
            if (viewId.startsWith("#")) {
                int viewIndex = new Integer(viewId.substring(1)).intValue();
                if (viewIndex == i) {
                    goodOnes.add(viewManager);
                    //                    System.err.println("\tskipping index");
                }
                continue;
            }
            if (viewId.startsWith("class:")) {
                if (StringUtil.stringMatch(viewManager.getClass().getName(),
                                           viewId.substring(6), true, true)) {
                    goodOnes.add(viewManager);
                }
                continue;
            }
            String name = viewManager.getName();
            if (name == null) {
                name = "";
            }
            if (StringUtil.stringMatch(name, viewId, true, true)) {
                goodOnes.add(viewManager);
            }
        }
        if (goodOnes.size() == 0) {
            warning("Unable to find any views with id:" + viewId);
        } else {
            //            System.err.println(viewId + " " + goodOnes);
        }
        return goodOnes;
    }


    /**
     * Wait until all displays are built
     */
    public void pause() {
        getIdv().waitUntilDisplaysAreDone();
    }


    /**
     * Toggle debug
     *
     * @param v debug
     */
    public void setDebug(boolean v) {
        debug = v;
    }


    /**
     * Evaluate the given isl
     *
     * @param isl The isl
     *
     * @return success
     *
     * @throws Throwable On badness
     */
    public boolean evaluateIsl(String isl) throws Throwable {
        isl = XmlUtil.tag(TAG_GROUP, "", isl);
        return processNode(XmlUtil.getRoot(isl));
    }



    /**
     * Load the given bundle file
     * @param bundleFile The bundle
     * @param setFiles This is a list, which may be null, of datasource patterns and file names to change
     *
     * @throws Throwable     On badness
     */
    public void loadBundle(String bundleFile, List setFiles)
            throws Throwable {
        loadBundle(bundleFile, setFiles, -1, -1, "", true);
    }

    /**
     * Load the given bundle file, list of datasets, width and height
     *
     * @param bundleFile The bundle
     * @param setFiles This is a list, which may be null, of datasource patterns and file names to change
     * @param width The width of the display area to use
     * @param height The height of the display are to use
     *
     * @throws Throwable  an exception
     */
    public void loadBundle(String bundleFile, List setFiles, int width,
                           int height)
            throws Throwable {
        loadBundle(bundleFile, setFiles, width, height, "", true);
    }


    /**
     * Load the given bundle file, list of datasets, width and height
     *
     * @param bundleFile The bundle
     * @param setFiles This is a list, which may be null, of datasource patterns and file names to change
     * @param width The width of the display area to use
     * @param height The height of the display are to use

     * @param times A string of times to use from the bundle file
     * @param clear If false then do not clear out the data sources and displays (which is otherwise the default)
     *
     * @throws Throwable  an exception
     */
    public void loadBundle(String bundleFile, List setFiles, int width,
                           int height, String times, boolean clear)
            throws Throwable {

        StringBuffer extra = new StringBuffer();
        if (setFiles != null) {
            for (int i = 0; i < setFiles.size(); i += 2) {
                String datasource = (String) setFiles.get(i);
                String files      = (String) setFiles.get(i + 1);
                if ((datasource != null) && (files != null)) {
                    extra.append(XmlUtil.tag(TAG_SETFILES,
                                             XmlUtil.attrs("datasource",
                                                 datasource, "file", files)));
                }
                extra.append("\n");
            }
        }
        StringBuffer attrs = new StringBuffer();
        attrs.append(" ");
        attrs.append(ATTR_FILE + "=" + quote(bundleFile));
        attrs.append(" ");
        if ((width > 0) && (height > 0)) {
            attrs.append(" ");
            attrs.append(ATTR_WIDTH + "=" + quote("" + width));
            attrs.append(" ");
            attrs.append(ATTR_HEIGHT + "=" + quote("" + height));
            attrs.append(" ");
        }
        if ((times != null) && !times.isEmpty()) {
            attrs.append(" ");
            attrs.append(ATTR_TIMES + "=" + quote("" + times));
        }
        if ( !clear) {
            attrs.append(" ");
            attrs.append(ATTR_CLEAR + "=" + quote("false"));
        }

        String xml = "" + extra + "";
        System.err.println(xml);
        processTagBundle(makeElement(xml));
    }

    /**
     * Write an Image to the specified file
     *
     * @param image Image to be written
     * @param file Name of output file (may use macros)
     *
     * @throws Exception On badness
     */
    public void writeImageToFile(Image image, String file) throws Exception {
        ImageUtils.writeImageToFile(image,
                                    applyMacros(getImageFileName(file)));
    }


    /**
     * Create XML from the input String
     *
     * @param s in the form:  "task arg=val arg2=val; task2 arg3=val"
     *
     * @return  
     *
     */
    protected static String makeXmlFromString(String s) {
        if ((s == null) || (s.length() == 0)) {
            return "";
        }
        StringTokenizer st = new StringTokenizer(s, ";");
        StringBuffer    sb = new StringBuffer();

        while (st.hasMoreTokens()) {
            String          so  = st.nextToken();
            StringTokenizer sot = new StringTokenizer(so, "=");
            int             k   = sot.countTokens();

            for (int i = 0; i < k; i++) {

                StringTokenizer sbt =
                    new StringTokenizer(sot.nextToken().trim(), " ");

                if (i == 0) {
                    sb.append("<" + sbt.nextToken().trim());
                }

                int     n      = sbt.countTokens();
                boolean gotone = false;
                while (n > 1) {
                    if (gotone) {
                        sb.append(" ");
                    }
                    sb.append(sbt.nextToken().trim());
                    n      = n - 1;
                    gotone = true;
                }

                // now deal with the last value
                if (sbt.hasMoreTokens()) {
                    if (i != k - 1) {
                        if (gotone) {
                            sb.append("\" " + sbt.nextToken().trim() + "=\"");
                        } else {
                            sb.append(" " + sbt.nextToken().trim() + "=\"");
                        }
                    } else {
                        if (gotone) {
                            sb.append(" " + sbt.nextToken().trim() + "\"");
                        } else {
                            sb.append(sbt.nextToken().trim() + "\"");
                        }
                    }
                }
            }

            sb.append(" />");
        }

        return sb.toString();
    }

    /**
     * Quote a string
     *
     * @param s  the string
     *
     * @return  the quotated string
     */
    private static String quote(String s) {
        return "\"" + s + "\"";
    }

    /**
     * Get the image of the current display and write to file. Image
     * may be modified by the params given in the form:
     *    tag1 arg=val arg2=val2; tag2 arg=val
     * where 'tag' are ISL tags.
     *
     * @param filename Output filename (may be modified by macros)
     * @param params String of parameters
     * @param qual Quality (def=1.0)
     *
     *
     * @throws Exception On badness
     * @throws Throwable On badness
     */
    public void writeImage(String filename, String params, float qual)
            throws Exception, Throwable {
        String isl = makeXmlFromString(params);
        String xml = XmlUtil.getHeader()+"\n" + isl + "";
        captureImage(applyMacros(filename), makeElement(xml));
    }


    /**
     * Get the Image of the current display
     *
     * @return the Image
     *
     * @throws Exception On badness
     */
    public Image getImage() throws Exception {
        //        updateViewManagers();
        List viewManagers = getVMManager().getViewManagers();
        for (int i = 0; i < viewManagers.size(); i++) {
            ViewManager viewManager = (ViewManager) viewManagers.get(i);
            if ( !getIdv().getArgsManager().getIsOffScreen()) {
                IdvWindow window = viewManager.getDisplayWindow();
                if (window != null) {
                    window.setLocation(50, 50);
                    viewManager.toFront();
                }
            }
            return viewManager.getMaster().getImage(false);
        }
        return null;
    }


    /**
     * Capture the image
     *
     * @param filename file
     * @param scriptingNode THe node from the isl. Possibly null.
     *
     * @throws Throwable On badness
     */
    private void captureImage(String filename, Element scriptingNode)
            throws Throwable {

        Hashtable imageProperties = new Hashtable();

        //See if we're in test mode
        if ((scriptingNode != null)
                && XmlUtil.hasAttribute(scriptingNode, "test")) {
            BufferedImage tmpImage =
                new BufferedImage(applyMacros(scriptingNode, ATTR_WIDTH,
                    300), applyMacros(scriptingNode, ATTR_HEIGHT, 300),
                          BufferedImage.TYPE_INT_RGB);
            String loopFilename = applyMacros(filename);
            lastImage = processImage((BufferedImage) tmpImage, loopFilename,
                                     scriptingNode, getAllProperties(), null,
                                     imageProperties);
            return;
        }


        List viewManagers = null;
        if ((scriptingNode != null)
                && XmlUtil.hasAttribute(scriptingNode, ATTR_DISPLAY)) {
            DisplayControlImpl display = findDisplayControl(scriptingNode);
            if (display == null) {
                throw new IllegalArgumentException("Could not find display:"
                        + XmlUtil.toString(scriptingNode));
            }
            String loopFilename = applyMacros(filename);
            String what = applyMacros(scriptingNode, ATTR_WHAT,
                                      (String) null);

            ViewManager viewManager = display.getViewManagerForCapture(what);
            if (viewManager != null) {
                viewManager.updateDisplayIfNeeded();
                viewManagers = (List) Misc.newList(viewManager);
            } else {
                lastImage = display.getImage(what);
                lastImage = processImage((BufferedImage) lastImage,
                                         loopFilename, scriptingNode,
                                         getAllProperties(), null,
                                         imageProperties);
                return;
            }
        }

        if (viewManagers == null) {
            viewManagers = (List) getViewManagers(scriptingNode);
        }

        if (viewManagers.size() == 0) {
            debug("No views to capture");
        }
        pushProperties();

        List indices = StringUtil.parseIntegerListString(
                                    XmlUtil.getAttribute(
                                        scriptingNode, ATTR_ANIMATION_INDEX,
                                        "1"));

        int idx = 0;
        for (int j = 0; j < indices.size(); j++) {
            List images = new ArrayList();
            String      fname  = (indices.size() > 1)
                                 ? fixFileName(filename, indices.get(j))
                                 : filename;
            for (int i = 0; i < viewManagers.size(); i++) {
                ViewManager viewManager = (ViewManager) viewManagers.get(i);
                if (viewManager.getAnimation() != null)
                   viewManager.getAnimation().setCurrent(indices.get(j));
                putIndex(getProperties(), PROP_VIEWINDEX, idx);
                String name = viewManager.getName();
                if (name == null) {
                    name = "view" + idx;
                }
                getProperties().put(PROP_VIEWNAME, name);
                if ( !getIdv().getArgsManager().getIsOffScreen()) {
                    IdvWindow window = viewManager.getDisplayWindow();
                    if (window != null) {
                        window.setLocation(50, 50);
                        viewManager.toFront();
                        //                    Misc.sleep(100);
                    }
                }
                String loopFilename = applyMacros(fname);
                if (scriptingNode == null) {
                    File imageFile = null;
                    if (loopFilename != null) {
                        imageFile = new File(getImageFileName(loopFilename));
                    }
                    viewManager.writeImage(imageFile, true, false);
                } else if ((loopFilename != null)
                           && ViewManager.isVectorGraphicsFile(
                               loopFilename)) {
                    VectorGraphicsRenderer vectorRenderer =
                        new VectorGraphicsRenderer(viewManager);
                    vectorRenderer.renderTo(loopFilename);
                } else {
                    getIdv().getIdvUIManager().waitUntilDisplaysAreDone(
                        getIdv().getIdvUIManager(), 0);
                    lastImage       = viewManager.getMaster().getImage(false);
                    imageProperties = new Hashtable();
                    lastImage = processImage((BufferedImage) lastImage,
                                             loopFilename, scriptingNode,
                                             getAllProperties(), viewManager,
                                             imageProperties);
                }
                images.add(lastImage);
                idx++;
            }

            boolean combine = XmlUtil.getAttribute(scriptingNode,
                                  ATTR_COMBINE, false);
            if (combine) {
                String          combineFilename = applyMacros(filename);
                List components      = new LinkedList();
                for (ViewManager vm : viewManagers) {
                    components.add(vm.getComponent());
                }
                int cols = 2;
                if ( !getIdv().getArgsManager().getIsOffScreen()) {
                    cols = ImageUtils.getColumnCountFromComps(components);
                } else {
                    cols = XmlUtil.getAttribute(scriptingNode, ATTR_COLUMNS,
                            cols);
                }

                if (ViewManager.isVectorGraphicsFile(combineFilename)) {
                    VectorGraphicsRenderer vectorRenderer =
                        new VectorGraphicsRenderer(viewManagers, cols);
                    vectorRenderer.renderTo(combineFilename);
                } else {
                    Image i = ImageUtils.gridImages2(images, 0, Color.GRAY,
                                  cols);
                    ImageUtils.writeImageToFile(i, combineFilename);
                }
            }
        }

        popProperties();
    }

    /**
     * Fixing file name for animation indices.
     *
     * @param filename
     * @param integer
     *
     * @return The file name with an appended index.
     */
    private String fixFileName(String filename, Integer integer) {
        String[] tokens = filename.split("\\.(?=[^\\.]+$)");
        return tokens[0] + integer + "." + tokens[1];
    }

    /**
     * Resize the image
     *
     * @param image The image
     * @param node Node to process. This may contain a width or a height attribute.
     *
     * @return The resized image
     */
    protected Image resize(Image image, Element node) {
        int imageWidth  = image.getWidth(null);
        int imageHeight = image.getHeight(null);
        int width       = -1;
        int height      = -1;
        if (XmlUtil.hasAttribute(node, ATTR_WIDTH)) {
            width = (int) toDouble(node, ATTR_WIDTH, imageWidth);
        }
        if (XmlUtil.hasAttribute(node, ATTR_HEIGHT)) {
            height = (int) toDouble(node, ATTR_HEIGHT, imageWidth);
        }
        if ((width == -1) && (height == -1)) {
            return image;
        }

        return image.getScaledInstance(width, height,
                                       Image.SCALE_AREA_AVERAGING);
    }




    /**
     * Resize the image
     *
     * @param image The image
     * @param widthStr width of desired image (pixels)
     * @param heightStr height of desired image (pixels)
     *
     * @return The resized image
     */
    public BufferedImage resizeImage(BufferedImage image, String widthStr,
                                     String heightStr) {
        int imageWidth  = image.getWidth(null);
        int imageHeight = image.getHeight(null);
        int width       = -1;
        int height      = -1;
        if ( !widthStr.equals("-1")) {
            width = (int) toDouble(widthStr, imageWidth);
        }
        if ( !heightStr.equals("-1")) {
            height = (int) toDouble(heightStr, imageHeight);
        }
        if ((width == -1) && (height == -1)) {
            return image;
        }

        BufferedImage resizedImage =
            ImageUtils.toBufferedImage(image.getScaledInstance(width, height,
                Image.SCALE_AREA_AVERAGING), BufferedImage.TYPE_INT_RGB);
        return resizedImage;

    }



    /**
     * Matte the image
     *
     * @param image The image
     * @param bgString color for the matte ("red", "green", etc)
     * @param top number of lines for the top (north) matte
     * @param left number of pixels for the left (west) matte
     * @param bottom number of lines for the bottom (south) matte
     * @param right number of pixels for the right (east) matte
     *
     * @return The matte'd image
     */
    public BufferedImage matteImage(BufferedImage image, String bgString,
                                    int top, int left, int bottom,
                                    int right) {
        Color bg = GuiUtils.decodeColor(bgString, (Color) null);
        return ImageUtils.matte(image, top, bottom, left, right, bg);
    }



    /**
     * Process the image
     *
     * @param image The image
     * @param filename File to write the image to
     * @param node Node to process
     * @param props Extra properties
     * @param viewManager The viewmanager this image came from
     * @param imageProps  the image properties
     *
     *
     * @return The processed image
     * @throws Throwable On badness
     */
    protected BufferedImage processImage(BufferedImage image,
                                         String filename, Element node,

                                         Hashtable props,
                                         ViewManager viewManager,
                                         Hashtable imageProps)
            throws Throwable {

        if (node == null) {
            return image;
        }

        if (props == null) {
            props = new Hashtable();
        }
        if (viewManager != null) {
            Animation animation = viewManager.getAnimation();
            props.put(PROP_ANIMATIONTIME, "");
            if (animation != null) {
                if (animation.getAniValue() != null) {
                    props.put(PROP_ANIMATIONTIME, animation.getAniValue());
                }
            }
        }
        getProperties().putAll(props);

        NodeList  elements       = XmlUtil.getElements(node);
        Hashtable seenColorTable = new Hashtable();
        for (int childIdx = 0; childIdx < elements.getLength(); childIdx++) {
            boolean       shouldIterateChildren = true;
            BufferedImage newImage              = null;
            int           imageWidth            = image.getWidth(null);
            int           imageHeight           = image.getHeight(null);
            Element       child = (Element) elements.item(childIdx);
            String        tagName               = child.getTagName();

            if (tagName.equals(TAG_RESIZE)) {
                newImage = ImageUtils.toBufferedImage(resize(image, child));
            } else if (tagName.equals(TAG_FILESET)) {
                //ignore
            } else if (tagName.equals(TAG_OUTPUT)) {
                processTagOutput(child);
            } else if (tagName.equals(TAG_DISPLAYLIST)) {
                if (viewManager != null) {
                    newImage = ImageUtils.toBufferedImage(image, true);
                    Graphics g = newImage.getGraphics();
                    String valign = applyMacros(child, ATTR_VALIGN,
                                        VALUE_BOTTOM);
                    Font font = getFont(child);
                    if (XmlUtil.hasAttribute(child, ATTR_MATTEBG)) {
                        int height =
                            viewManager.paintDisplayList((Graphics2D) g,
                                null, imageWidth, imageHeight,
                                valign.equals(VALUE_BOTTOM), null, font);

                        int top    = (valign.equals(VALUE_TOP)
                                      ? height
                                      : 0);
                        int bottom = (valign.equals(VALUE_BOTTOM)
                                      ? height
                                      : 0);
                        newImage = ImageUtils.matte(image, top, bottom, 0, 0,
                                applyMacros(child, ATTR_MATTEBG,
                                            Color.white));
                        g           = newImage.getGraphics();
                        imageHeight += height;
                    }

                    Color c = applyMacros(child, ATTR_COLOR, (Color) null);
                    viewManager.paintDisplayList((Graphics2D) g, null,
                            imageWidth, imageHeight,
                            valign.equals(VALUE_BOTTOM), c, font);
                }
            } else if (tagName.equals(TAG_COLORBAR)
                       || tagName.equals(TAG_KML_COLORBAR)) {
                // only do one colorbar if we are writing to kml
                Integer index = (Integer) props.get(PROP_IMAGEINDEX);
                if ((index != null) && (index.intValue() > 0)
                        && tagName.equals(TAG_KML_COLORBAR)) {
                    continue;
                }

                boolean showLines = applyMacros(child, ATTR_SHOWLINES, false);

                List controls =
                    (List) ((viewManager != null)
                        ? viewManager.getControls()
                        : new ArrayList());

                if (XmlUtil.hasAttribute(child, ATTR_DISPLAY)) {
                    DisplayControlImpl display = ((controls.size() > 0)
                            ? findDisplayControl(XmlUtil.getAttribute(child,
                                ATTR_DISPLAY), controls)
                            : findDisplayControl(child));
                    if (display == null) {
                        error("Could not find display:"
                              + XmlUtil.toString(node));
                        return null;
                    }
                    controls = Misc.newList(display);
                }

                int    width    = applyMacros(child, ATTR_WIDTH, 150);
                int    height   = applyMacros(child, ATTR_HEIGHT, 20);
                int    ticks    = applyMacros(child, ATTR_TICKMARKS, 0);
                double interval = applyMacros(child, ATTR_INTERVAL, -1.0);
                String valuesStr = applyMacros(child, ATTR_VALUES,
                                       (String) null);
                Color c = applyMacros(child, ATTR_COLOR, Color.black);

                Color lineColor = applyMacros(child, ATTR_LINECOLOR, c);

                Rectangle imageRect = new Rectangle(0, 0, imageWidth,
                                          imageHeight);

                Point pp = ImageUtils.parsePoint(applyMacros(child,
                               ATTR_PLACE, "ll,10,-10"), imageRect);
                Point ap = ImageUtils.parsePoint(applyMacros(child,

                               ATTR_ANCHOR, "ll"), new Rectangle(0, 0, width,
                                   height));

                String orientation = applyMacros(child, ATTR_ORIENTATION,
                                         VALUE_BOTTOM);
                boolean vertical = orientation.equals(VALUE_RIGHT)
                                   || orientation.equals(VALUE_LEFT);
                int     baseY       = pp.y - ap.y + (vertical
                        ? 0
                        : height);
                int     baseX       = pp.x - ap.x;

                List    colorTables = new ArrayList();
                List    ranges      = new ArrayList();
                List    units       = new ArrayList();

                boolean forKml      = tagName.equals(TAG_KML_COLORBAR);

                for (int i = 0; i < controls.size(); i++) {
                    DisplayControlImpl control =
                        (DisplayControlImpl) controls.get(i);
                    ColorTable colorTable = control.getColorTable();
                    if (colorTable == null) {
                        continue;
                    }
                    Range range = control.getRangeForColorTable();
                    //only do unique color tables
                    Object[] key = { colorTable, range };
                    if (seenColorTable.get(key) != null) {
                        continue;
                    }
                    seenColorTable.put(key, key);
                    colorTables.add(colorTable);
                    ranges.add(range);
                    units.add(control.getDisplayUnit());
                }

                for (int i = 0; i < colorTables.size(); i++) {
                    ColorTable colorTable = (ColorTable) colorTables.get(i);
                    Range      range      = (Range) ranges.get(i);
                    Unit       unit       = (Unit) units.get(i);
                    Image      imageToDrawIn;
                    if (forKml) {
                        if (vertical) {
                            baseX = 0;
                            baseY = 0;
                        } else {
                            baseX = 0;
                            baseY = height;
                        }
                        int space = applyMacros(child, ATTR_SPACE, (vertical
                                ? width
                                : height));
                        imageToDrawIn = new BufferedImage(width + (vertical
                                ? space
                                : 0), height + (vertical
                                ? 0
                                : space), BufferedImage.TYPE_INT_RGB);
                    } else {
                        imageToDrawIn = newImage =
                            ImageUtils.toBufferedImage(image);
                    }
                    Graphics g = imageToDrawIn.getGraphics();
                    if (forKml) {
                        Color bgColor = applyMacros(child, ATTR_BACKGROUND,
                                            Color.white);
                        g.setColor(bgColor);
                        g.fillRect(0, 0, imageToDrawIn.getWidth(null),
                                   imageToDrawIn.getHeight(null));
                    }
                    ColorPreview preview =
                        new ColorPreview(
                            new BaseRGBMap(colorTable.getNonAlphaTable()),
                            (vertical
                             ? width
                             : height));
                    if (vertical) {
                        preview.setSize(new Dimension(height, width));
                    } else {
                        preview.setSize(new Dimension(width, height));
                    }
                    Image previewImage = GuiUtils.getImage(preview);
                    boolean includeAlpha = applyMacros(child, ATTR_TRANSPARENCY,
                                               true);
                    previewImage = ColorTableCanvas.getImage(colorTable,
                            (vertical
                             ? height
                             : width), (vertical
                                        ? width
                                        : height), includeAlpha);


                    if (vertical) {
                        BufferedImage tmpImage =
                            new BufferedImage(width, height,
                                BufferedImage.TYPE_INT_RGB);

                        BufferedImage tmpImagexxx =
                            new BufferedImage(500, 500,
                                BufferedImage.TYPE_INT_RGB);
                        Graphics2D tmpG = (Graphics2D) tmpImage.getGraphics();
                        tmpG.setColor(Color.red);
                        tmpG.fillRect(0, 0, 1000, 1000);
                        tmpG.rotate(Math.toRadians(90.0));
                        tmpG.drawImage(previewImage, 0, 0 - width, null);
                        previewImage = tmpImage;
                    }
                    if (forKml) {
                        g.drawImage(previewImage, 0, 0, null);
                    } else {
                        g.drawImage(previewImage, baseX, (vertical
                                ? baseY
                                : baseY - height), null);
                    }
                    if (showLines) {
                        g.setColor(lineColor);
                        g.drawRect(baseX, (vertical
                                           ? baseY
                                           : baseY - height), width - 1,
                                           height - (vertical
                                ? 1
                                : 0));
                    }
                    setFont(g, child);
                    FontMetrics fm     = g.getFontMetrics();
                    List        values = new ArrayList();
                    String suffixFrequency = XmlUtil.getAttribute(child,
                                                 ATTR_SUFFIXFREQUENCY,
                                                 XmlUtil.getAttribute(child,
                                                     ATTR_SHOWUNIT,
                                                     "false")).toLowerCase();
                    String unitDefault = ( !suffixFrequency.equals("false"))
                                         ? " %unit%"
                                         : "";
                    String labelSuffix = applyMacros(child, ATTR_SUFFIX,
                                             unitDefault);
                    if (unit != null) {

                        labelSuffix = labelSuffix.replace("%unit%",
                                "" + unit);
                    } else {
                        labelSuffix = labelSuffix.replace("%unit%", "");
                    }
                    if (valuesStr != null) {
                        double[] valueArray = Misc.parseDoubles(valuesStr,
                                                  ",");
                        for (int valueIdx = 0; valueIdx < valueArray.length;
                                valueIdx++) {
                            values.add(new Double(valueArray[valueIdx]));
                        }
                    } else if (ticks > 0) {
                        int spacing = ((ticks == 1)
                                       ? 0
                                       : (vertical
                                          ? height
                                          : width) / (ticks - 1));
                        for (int tickIdx = 0; tickIdx < ticks; tickIdx++) {
                            double percent = ((ticks > 1)
                                    ? (double) tickIdx / (double) (ticks - 1)
                                    : 0.0);
                            values.add(
                                new Double(range.getValueOfPercent(percent)));
                        }
                    } else if (interval > 0) {
                        double value = range.getMin();
                        double max   = range.getMax();
                        while (value <= max) {
                            values.add(new Double(value));
                            value += interval;
                        }
                    }
                    for (int valueIdx = 0; valueIdx < values.size();
                            valueIdx++) {
                        double value =
                            ((Double) values.get(valueIdx)).doubleValue();
                        int x;
                        int y;
                        if (vertical) {
                            if (orientation.equals(VALUE_RIGHT)) {
                                x = baseX + width;
                            } else {
                                x = baseX;
                            }
                            y = baseY
                                + (int) (range.getPercent(value) * height);
                            if (y > baseY + height) {
                                break;
                            }
                        } else {
                            if (orientation.equals(VALUE_BOTTOM)) {
                                y = baseY;
                            } else {
                                y = baseY - height;
                            }

                            if (range != null) {
                                x = baseX
                                    + (int) (range.getPercent(value) * width);
                            } else {
                                x = baseX;
                            }

                            if (x > baseX + width) {
                                break;
                            }
                        }
                        String tickLabel =
                            getIdv().getDisplayConventions().format(value);
                        if (suffixFrequency.equals(VALUE_LAST)
                                && (valueIdx == values.size() - 1)) {
                            tickLabel += labelSuffix;
                        } else if (suffixFrequency.equals(VALUE_FIRST)
                                   && (valueIdx == 0)) {
                            tickLabel += labelSuffix;
                        } else if (suffixFrequency.equals(VALUE_ALL)
                                   || suffixFrequency.equals("true")) {
                            tickLabel += labelSuffix;
                        }


                        Rectangle2D rect = fm.getStringBounds(tickLabel, g);
                        g.setColor(lineColor);
                        if (orientation.equals(VALUE_RIGHT)) {
                            g.drawLine(x + 1, y, x, y);
                            if (showLines) {
                                g.drawLine(x, y, x - width, y);
                            }
                        } else if (orientation.equals(VALUE_LEFT)) {
                            g.drawLine(x - 1, y, x, y);
                            if (showLines) {
                                g.drawLine(x, y, x + width, y);
                            }
                        } else if (orientation.equals(VALUE_BOTTOM)) {
                            g.drawLine(x, y + 1, x, y);
                            if (showLines) {
                                g.drawLine(x, y, x, y - height);
                            }
                        } else {
                            g.drawLine(x, y - 1, x, y);
                            if (showLines) {
                                g.drawLine(x, y, x, y + height);
                            }
                        }
                        g.setColor(c);
                        if (orientation.equals(VALUE_RIGHT)) {
                            int yLoc = y + (int) (rect.getHeight() / 2) - 2;
                            if (forKml) {
                                if (valueIdx == 0) {
                                    yLoc = y + (int) (rect.getHeight()) - 2;
                                } else if (valueIdx == values.size() - 1) {
                                    yLoc = y - (int) (rect.getHeight()) + 6;
                                }
                            }
                            g.drawString(tickLabel, x + 2, yLoc);
                        } else if (orientation.equals(VALUE_LEFT)) {
                            int xLoc = x - 2 - (int) rect.getWidth();
                            g.drawString(tickLabel, xLoc,
                                         y + (int) (rect.getHeight() / 2)
                                         - 2);
                        } else if (orientation.equals(VALUE_BOTTOM)) {
                            int xLoc = x - (int) (rect.getWidth() / 2);
                            if (forKml) {
                                if (valueIdx == 0) {
                                    xLoc = x + 2;
                                } else if (valueIdx == values.size() - 1) {
                                    xLoc = x - (int) rect.getWidth() + 2;
                                }
                            }
                            g.drawString(tickLabel, xLoc,
                                         y + (int) rect.getHeight() + 2);
                        } else {
                            g.drawString(tickLabel,
                                         x - (int) (rect.getWidth() / 2),
                                         y - 2);
                        }
                    }
                    if (vertical) {
                        baseX += width + 30;
                    } else {
                        baseY += height + 30;
                    }
                    if (forKml) {
                        String tmpImageFile =
                            applyMacros(
                                child, ATTR_FILE,
                                getIdv().getStore().getTmpFile(
                                    "testcolorbar${viewindex}.png"));
                        String template =
                            "${kml.name}${icon}\n"
                            + "\n"
                            + "\n"
                            + "\n"
                            + "\n";
                        String[] macros = {
                            "kml.name", "kml.overlayXY.x", "kml.overlayXY.y",
                            "kml.overlayXY.xunits", "kml.overlayXY.yunits",
                            "kml.screenXY.x", "kml.screenXY.y",
                            "kml.screenXY.xunits", "kml.screenXY.yunits",
                            "kml.size.x", "kml.size.y", "kml.size.xunits",
                            "kml.size.yunits"
                        };
                        String[] macroValues = {
                            "", "0", "1", "fraction", "fraction", "0", "1",
                            "fraction", "fraction", "-1", "-1", "pixels",
                            "pixels"
                        };

                        for (int macroIdx = 0; macroIdx < macros.length;
                                macroIdx++) {
                            template =
                                template.replace("${" + macros[macroIdx]
                                    + "}", applyMacros(child,
                                        macros[macroIdx],
                                        macroValues[macroIdx]));
                        }
                        template = template.replace("${icon}",
                                IOUtil.getFileTail(tmpImageFile));
                        imageProps.put("kml", template);
                        List kmlFiles = (List) imageProps.get("kmlfiles");
                        //TODO: Only do the first one for now
                        if (kmlFiles == null) {
                            kmlFiles = new ArrayList();
                            imageProps.put("kmlfiles", kmlFiles);
                        }
                        kmlFiles.add(tmpImageFile);

                        //                        System.out.println(template);
                        ImageUtils.writeImageToFile(imageToDrawIn,
                                tmpImageFile);
                    }
                }

            } else if (tagName.equals(TAG_TRANSPARENT)
                       || tagName.equals(TAG_BGTRANSPARENT)) {
                Color c = null;
                if (tagName.equals(TAG_BGTRANSPARENT)) {
                    c = viewManager.getBackground();
                } else {
                    c = applyMacros(child, ATTR_COLOR, (Color) null);
                }
                //                System.err.println ("c:" + c);
                int[] redRange   = { 0, 0 };
                int[] greenRange = { 0, 0 };
                int[] blueRange  = { 0, 0 };
                if (c != null) {
                    //                    System.err.println("got color");
                    redRange[0]   = redRange[1] = c.getRed();
                    greenRange[0] = greenRange[1] = c.getGreen();
                    blueRange[0]  = blueRange[1] = c.getBlue();
                } else {}
                newImage = ImageUtils.makeColorTransparent(image, redRange,
                        greenRange, blueRange);
            } else if (tagName.equals(TAG_SHOW)) {
                JComponent contents = new JLabel(new ImageIcon(image));
                String message = applyMacros(child, ATTR_MESSAGE,
                                             (String) null);
                if (message != null) {
                    contents = GuiUtils.topCenter(new JLabel(message),
                            contents);
                }
                if ( !GuiUtils.askOkCancel("Continue?", contents)) {
                    throw new MyQuitException();
                }
            } else if (tagName.equals(TAG_MATTE)) {
                newImage = doMatte(image, child, 0);
            } else if (tagName.equals(TAG_LATLONLABELS)) {
                newImage = doLatLonLabels(child, viewManager, image,
                                          imageProps);
            } else if (tagName.equals(TAG_WRITE)) {
                ImageUtils.writeImageToFile(
                    image, getImageFileName(applyMacros(child, ATTR_FILE)));

            } else if (tagName.equals(TAG_PUBLISH)) {
                getIdv().getPublishManager().publishIslImage(this, node,
                        image);
            } else if (tagName.equals(TAG_CLIP)) {
                int[] ul;
                int[] lr;
                if (XmlUtil.hasAttribute(child, ATTR_DISPLAY)) {
                    //                    System.err.println("Clipping from display");
                    DisplayControlImpl dc = findDisplayControl(child);
                    if (dc == null) {
                        throw new IllegalArgumentException(
                            "Could not find display:"
                            + XmlUtil.toString(node));
                    }
                    NavigatedDisplay display =
                        (NavigatedDisplay) viewManager.getMaster();
                    MapProjection mapProjection = dc.getDataProjection();
                    java.awt.geom.Rectangle2D rect =
                        mapProjection.getDefaultMapArea();
                    LatLonPoint llplr =
                        mapProjection.getLatLon(new double[][] {
                        { rect.getX() + rect.getWidth() },
                        { rect.getY() + rect.getHeight() }
                    });
                    LatLonPoint llpul =
                        mapProjection.getLatLon(new double[][] {
                        { rect.getX() }, { rect.getY() }
                    });
                    EarthLocation ulEl = new EarthLocationTuple(llpul,
                                             new Real(RealType.Altitude, 0));
                    EarthLocation lrEl = new EarthLocationTuple(llplr,
                                             new Real(RealType.Altitude, 0));
                    ul = display.getScreenCoordinates(
                        display.getSpatialCoordinates(ulEl, null));
                    lr = display.getScreenCoordinates(
                        display.getSpatialCoordinates(lrEl, null));
                    //System.err.println("ul:" + ulEl + " lr:" + lrEl);
                    if (ul[0] > lr[0]) {
                        int tmp = ul[0];
                        ul[0] = lr[0];
                        lr[0] = tmp;
                    }
                    if (ul[1] > lr[1]) {
                        int tmp = ul[1];
                        ul[1] = lr[1];
                        lr[1] = tmp;
                    }
                    imageProps.put(ATTR_NORTH,
                                   new Double(ulEl.getLatitude().getValue()));
                    imageProps.put(
                        ATTR_WEST,
                        new Double(ulEl.getLongitude().getValue()));
                    imageProps.put(ATTR_SOUTH,
                                   new Double(lrEl.getLatitude().getValue()));
                    imageProps.put(
                        ATTR_EAST,
                        new Double(lrEl.getLongitude().getValue()));
                } else if ((viewManager != null)
                           && XmlUtil.hasAttribute(child, ATTR_NORTH)) {
                    NavigatedDisplay display =
                        (NavigatedDisplay) viewManager.getMaster();
                    EarthLocation el1 =
                        DisplayControlImpl.makeEarthLocation(toDouble(child,
                            ATTR_NORTH), toDouble(child, ATTR_WEST), 0);
                    EarthLocation el2 =
                        DisplayControlImpl.makeEarthLocation(toDouble(child,
                            ATTR_SOUTH), toDouble(child, ATTR_EAST), 0);
                    ul = display.getScreenCoordinates(
                        display.getSpatialCoordinates(el1, null));
                    lr = display.getScreenCoordinates(
                        display.getSpatialCoordinates(el2, null));
                    imageProps.put(ATTR_NORTH,
                                   new Double(el1.getLatitude().getValue()));
                    imageProps.put(ATTR_WEST,
                                   new Double(el1.getLongitude().getValue()));
                    imageProps.put(ATTR_SOUTH,
                                   new Double(el2.getLatitude().getValue()));
                    imageProps.put(ATTR_EAST,
                                   new Double(el2.getLongitude().getValue()));
                } else if (XmlUtil.hasAttribute(child, ATTR_LEFT)) {
                    ul = new int[] {
                        (int) toDouble(child, ATTR_LEFT, imageWidth),
                        (int) toDouble(child, ATTR_TOP, imageHeight) };
                    lr = new int[] {
                        (int) toDouble(child, ATTR_RIGHT, imageWidth),
                        (int) toDouble(child, ATTR_BOTTOM, imageHeight) };
                } else if (viewManager != null) {
                    //TODO: Clip on visad coordinates
                    NavigatedDisplay display =
                        (NavigatedDisplay) viewManager.getMaster();

                    ul = display.getScreenCoordinates(new double[] { -1, 1,
                            0 });
                    lr = display.getScreenCoordinates(new double[] { 1, -1,
                            0 });
                    int space  = applyMacros(child, ATTR_SPACE, 0);
                    int hspace = applyMacros(child, ATTR_HSPACE, space);
                    int vspace = applyMacros(child, ATTR_VSPACE, space);
                    ul[0] -= applyMacros(child, ATTR_SPACE_LEFT, hspace);
                    ul[1] -= applyMacros(child, ATTR_SPACE_TOP, vspace);
                    lr[0] += applyMacros(child, ATTR_SPACE_RIGHT, hspace);
                    lr[1] += applyMacros(child, ATTR_SPACE_BOTTOM, vspace);
                } else {
                    continue;
                }


                for (String attr :
                        (List) Misc.newList(ATTR_NORTH, ATTR_SOUTH,
                            ATTR_EAST, ATTR_WEST)) {
                    String kmlAttr = "kml." + attr;
                    if (XmlUtil.hasAttribute(child, kmlAttr)) {
                        imageProps.put(attr,
                                       new Double(applyMacros(child, kmlAttr,
                                           0.0)));
                    }
                }



                ul[0]    = Math.max(0, ul[0]);
                ul[1]    = Math.max(0, ul[1]);

                lr[0]    = Math.min(lr[0], imageWidth);
                lr[1]    = Math.min(lr[1], imageHeight);
                newImage = ImageUtils.clip(image, ul, lr);
            } else if (tagName.equals(TAG_SPLIT)) {
                shouldIterateChildren = false;
                int    width  = image.getWidth(null);
                int    height = image.getHeight(null);
                int    cols   = applyMacros(child, ATTR_COLUMNS, 2);
                int    rows   = applyMacros(child, ATTR_ROWS, 2);
                String file   = applyMacros(child, ATTR_FILE);
                int    cnt    = 0;
                int    hSpace = width / cols;
                int    vSpace = height / rows;
                for (int row = 0; row < rows; row++) {
                    for (int col = 0; col < cols; col++) {
                        pushProperties();
                        Hashtable myprops = new Hashtable();
                        putProperty("row", new Integer(row));
                        putProperty("column", new Integer(col));
                        putProperty("count", new Integer(++cnt));
                        String realFile = applyMacros(file, myprops);
                        Image splitImage = image.getSubimage(hSpace * col,
                                               vSpace * row, hSpace, vSpace);
                        processImage(ImageUtils.toBufferedImage(splitImage),
                                     realFile, child, myprops, viewManager,
                                     new Hashtable());
                        popProperties();
                    }
                }
            } else if (tagName.equals(TAG_THUMBNAIL)) {
                shouldIterateChildren = false;
                BufferedImage thumbImage =
                    ImageUtils.toBufferedImage(resize(image, child));
                String thumbFile = applyMacros(child, ATTR_FILE,
                                       (String) null);
                if (thumbFile == null) {
                    thumbFile = IOUtil.stripExtension(filename) + "_thumb"
                                + IOUtil.getFileExtension(filename);
                }
                processImage(thumbImage, thumbFile, child, null, viewManager,
                             new Hashtable());
            } else if (tagName.equals(TAG_KML)) {
                //NOOP
            } else if (tagName.equals(TAG_KMZFILE)) {
                //NOOP
            } else if (tagName.equals(TAG_OVERLAY)) {
                double transparency = applyMacros(child, ATTR_TRANSPARENCY,
                                          0.0);
                Graphics2D g = (Graphics2D) image.getGraphics();
                String imagePath = applyMacros(child, ATTR_IMAGE,
        g.setFont(f);
                                       (String) null);

                Rectangle imageRect = new Rectangle(0, 0, imageWidth,
                                          imageHeight);
                Point pp = ImageUtils.parsePoint(applyMacros(child,
                               ATTR_PLACE, "lr,-10,-10"), imageRect);
                String text = applyMacros(child, ATTR_TEXT, (String) null);
                Color  bg = applyMacros(child, ATTR_BACKGROUND, (Color) null);
                if (text != null) {
                    double angle = Math.toRadians(applyMacros(child,
                                       ATTR_ANGLE, 0.0));
                    text = applyMacros(text);
                    Color c = applyMacros(child, ATTR_COLOR, Color.white);
                    if ((c != null) && (transparency > 0)) {
                        c = new Color(c.getRed(), c.getGreen(), c.getBlue(),
                                      ImageUtils.toAlpha(transparency));
                    }
                    //Color bg = applyMacros(child, ATTR_BACKGROUND,
                    //                       (Color) null);
                    if ((bg != null) && (transparency > 0)) {
                        bg = new Color(bg.getRed(), bg.getGreen(),
                                       bg.getBlue(),
                                       ImageUtils.toAlpha(transparency));
                    }
                    setFont(g, child);
                    FontMetrics fm     = g.getFontMetrics();
                    Rectangle2D rect   = fm.getStringBounds(text, g);
                    int         width  = (int) rect.getWidth();
                    int         height = (int) (rect.getHeight());

                    Point ap = ImageUtils.parsePoint(applyMacros(child,
                                   ATTR_ANCHOR,
                                   "lr,-10,-10"), new Rectangle(0, 0, width,
                                       height));

                    g.rotate(angle);

                    if (bg != null) {
                        g.setColor(bg);
                        g.fillRect(pp.x - ap.x - 1, pp.y - ap.y - 1,
                                   (int) width + 2, (int) height + 2);
                    }
                    g.setColor(c);
                    g.drawString(text, pp.x - ap.x, pp.y - ap.y + height);
                }

                if (imagePath != null) {
                    Image overlay = ImageUtils.readImage(imagePath);
                    if (overlay != null) {
                        if (transparency > 0) {
                            overlay = ImageUtils.setAlpha(overlay,
                                    transparency);
                        }
                        int width  = overlay.getWidth(null);
                        int height = overlay.getHeight(null);
                        Point ap = ImageUtils.parsePoint(applyMacros(child,
                                       ATTR_ANCHOR,
                                       "lr,-10,-10"), new Rectangle(0, 0,
                                           width, height));
                        g.drawImage(overlay, pp.x - ap.x, pp.y - ap.y, bg,
                                    null);
                    }
                }
            } else {
                error("Unknown tag:" + tagName);
            }
            if (newImage != null) {
                String newFileName = applyMacros(child, ATTR_FILE,
                                         (String) null);
                if (shouldIterateChildren) {
                    newImage = processImage(newImage, newFileName, child,
                                            null, viewManager,
                                            new Hashtable());
                }
                if (newFileName != null) {
                    ImageUtils.writeImageToFile(newImage,
                            getImageFileName(newFileName));
                    debug("Writing image:" + newFileName);
                }
                if ( !applyMacros(child, ATTR_COPY, false)) {
                    image = newImage;
                }
            }
        }
        if (filename != null) {
            float quality = (float) applyMacros(node, ATTR_QUALITY, 1.0);
            List fileToks = (List) StringUtil.split(filename,
                                        ",", true, true);
            for (String file : fileToks) {
                file = getImageFileName(file);
                debug("Writing image:" + file);
                if (file.endsWith(FileManager.SUFFIX_KMZ)) {
                    GeoLocationInfo bounds = null;
                    if (viewManager != null) {
                        bounds = viewManager.getVisibleGeoBounds();
                        ImageSequenceGrabber.subsetBounds(bounds, imageProps);
                        String tail = IOUtil.getFileTail(file);
                        String tmpImageFile =
                            getIdv().getStore().getTmpFile(tail + ".png");
                        ImageUtils.writeImageToFile(image, tmpImageFile,
                                quality);
                        ImageWrapper imageWrapper =
                            new ImageWrapper(tmpImageFile, null, bounds,
                                             null);
                        imageWrapper.setProperties(imageProps);
                        new ImageSequenceGrabber(
                            file, getIdv(), this, node,
                            (List) Misc.newList(imageWrapper),
                            null, 1);
                    }
                } else {
                    ImageUtils.writeImageToFile(image, file, quality);
                }
            }
        }
        return image;
    }


    /**
     * Get the insets
     *
     * @param child  the element
     * @param dflt   the default value
     *
     * @return the Insets
     */
    public Insets getInsets(Element child, int dflt) {
        int space  = applyMacros(child, ATTR_SPACE, dflt);
        int hspace = applyMacros(child, ATTR_HSPACE, space);
        int vspace = applyMacros(child, ATTR_VSPACE, space);
        int top    = applyMacros(child, ATTR_TOP, vspace);
        int bottom = applyMacros(child, ATTR_BOTTOM, vspace);
        int left   = applyMacros(child, ATTR_LEFT, hspace);
        int right  = applyMacros(child, ATTR_RIGHT, hspace);
        return new Insets(top, left, bottom, right);
    }


    /**
     * Process the lat/lon labels tag
     *
     * @param child  the XML
     * @param viewManager  the associated view manager
     * @param image  the image to draw on
     * @param imageProps  the image properties
     *
     * @return  a new image
     *
     * @throws Exception  on badness
     */
    public BufferedImage doLatLonLabels(Element child,
                                        ViewManager viewManager,
                                        BufferedImage image,
                                        Hashtable imageProps)
            throws Exception {

        if (viewManager == null) {
            throw new IllegalArgumentException("Tag " + TAG_LATLONLABELS
                    + " requires a view");
        }
        if ( !(viewManager instanceof MapViewManager)) {
            throw new IllegalArgumentException("Tag " + TAG_LATLONLABELS
                    + " requires a map view");
        }
        MapViewManager   mvm     = (MapViewManager) viewManager;
        NavigatedDisplay display = (NavigatedDisplay) viewManager.getMaster();
        DecimalFormat format = new DecimalFormat(applyMacros(child,
                                   ATTR_FORMAT, "##0.0"));
        Color color     = applyMacros(child, ATTR_COLOR, Color.red);
        Color lineColor = applyMacros(child, ATTR_LINECOLOR, color);
        Color bg = applyMacros(child, ATTR_LABELBACKGROUND, (Color) null);

        double[] latValues = Misc.parseDoubles(applyMacros(child,
    }
                                 ATTR_LAT_VALUES, ""));
        List latLabels = StringUtil.split(applyMacros(child,
                                     ATTR_LAT_LABELS, ""), ",", true, true);
        double[] lonValues = Misc.parseDoubles(applyMacros(child,
                                 ATTR_LON_VALUES, ""));
        List lonLabels = StringUtil.split(applyMacros(child,
                                     ATTR_LON_LABELS, ""), ",", true, true);

        boolean drawLonLines = applyMacros(child, ATTR_DRAWLONLINES, false);
        boolean drawLatLines = applyMacros(child, ATTR_DRAWLATLINES, false);
        boolean       showTop    = applyMacros(child, ATTR_SHOWTOP, false);
        boolean       showBottom = applyMacros(child, ATTR_SHOWBOTTOM, true);
        boolean       showLeft   = applyMacros(child, ATTR_SHOWLEFT, true);
        boolean       showRight  = applyMacros(child, ATTR_SHOWRIGHT, false);

        int           width      = image.getWidth(null);
        int           height     = image.getHeight(null);
        int           centerX    = width / 2;
        int           centerY    = height / 2;
        EarthLocation nw, ne, se, sw;

        //don: this  what I added
        Double north = (Double) imageProps.get(ATTR_NORTH);
        Double south = (Double) imageProps.get(ATTR_SOUTH);
        Double east  = (Double) imageProps.get(ATTR_EAST);
        Double west  = (Double) imageProps.get(ATTR_WEST);
        //Assume if we have one we have them all
        if (north != null) {
            nw = DisplayControlImpl.makeEarthLocation(north.doubleValue(),
                    west.doubleValue(), 0);

    }
            ne = DisplayControlImpl.makeEarthLocation(north.doubleValue(),
                    east.doubleValue(), 0);
            sw = DisplayControlImpl.makeEarthLocation(south.doubleValue(),
                    west.doubleValue(), 0);
            se = DisplayControlImpl.makeEarthLocation(south.doubleValue(),
                    east.doubleValue(), 0);

        } else {
            nw = display.screenToEarthLocation(0, 0);
            ne = display.screenToEarthLocation(width, 0);
            se = display.screenToEarthLocation(0, height);
            sw = display.screenToEarthLocation(width, height);
        }


        double widthDegrees = ne.getLongitude().getValue()
                              - nw.getLongitude().getValue();
        double heightDegrees = ne.getLatitude().getValue()
                               - se.getLatitude().getValue();

        Insets insets = getInsets(child, 0);
        int    delta  = 2;
        int    bgPad  = 1;

        image = doMatte(image, child, 0);

        Graphics2D g = (Graphics2D) image.getGraphics();
        g.setFont(getFont(child));
        FontMetrics fm            = g.getFontMetrics();

        int lineOffsetRight = applyMacros(child, ATTR_LINEOFFSET_RIGHT, 0);
        int lineOffsetLeft = applyMacros(child, ATTR_LINEOFFSET_LEFT, 0);
        int         lineOffsetTop = applyMacros(child, ATTR_LINEOFFSET_TOP,
                                        0);
        int lineOffsetBottom = applyMacros(child, ATTR_LINEOFFSET_BOTTOM, 0);



        Stroke      lineStroke;
        if (XmlUtil.hasAttribute(child, ATTR_DASHES)) {
            lineStroke = new BasicStroke((float) applyMacros(child,
                    ATTR_LINEWIDTH, 1.0), BasicStroke.CAP_BUTT,
                                          BasicStroke.JOIN_BEVEL, 1.0f,
                                          Misc.parseFloats(applyMacros(child,
                                              ATTR_DASHES, "8")), 0.0f);
        } else {
            lineStroke = new BasicStroke((float) applyMacros(child,
                    ATTR_LINEWIDTH, 1.0));
        }

        g.setStroke(lineStroke);
        double leftLon  = nw.getLongitude().getValue(CommonUnit.degree);
        double rightLon = ne.getLongitude().getValue(CommonUnit.degree);
        Range  lonRange = new Range(leftLon, rightLon);
        for (int i = 0; i < lonValues.length; i++) {
            double lon = GeoUtils.normalizeLongitude(lonRange, lonValues[i]);
            double percent = (lon - nw.getLongitude().getValue())
                             / widthDegrees;
            //            if(percent<0 || percent>1) continue;
            String label;
            if (i < lonLabels.size()) {
                label = lonLabels.get(i);
            } else {
                label = format.format(lonValues[i]);
            }
            Rectangle2D rect  = fm.getStringBounds(label, g);
            int         baseX = insets.left + (int) (percent * width);
            int         x     = baseX - (int) rect.getWidth() / 2;
            int         topY;
            if (insets.top == 0) {
                topY = (int) rect.getHeight() + delta;
            } else {
                topY = insets.top - delta;
            }
            if (drawLonLines) {
                g.setColor(lineColor);
                g.drawLine(baseX, insets.top + lineOffsetTop, baseX,
                           insets.top + height - lineOffsetBottom);
            }

            if (showTop) {
                if (bg != null) {
                    g.setColor(bg);
                    g.fillRect(x - bgPad,
                               topY - (int) rect.getHeight() - bgPad,
                               (int) rect.getWidth() + bgPad * 2,
                               (int) rect.getHeight() + bgPad * 2);
                }

                g.setColor(color);
                g.drawString(label, x, topY);
            }
            int bottomY;
            if (insets.bottom == 0) {
                bottomY = insets.top + height - delta;
            } else {
                bottomY = insets.top + height + (int) rect.getHeight()
                          + delta;
            }
            if (showBottom) {
                if (bg != null) {
                    g.setColor(bg);
                    g.fillRect(x - bgPad,
                               bottomY - (int) rect.getHeight() - bgPad,
                               (int) rect.getWidth() + bgPad * 2,
                               (int) rect.getHeight() + bgPad * 2);
                }
                g.setColor(color);
                g.drawString(label, x, bottomY);
            }
        }


        for (int i = 0; i < latValues.length; i++) {
            double lat = latValues[i];
            double percent = 1.0
                             - (lat - se.getLatitude().getValue())
                               / heightDegrees;
            int baseY = insets.top + (int) (percent * height);
            //            if(percent<0 || percent>1) continue;
            String label;
            if (i < latLabels.size()) {
                label = latLabels.get(i);
            } else {
                label = format.format(lat);
            }
            Rectangle2D rect = fm.getStringBounds(label, g);
            int         y    = baseY + (int) rect.getHeight() / 2;
            int         leftX;
            if (insets.left == 0) {
                leftX = 0 + delta;
            } else {
                leftX = insets.left - (int) rect.getWidth() - delta;
            }
            if (drawLonLines) {
                g.setColor(lineColor);
                g.drawLine(insets.left + lineOffsetRight, baseY,
                           insets.left + width - lineOffsetRight, baseY);
            }

            if (showLeft) {
                if (bg != null) {
                    g.setColor(bg);
                    g.fillRect(leftX - bgPad,
                               y - (int) rect.getHeight() - bgPad,
                               (int) rect.getWidth() + bgPad * 2,

                               (int) rect.getHeight() + bgPad * 2);
                }
                g.setColor(color);
                g.drawString(label, leftX, y);
            }

            if (insets.right == 0) {
                leftX = insets.left + width - (int) rect.getWidth() - delta;
            } else {
                leftX = insets.left + width + delta;
            }
            if (showRight) {
                if (bg != null) {
                    g.setColor(bg);
                    g.fillRect(leftX - bgPad,
                               y - (int) rect.getHeight() - bgPad,
                               (int) rect.getWidth() + bgPad * 2,
                               (int) rect.getHeight() + bgPad * 2);
                }
                g.setColor(color);
                g.drawString(label, leftX, y);
            }
        }

        return image;

    }

    /**
     * Matte the image
     *
     * @param image  the image
     * @param child  the XML defining the matting
     * @param dfltSpace  default spacing
     *
     * @return a new image
     */
    public BufferedImage doMatte(BufferedImage image, Element child,
                                 int dfltSpace) {
        return doMatte(image, child, getInsets(child, dfltSpace));

    /**
     * Matte the image
     *
     * @param image  the image
     * @param child  the matte specs
     * @param insets the insets
     *
     * @return  a new image
     */
    public BufferedImage doMatte(BufferedImage image, Element child,
                                 Insets insets) {
        Color bg = applyMacros(child, ATTR_BACKGROUND, Color.white);
        return ImageUtils.matte(image, insets.top, insets.bottom,
                                insets.left, insets.right, bg);
    }



    /*
      public void setDataSourceFiles(String[] datasource, String[] filenames) {

      List dataSources = getIdv().getDataSources();
        for (int k = 0; k < datasource.size(); k++) {
          for (int i = 0; i < dataSources.size(); i++) {
            DataSource theDataSource = (DataSource) dataSources.get(i);
            if (theDataSource.identifiedByName(datasource[k])) {
              theDataSource.setNewFiles(new ArrayList().add(filenames[k]));
            }
          }
        }
      }

     */


    /**
     * Get the file name to write images to. If we are in test mode then prepend the test directory
     *
     * @param filename image file name
     *
     * @return filename to use
     */
    private String getImageFileName(String filename) {
        if (LogUtil.getTestMode()) {
            if (getIdv().getArgsManager().testDir != null) {
                filename = IOUtil.joinDir(getIdv().getArgsManager().testDir,
                                          filename);
            }
        }
        return filename;
    }


    /**
     * Set the font on the graphics from the font defined on the node.
     *
     * @param g The graphics
     * @param node Node to get font info from
     */
    private void setFont(Graphics g, Element node) {
        int fontSize = applyMacros(node, ATTR_FONTSIZE, 12);
        Font f = new Font(applyMacros(node, ATTR_FONTFACE, "dialog"),
                          Font.PLAIN, fontSize);

    /**
     * Get the font from the XML
     *
     * @param node  the XML
     *
     * @return  the font or null
     */
    private Font getFont(Element node) {
        if (XmlUtil.hasAttribute(node, ATTR_FONTSIZE)
                || XmlUtil.hasAttribute(node, ATTR_FONTFACE)) {
            int fontSize = applyMacros(node, ATTR_FONTSIZE, 12);
            return new Font(applyMacros(node, ATTR_FONTFACE, "dialog"),
                            Font.PLAIN, fontSize);
        }
        return null;
    }



    /**
     * Called to notify this object that the movie capture is done
     */
    public synchronized void doneCapturingMovie() {
        this.notify();
    }


    /**
     * Capture a movie from the first view manager
     *
     * @param filename The movie  filename
     */
    public synchronized void captureMovie(String filename) {
        captureMovie(filename, null);
    }


    /**
     * Capture the movie
     *
     * @param filename The file
     * @param scriptingNode Node form isl.
     */
    public synchronized void captureMovie(String filename,
                                          Element scriptingNode) {

        if ((filename == null) && (scriptingNode != null)) {
            filename = XmlUtil.getAttribute(scriptingNode, ATTR_FILE);
        }

        if (scriptingNode != null) {
            List files = findFiles(scriptingNode);
            if (files != null) {
                debug("Making movie from existing images " + filename);
                filename = applyMacros(filename);
                Dimension size = new Dimension(applyMacros(scriptingNode,
                                     ATTR_WIDTH,
                                     400), applyMacros(scriptingNode,
                                         ATTR_HEIGHT, 300));
                ImageSequenceGrabber isg = new ImageSequenceGrabber(
                                               filename, getIdv(), this,
                                               scriptingNode, files, size,
                                               applyMacros(
                                                   scriptingNode,
                                                   ATTR_FRAMERATE,
                                                   2), applyMacros(
                                                       scriptingNode,
                                                       ATTR_ENDFRAMEPAUSE,
                                                       -1));
                return;
            }
        }

        List viewManagers = null;
        if ((scriptingNode != null)
                && XmlUtil.hasAttribute(scriptingNode, ATTR_DISPLAY)) {
            DisplayControlImpl display = findDisplayControl(scriptingNode);
            if (display == null) {
                throw new IllegalArgumentException("Could not find display:"
                        + XmlUtil.toString(scriptingNode));
            }
            String what = applyMacros(scriptingNode, ATTR_WHAT,
                                      (String) null);

            ViewManager viewManager = null;
            try {
                viewManager = display.getViewManagerForCapture(what);
                if (viewManager != null) {
                    viewManager.updateDisplayIfNeeded();
                }
            } catch (Exception exc) {
                throw new RuntimeException(exc);
            }

            if (viewManager != null) {
                viewManagers = (List) Misc.newList(viewManager);
            } else {
                throw new IllegalArgumentException(
                    "Cannot capture a movie with display:"
                    + XmlUtil.toString(scriptingNode));
            }
        }

        if (viewManagers == null) {
            viewManagers = (List) getViewManagers(scriptingNode);
        }



        boolean combine = XmlUtil.getAttribute(scriptingNode,
                              ImageGenerator.ATTR_COMBINE, false);


        if (combine) {
            ViewManager viewManager =
                getIdv().getVMManager().getLastActiveViewManager();

            getProperties().put(PROP_VIEWINDEX, new Integer(0));
            String name = viewManager.getName();
            if (name == null) {
                name = "view" + 0;
            }
            getProperties().put(PROP_VIEWNAME, name);

            if ( !getIdv().getArgsManager().getIsOffScreen()) {
                JFrame frame = GuiUtils.getFrame(viewManager.getContents());
                if (frame != null) {
                    LogUtil.registerWindow(frame);
                    frame.show();
                    GuiUtils.toFront(frame);
                    frame.setLocation(50, 50);
     */
                    Misc.sleep(50);
                }
            }
            String loopFilename = applyMacros(filename);
            debug("Making movie:" + loopFilename);
            ImageSequenceGrabber isg = new ImageSequenceGrabber(viewManager,
                                           loopFilename, getIdv(), this,
                                           scriptingNode);
            try {
                wait();
            } catch (Exception exc) {
                logException("Doing the captureMovie wait", exc);
            }
            debug("Done making movie:" + loopFilename);

        } else {

            for (int i = 0; i < viewManagers.size(); i++) {
                ViewManager viewManager = viewManagers.get(i);

                getProperties().put(PROP_VIEWINDEX, new Integer(i));
                String name = viewManager.getName();
                if (name == null) {
                    name = "view" + i;
                }
                getProperties().put(PROP_VIEWNAME, name);

                if ( !getIdv().getArgsManager().getIsOffScreen()) {
                    JFrame frame =
                        GuiUtils.getFrame(viewManager.getContents());
                    if (frame != null) {
                        LogUtil.registerWindow(frame);
                        frame.show();
                        GuiUtils.toFront(frame);
                        frame.setLocation(50, 50);
                        Misc.sleep(50);
                    }
                }
                String loopFilename = applyMacros(filename);
                debug("Making movie:" + loopFilename);
                ImageSequenceGrabber isg =
                    new ImageSequenceGrabber(viewManager, loopFilename,
                                             getIdv(), this, scriptingNode);
                try {
                    wait();
                } catch (Exception exc) {
                    logException("Doing the captureMovie wait", exc);
                }
                debug("Done making movie:" + loopFilename);
            }
        }

    }


    /**
     * Find the animation time of the first Animation in a view manager we find
     *
     * @return Animation time
     */
    public Date getAnimationTime() {
        List vms = getViewManagers(currentNode);
        if (vms.size() > 0) {
            ViewManager vm        = (ViewManager) vms.get(0);
            Animation   animation = vm.getAnimation();
            if (animation != null) {
                Real v = animation.getAniValue();
                if (v != null) {
                    return new Date((long) v.getValue() * 1000);
                }

            }
        }
        return new Date(Misc.getCurrentTime());
    }


    /**
     * Create and instantiate the jython interp.
     *
     * @return The interp
     */
    private PythonInterpreter getInterpreter() {
        if (interpreter == null) {
            interpreter = getIdv().getJythonManager().createInterpreter();
            interpreter.set("ig", this);
            interpreter.set("interp", this);
            interpreter.set("islInterpreter", this);
        }
        return interpreter;
    }

    /**
     * callable by jython to find the data choices that match the given pattern
     *
     * @param datasource data source
     * @param pattern pattern to match
     *
     * @return comma separated list of data choice names
    public String fields(String datasource, String pattern) {
        DataSource dataSource = findDataSource(datasource);
        if (dataSource == null) {
            throw new IllegalArgumentException("Could not find data source:"
                    + datasource);
        }
        List choices;
        if ((pattern == null) || (pattern.length() == 0)) {
            choices = dataSource.getDataChoices();
        } else {
            choices = dataSource.findDataChoices(pattern);
        }

        List names = new ArrayList();
        for (int i = 0; i < choices.size(); i++) {
            DataChoice dataChoice = (DataChoice) choices.get(i);
            names.add(dataChoice.getName());
        }
        return StringUtil.join(",", names);
    }



    /**
     * Class OutputInfo is used for handling output tags
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.113 $
     */
    private class OutputInfo {

        /** The node */
        Element outputNode;

        /** mapping of where to StringBuffer */
        Hashtable buffers = new Hashtable();

        /** mapping of where to templates */
        Hashtable templates = new Hashtable();

        /**
         * ctor
         *
         * @param node The output node
         */
        public OutputInfo(Element node) {
            this.outputNode = node;
        }

        /**
         * Handle the node.
         *
         * @param node Node to process
         *
         * @throws Throwable On badness
         */
        public void process(Element node) throws Throwable {
            String       where = applyMacros(node, ATTR_TEMPLATE, "contents");
            StringBuffer sb       = (StringBuffer) buffers.get(where);
            String       template = (String) templates.get(where);
            if (sb == null) {
                sb = new StringBuffer();
                template = XmlUtil.getAttribute(outputNode,
                        ATTR_TEMPLATE + ":" + where, "${text}");
                if (template.startsWith("file:")) {
                    template = applyMacros(template);
                    template = IOUtil.readContents(template.substring(5));
                }
                buffers.put(where, sb);
                templates.put(where, template);
            }
            String text = XmlUtil.getAttribute(node, ATTR_TEXT,
                              (String) null);
            if (text == null) {
                if (XmlUtil.hasAttribute(node, ATTR_FROMFILE)) {
                    String filename = applyMacros(node, ATTR_FROMFILE);
                    text = applyMacros(IOUtil.readContents(filename));
                } else {
                    text = XmlUtil.getChildText(node);
                    if ((text != null) && (text.length() == 0)) {
                        text = null;
                    }
                }
            }
            if (text == null) {
                NamedNodeMap nnm   = node.getAttributes();
                Hashtable    props = new Hashtable();
                if (nnm != null) {
                    for (int i = 0; i < nnm.getLength(); i++) {
                        Attr attr = (Attr) nnm.item(i);
                        if ( !ATTR_TEMPLATE.equals(attr.getNodeName())) {
                            props.put(attr.getNodeName(),
                                      applyMacros(attr.getNodeValue()));
                        }
                    }
                }
        /**
                text = applyMacros(template, props);
            } else {
                text = applyMacros(text);
            }
            sb.append(text);
        }

        /**
         * Write out the output
         *
         * @throws Throwable On badness
         */
        public void write() throws Throwable {
            String outputFile = applyMacros(outputNode, ATTR_FILE);
            String template = applyMacros(outputNode, ATTR_TEMPLATE,
                                          (String) null);
            if (template == null) {
                template = "${contents}";
            }
            if (template.startsWith("file:")) {
                template = IOUtil.readContents(template.substring(5));
            }
            for (Enumeration keys =
                    buffers.keys(); keys.hasMoreElements(); ) {
                String       key  = (String) keys.nextElement();
                StringBuffer buff = (StringBuffer) buffers.get(key);
                template = applyMacros(template,
                                       Misc.newHashtable(key,
                                           buff.toString()));
            }
            IOUtil.writeFile(outputFile, template);
        }

    }


    /**
     * Print out a wanring message
     *
     * @param msg message
     */
    private void warning(String msg) {
        System.err.println(new Date() + " WARNING:" + msg);
    }



    /**
     * Print the message if in debug mode
     *
     * @param msg The message
     */
    protected void debug(String msg) {
        if (debug) {
            System.out.println(new Date() + ": " + msg);
        }
    }


    /**
     * Class MyBreakException for handling break tags
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.113 $
     */
    protected static class MyBreakException extends Exception {}

    /**
     * Class MyContinueException for handling continue tags
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.113 $
     */
    protected static class MyContinueException extends Exception {}

    /**
     * Class MyReturnException allows us to return from a isl procedure by throwing an exception.
     * Yes, I know you're not supposed to use exceptions in a non-exceptional way but it works
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.113 $
     */
    protected static class MyReturnException extends Exception {}


    /**
     * Class description
     *
     *
     * @version        Enter version here..., Tue, Jan 12, '10
     * @author         Enter your name here...
     */
    protected static class MyQuitException extends Exception {}

    /**
     * Class BadIslException is used to handle bad isl errors
     *
     *
     * @author IDV Development Team
     * @version $Revision: 1.113 $
     */
    private static class BadIslException extends RuntimeException {

        /** message */
        String msg;

         * ctor
         *
         * @param msg error message
         */
        public BadIslException(String msg) {
            this.msg = msg;
        }

        /**
         * to string
         *
         * @return error message
         */
        public String toString() {
            return msg;
        }

    }


    /**
     * IS the FtpClient in an ok state. If it isn't then disconnect it and throw and IllegalStateException
     *
     * @param f Ftp client
     * @param msg Message to use if in error
     *
     * @throws Exception On badness
     */
    private static void checkFtp(FTPClient f, String msg) throws Exception {
        int replyCode = f.getReplyCode();
        if ( !FTPReply.isPositiveCompletion(replyCode)) {
            String reply = f.getReplyString();
            f.disconnect();
            throw new IllegalStateException("Error with ftp: " + replyCode
                                            + " " + msg + "\n" + reply);
        }
    }

    /**
     * Do an FTP put of the given bytes
     *
     * @param server server
     * @param userName user name on server
     * @param password password on server
     * @param destination Where to put the bytes
     * @param bytes The bytes
     *
     * @throws Exception On badness
     */
    public static void ftpPut(String server, String userName,
                              String password, String destination,
                              byte[] bytes)
            throws Exception {
        FTPClient f = new FTPClient();

        f.connect(server);
        f.login(userName, password);
        f.setFileType(FTP.BINARY_FILE_TYPE);
        f.enterLocalPassiveMode();
        checkFtp(f, "Connecting to ftp server");
        f.storeFile(destination, new ByteArrayInputStream(bytes));
        checkFtp(f, "Storing file");
        f.logout();
        f.disconnect();
    }

    /** abcdefg... */
    private static String[] alphabet = {
        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
        "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
    };

    /** roman numerals */
    private static String[] roman = {
        "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI",
        "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XX", "XXI",
        "XXII", "XXIII", "XXIV", "XXV", "XXVI", "XXVII", "XXVIII"
    };

    /**
     * Get the letter for the index
     *
     * @param i  the index
     *
     * @return  the letter
     */
    public String getLetter(int i) {
        if ((i >= 0) && (i < alphabet.length)) {
            return alphabet[i];
        }
        //A hack for now
        return "out of range";

    }

    /**
     * Get the roman numeral
     *
     * @param i the index
     *
     * @return  the corresponding number
     */
    public String getRoman(int i) {
        if ((i >= 0) && (i < roman.length)) {
            return roman[i];
        }
        //A hack for now
        return "out of range";
    }
}
File
ImageGenerator.java
Developer's decision
Version 2
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
     */
<<<<<<< HEAD
/*
 * Copyright 1997-2013 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 * 
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package ucar.unidata.view.geoloc;



import ucar.unidata.geoloc.Bearing;
import ucar.unidata.geoloc.LatLonPointImpl;
import ucar.unidata.geoloc.ProjectionImpl;
import ucar.unidata.geoloc.ProjectionPoint;
import ucar.unidata.geoloc.ProjectionRect;
import ucar.unidata.geoloc.projection.LatLonProjection;
import ucar.unidata.ui.FontSelector;
import ucar.unidata.util.GuiUtils;
import ucar.unidata.util.LogUtil;
import ucar.unidata.util.Misc;
import ucar.unidata.util.Trace;

import ucar.visad.GeoUtils;
import ucar.visad.ProjectionCoordinateSystem;
import ucar.visad.display.MapLines;
import ucar.visad.display.ScalarMapSet;
import ucar.visad.quantities.CommonUnits;
import ucar.visad.quantities.GeopotentialAltitude;

import visad.AxisScale;
import visad.CachingCoordinateSystem;
import visad.CommonUnit;
import visad.CoordinateSystem;
import visad.Display;
import visad.DisplayImpl;
import visad.DisplayRealType;
import visad.DisplayTupleType;
import visad.Gridded2DSet;
import visad.InverseLinearScaledCS;
import visad.KeyboardBehavior;
import visad.MouseBehavior;
import visad.ProjectionControl;
import visad.Real;
import visad.RealTuple;
import visad.RealTupleType;
import visad.RealType;
import visad.ScalarMap;
import visad.ScalarType;
import visad.SetType;
import visad.Unit;
import visad.UnitException;
import visad.VisADException;
import visad.VisADRay;

import visad.data.mcidas.AREACoordinateSystem;
import visad.data.mcidas.BaseMapAdapter;

import visad.georef.EarthLocation;
import visad.georef.EarthLocationTuple;
import visad.georef.MapProjection;
import visad.georef.TrivialMapProjection;


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GraphicsDevice;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Rectangle2D;


import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import java.math.BigDecimal;

import java.net.URL;

import java.rmi.RemoteException;

import java.text.DecimalFormat;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JToolBar;


/**
 * Provides a navigated VisAD DisplayImpl for displaying data.
 * The Projection or MapProjection provides the transformation from
 * lat/lon space to xy space.  There are three modes that can be used
 * with this display - MODE_3D (Java 3D), MODE_2Din3D (2D in Java 3D),
 * MODE_2D (Java 2D).  Performance is better in Java 3D modes. In the 3D
 * mode, RealType.Altitude is mapped to the display Z axis.

* Any displayable data must be able to map to RealType.Latitude, * RealType.Longitude and/or RealType.Altitude.

* * @author Don Murray */ public abstract class MapProjectionDisplay extends NavigatedDisplay { /** * The name of the bearing from center property. */ public static final String CURSOR_BEARING = "cursorBearing"; /** * The name of the range from center property. */ public static final String CURSOR_RANGE = "cursorRange"; /** instance counter */ private static int instance = 0; /** instance locking mutex */ private static Object INSTANCE_MUTEX = new Object(); /** logging category */ private static LogUtil.LogCategory log_ = LogUtil.getLogInstance(MapProjectionDisplay.class.getName()); /** * flag for forcing 2D */ public static boolean force2D = false; /** * The range from center RealType. */ public static RealType CURSOR_RANGE_TYPE = RealType.getRealType("Cursor_Range", CommonUnits.KILOMETER); /** * The bearing from center RealType. */ public static RealType CURSOR_BEARING_TYPE = RealType.getRealType("Cursor_Bearing", CommonUnit.degree); /** ScalarMapf for altitude -> displayAltitudeType */ private ScalarMap altitudeMap = null; /** coordinate system units */ private Unit[] csUnits = null; /** y axis scale */ private AxisScale latScale = null; /** ScalarMapf for latitude -> displayLatitudeType */ private ScalarMap latitudeMap = null; /** x axis scale */ private AxisScale lonScale = null; /** ScalarMapf for longitude -> displayLongitudeType */ private ScalarMap longitudeMap = null; /** default maximum vertical range value */ private double maxVerticalRange = 16000; /** default minimum vertical range value */ private double minVerticalRange = 0; /** vertical axis scale */ private AxisScale verticalScale = null; /** ScalarMap to Display.XAxis */ private ScalarMap xMap = null; /** ScalarMap to Display.YAxis */ private ScalarMap yMap = null; /** ScalarMap to Display.ZAxis */ private ScalarMap zMap = null; /** bearing class for bearing calculations */ private Bearing workBearing = new Bearing(); /** default vertical range unit */ private Unit verticalRangeUnit = CommonUnit.meter; /** Vertical type */ private RealType verticalParameter = RealType.Altitude; /** Set of vertical maps */ private VerticalMapSet verticalMapSet = new VerticalMapSet(); /** use 0-360 for longitude range */ private boolean use360 = true; /** number format for axis labels */ DecimalFormat labelFormat = new DecimalFormat("####0.0"); /** flag for whether we've been initialized */ private boolean init = false; /** cursor location for bearing calculations */ private LatLonPointImpl cursorLLP = new LatLonPointImpl(); /** centerpoint for bearing calculations */ private LatLonPointImpl centerLLP = new LatLonPointImpl(); /** flag for adjusting lons or not */ private boolean adjustLons = false; /** The coordinate system for the display */ private CoordinateSystem coordinateSystem; /** * The cursor altitude. * @serial */ private volatile Real cursorBearing; /** * The cursor altitude. * @serial */ private volatile Real cursorRange; /** The display's Altitude DisplayRealType */ private DisplayRealType displayAltitudeType; /** The display's Latitude DisplayRealType */ private DisplayRealType displayLatitudeType; /** The display's Longitude DisplayRealType */ private DisplayRealType displayLongitudeType; /** the display tuple type */ private DisplayTupleType displayTupleType; /** The lat scale info */ private LatLonAxisScaleInfo latScaleInfo; /** The lon scale info */ private LatLonAxisScaleInfo lonScaleInfo; /** The MapProjection */ private MapProjection mapProjection; /** * Constructs an instance with the specified MapProjection */ protected MapProjectionDisplay() {} /** * Constructs an instance with the specified MapProjection * CoordinateSystem and display. * * @param projection map projection CS * @param display display to use * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ protected MapProjectionDisplay(MapProjection projection, DisplayImpl display) throws VisADException, RemoteException { super(display); // mapProjection = projection; // coordinateSystem = makeCoordinateSystem(projection); setMapProjection(projection); initializeClass(); } /** * Initializes an instance with the specified MapProjection * CoordinateSystem and display. * * @param projection map projection CS * @param display display to use * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ protected void init(MapProjection projection, DisplayImpl display) throws VisADException, RemoteException { super.init(display); setMapProjection(projection); initializeClass(); } /** * Set up the display. Any additional work should be done in * a subclass's intializeClass() method, which should call * super.initializeClass() first. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ protected void initializeClass() throws VisADException, RemoteException { super.initializeClass(); setDisplayTypes(); } /** * Get an instance of a MapProjectionDisplay using the mode specified * and the default projection. * * @param mode mode to use * * @return a MapProjectionDisplay * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ public static MapProjectionDisplay getInstance(int mode) throws VisADException, RemoteException { return getInstance(null, mode); } /** * Get an instance of a MapProjectionDisplay using the mode specified * and the MapProjection. * * @param mode mode to use * @param p initial MapProjection for display * * @return a MapProjection display of the correct mode and projection * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ public static MapProjectionDisplay getInstance(MapProjection p, int mode) throws VisADException, RemoteException { return getInstance(p, mode, false, null); } /** * Get an instance of a MapProjectionDisplay using the mode specified * and the MapProjection. * * @param p map projection * @param mode mode * @param offscreen true if offscreen * @param dimension dimension of display * * @return the instance * * @throws RemoteException Java RMI problem * @throws VisADException problem creating the display or some component */ public static MapProjectionDisplay getInstance(MapProjection p, int mode, boolean offscreen, Dimension dimension) throws VisADException, RemoteException { return getInstance(p, mode, offscreen, dimension, null); } /** * Get an instance of a MapProjectionDisplay using the mode specified * and the MapProjection. * * @param p map projection * @param mode mode * @param offscreen true if offscreen * @param dimension dimension of display * @param screen screen to display it on * * @return the instance * * @throws RemoteException Java RMI problem * @throws VisADException problem creating the display or some component */ public static MapProjectionDisplay getInstance(MapProjection p, int mode, boolean offscreen, Dimension dimension, GraphicsDevice screen) throws VisADException, RemoteException { if (p == null) { Trace.call1("MapProjectionDisplay.getInstance:makeProjection"); p = makeDefaultMapProjection(); Trace.call2("MapProjectionDisplay.getInstance:makeProjection"); } if (((mode == MODE_3D) || (mode == MODE_2Din3D)) && !force2D) { Trace.call1( "MapProjectionDisplay.getInstance:new MapProjectionDisplayJ3D"); MapProjectionDisplay mpd = new MapProjectionDisplayJ3D(p, mode, offscreen, dimension, screen); Trace.call2( "MapProjectionDisplay.getInstance:new MapProjectionDisplayJ3D"); return mpd; } else { return new MapProjectionDisplayJ2D(p); } } /** * Destroy this class */ public void destroy() { super.destroy(); } // private List keyboardBehaviors; /** * Add a KeyboardBehavior to this class * * @param behavior behavior to add */ public abstract void addKeyboardBehavior(KeyboardBehavior behavior); /** * Set the make lat scale * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void makeLatScales() throws VisADException, RemoteException { setDisplayInactive(); if (latScale != null) { latScale.setVisible(getLatScaleInfo().isVisible()); latScale.setLabelRelief(getLatScaleInfo().isLabelRelief()); updateLatScale(latScale); } setDisplayActive(); } /** * Calculate minimum latitude according to VisAD. * * @return the lat base */ private double calcLatBase() { double[] xRange = xMap.getRange(); double[] yRange = yMap.getRange(); double[] zRange = (zMap != null) ? zMap.getRange() : new double[] { 0, 0 }; EarthLocation el = getEarthLocation(xRange[0], yRange[0], zRange[0]); double base = el.getLatitude().getValue(); final double DELTA = (xRange[1] - xRange[0]) / 100; if (Double.isNaN(base)) { outerloop: for (double y = yRange[0]; y < yRange[1]; y = y + DELTA) { for (double x = xRange[0]; x < xRange[1]; x = x + DELTA) { el = getEarthLocation(x, y, zRange[0]); base = el.getLatitude().getValue(); if ( !Double.isNaN(base)) { break outerloop; } } } } return base; } /** * Calculate maximum latitude according to VisAD. * * @return the lat top */ private double calcLatTop() { final double LAT_MAX = 90; double[] xRange = xMap.getRange(); double[] yRange = yMap.getRange(); double[] zRange = (zMap != null) ? zMap.getRange() : new double[] { 0, 0 }; EarthLocation el = getEarthLocation(xRange[0], yRange[1], zRange[0]); double top = el.getLatitude().getValue(); final double DELTA = (xRange[1] - xRange[0]) / 100; if (Double.isNaN(top)) { outerloop: for (double y = yRange[1]; y > yRange[0]; y = y - DELTA) { * @return true, if is south pole for (double x = xRange[0]; x < xRange[1]; x = x + DELTA) { el = getEarthLocation(x, y, zRange[0]); top = el.getLatitude().getValue(); if ( !Double.isNaN(top)) { break outerloop; } } } } return (top > LAT_MAX) ? LAT_MAX : top; } /** * Calculate minimum longitude according to VisAD. * * @return the lon base */ private double calcLonBase() { double[] xRange = xMap.getRange(); double[] yRange = yMap.getRange(); double[] zRange = (zMap != null) ? zMap.getRange() : new double[] { 0, 0 }; EarthLocation el = getEarthLocation(xRange[0], yRange[0], zRange[0]); double base = el.getLongitude().getValue(); final double DELTA = (xRange[1] - xRange[0]) / 100; if (Double.isNaN(base)) { outerloop: for (double x = xRange[0]; x < xRange[1]; x = x + DELTA) { for (double y = yRange[0]; y < yRange[1]; y = y + DELTA) { el = getEarthLocation(x, y, zRange[0]); base = el.getLongitude().getValue(); if ( !Double.isNaN(base)) { break outerloop; } } } } return base; } /** * Calculate maximum longitude according to VisAD. * * @return the lon top */ private double calcLonTop() { double[] xRange = xMap.getRange(); double[] yRange = yMap.getRange(); double[] zRange = (zMap != null) ? zMap.getRange() : new double[] { 0, 0 }; EarthLocation el = getEarthLocation(xRange[1], yRange[1], zRange[0]); double top = el.getLongitude().getValue(); final double DELTA = (xRange[1] - xRange[0]) / 100; if (Double.isNaN(top)) { outerloop: for (double x = xRange[1]; x > xRange[0]; x = x - DELTA) { for (double y = yRange[1]; y > yRange[0]; y = y - DELTA) { el = getEarthLocation(x, y, zRange[0]); top = el.getLongitude().getValue(); if ( !Double.isNaN(top)) { break outerloop; } } } } return top; } /** * Set the lon scales * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void makeLonScales() throws VisADException, RemoteException { setDisplayInactive(); if (lonScale != null) { lonScale.setVisible(getLonScaleInfo().isVisible()); lonScale.setLabelRelief(getLonScaleInfo().isLabelRelief()); if (isSouthPole()) { updateSouthPoleLonScale(lonScale); } else { updateLonScale(lonScale); } } setDisplayActive(); } /** * Checks if current project is over the South Pole. * private boolean isSouthPole() { double[] xRange = xMap.getRange(); double[] yRange = yMap.getRange(); double[] zRange = (zMap != null) ? zMap.getRange() : new double[] { 0, 0 }; EarthLocation ll = getEarthLocation(xRange[0], yRange[0], zRange[0]); EarthLocation lr = getEarthLocation(xRange[1], yRange[0], zRange[0]); EarthLocation ur = getEarthLocation(xRange[1], yRange[1], zRange[0]); EarthLocation ul = getEarthLocation(xRange[0], yRange[1], zRange[0]); return ((ll.getLongitude().getValue() < ul.getLongitude().getValue()) && (ul.getLongitude().getValue() < ur.getLongitude().getValue()) && (ur.getLongitude() .getValue() < lr.getLongitude().getValue())); } /** * Method to update lat scale. * * @param scale AxisScale to update * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void updateLatScale(AxisScale scale) throws VisADException, RemoteException { final int LAT_MIN = -90; final int LAT_MAX = 90; final double DELTA = 0.0001; double bottomLat = calcLatBase(); double topLat = calcLatTop(); Hashtable labelTable = new Hashtable(); double base = Misc.parseNumber(getLatScaleInfo().getBaseLabel()); List majorTicks = new ArrayList(); int minorTickInc = getLatScaleInfo().getMinorDivision(); List minorTicks = new ArrayList(); double inc = Misc.parseNumber(getLatScaleInfo().getIncrement()); // In case user inputs something bogus. if ((base < LAT_MIN) || (base > LAT_MAX)) { base = bottomLat; } outerloop: for (double i = base; i < topLat; i += inc / minorTickInc) { if (i < bottomLat) { // Latitudes that are not in this range are not visible. continue; } EarthLocationTuple elt = new EarthLocationTuple(i, calcLonBase(), 0); double[] values = newtonLat(elt, 0); for (int j = 0; j < values.length; j++) { if (Double.isNaN(values[j])) { continue outerloop; } } Double d = round(values[1], 3, BigDecimal.ROUND_HALF_UP); double mm = (i - base) % inc; if ((mm < DELTA) || (mm > (inc - DELTA))) { // Must account for numerical leeway. majorTicks.add(d); labelTable.put(d, CoordinateFormat.formatLatitude(i, latScaleInfo.getCoordFormat())); } else { minorTicks.add(d); } } finalizeAxis(scale, getLatScaleInfo().getLabel(), labelTable, majorTicks, minorTicks, getLatScaleInfo().getFont()); } /** * Method to update lon scale. * * @param scale AxisScale to update * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void updateLonScale(AxisScale scale) throws VisADException, RemoteException { final int MAX_LON = 360; final int MIN_LON = -180; final double DELTA = 0.0001; double leftLon = calcLonBase(); double rightLon = calcLonTop(); boolean isMeridianCross = leftLon > rightLon; Hashtable labelTable = new Hashtable(); double base = Misc.parseNumber(getLonScaleInfo().getBaseLabel()); List majorTicks = new ArrayList(); int minorTickInc = getLonScaleInfo().getMinorDivision(); List minorTicks = new ArrayList(); double inc = Misc.parseNumber(getLonScaleInfo().getIncrement()); List increment = new LinkedList(); //Need to normalize the base in some circumstances if (rightLon - base > 360) { base += 360; } // Northern hemisphere meridian cross leftLon = isMeridianCross ? leftLon - MAX_LON : leftLon; // In case user inputs something bogus. if ((base < MIN_LON) || (base > MAX_LON)) { base = leftLon; } for (double i = base; i < rightLon; i += inc / minorTickInc) { if (i < leftLon) { // Longitudes that are not in this range are not visible. continue; } increment.add(i); } outerloop: for (Double i : increment) { EarthLocationTuple elt = new EarthLocationTuple(calcLatBase(), i, 0); double[] values = newtonLon(elt, 0); for (int j = 0; j < values.length; j++) { if (Double.isNaN(values[j])) { continue outerloop; } } Double d = round(values[0], 3, BigDecimal.ROUND_HALF_UP); double mm = (i - base) % inc; if ((mm < DELTA) || (mm > (inc - DELTA))) { // Must account for numerical leeway. majorTicks.add(d); labelTable.put(d, CoordinateFormat.formatLongitude(i, lonScaleInfo.getCoordFormat(), lonScaleInfo.isUse360())); } else { minorTicks.add(d); } } finalizeAxis(scale, getLonScaleInfo().getLabel(), labelTable, majorTicks, minorTicks, getLonScaleInfo().getFont()); } /** * Update south pole lon scale. * * @param scale the scale * @throws VisADException the vis ad exception * @throws RemoteException the remote exception */ private void updateSouthPoleLonScale(AxisScale scale) throws VisADException, RemoteException { final int MAX_LON = 360; final int MIN_LON = -180; final double DELTA = 0.0001; double leftLon = calcLonBase(); double rightLon = calcLonTop() - MAX_LON; double bottomLat = calcLatBase(); Hashtable labelTable = new Hashtable(); double base = Misc.parseNumber(getLonScaleInfo().getBaseLabel()); List majorTicks = new ArrayList(); int minorTickInc = getLonScaleInfo().getMinorDivision(); List minorTicks = new ArrayList(); double inc = Misc.parseNumber(getLonScaleInfo().getIncrement()); List increment = new LinkedList(); // In case the user enters something bogus if ((base < MIN_LON) || (base > MAX_LON)) { base = leftLon; } for (double i = base; i > rightLon; i -= inc / minorTickInc) { if (i > leftLon) { // Longitudes that are not in this range are not visible. continue; } increment.add(i); } for (Double i : increment) { EarthLocationTuple elt = new EarthLocationTuple(bottomLat, i, 0); double[] values = newtonLon(elt, 0); Double d = new Double(values[0]); double mm = (base - i) % inc; if ((mm < DELTA) || (mm > (inc - DELTA))) { // Must account for numerical leeway. majorTicks.add(d); labelTable.put(d, CoordinateFormat.formatLongitude(i, lonScaleInfo.getCoordFormat(), lonScaleInfo.isUse360())); } else { minorTicks.add(d); } } finalizeAxis(scale, getLonScaleInfo().getLabel(), labelTable, majorTicks, minorTicks, getLonScaleInfo().getFont()); } /** * Finalize axis labeling. * * @param scale the scale * @param title the title * @param labelTable the label table * @param majorTicks the major ticks * @param minorTicks the minor ticks * @param axisFont the axis font * @throws VisADException the vis ad exception */ private void finalizeAxis( AxisScale scale, String title, Hashtable labelTable, List majorTicks, List minorTicks, Font axisFont) throws VisADException { double[] mjt = new double[majorTicks.size()]; double[] mnt = new double[minorTicks.size()]; for (int i = 0; i < mjt.length; i++) { mjt[i] = majorTicks.get(i); } for (int i = 0; i < mnt.length; i++) { mnt[i] = minorTicks.get(i); } scale.setAutoComputeTicks(false); scale.setSnapToBox(true); scale.setMajorTicks(mjt); scale.setMinorTicks(mnt); scale.setTitle(title); scale.setLabelTable(labelTable); scale.setTicksVisible(true); scale.setMajorTickSpacing(0); scale.setMinorTickSpacing(0); if ((axisFont != null) && axisFont.getName().equals( FontSelector.DEFAULT_FONT.getName())) { scale.setFont((Font) null); scale.setLabelSize(axisFont.getSize()); } else { scale.setFont(axisFont); } } /** * Numerically arriving at the screen coordinates needed for the intersection between the wire frame axis * and the latitude. * * @param elt the earth location tuple. * @param cnt must maintain a count in case of numerical problems * @return the spatial coordinates * @throws RemoteException the remote exception * @throws VisADException the vis ad exception */ private double[] newtonLat(EarthLocationTuple elt, int cnt) throws RemoteException, VisADException { final double DELTA = 0.02; final double DELTA2 = 0.001; final int MAX_CNT = 10; double[] values = getSpatialCoordinates(elt).getValues(); values[0] = -1; EarthLocation el = getEarthLocation(values); // We are in no man's land so must adjust until we find Earth. while (Double.isNaN(el.getLatitude().getValue())) { values[0] = values[0] + DELTA2; el = getEarthLocation(values); if (values[0] > 1) { return new double[] { Double.NaN, Double.NaN, Double.NaN }; } } double diff = elt.getLatitude().getValue() - el.getLatitude().getValue(); if ((Math.abs(diff) < DELTA) || (cnt > MAX_CNT)) { // cnt > 10 in case solution does not converge. Safety valve. return getSpatialCoordinates(el).getValues(); } else { EarthLocationTuple t = new EarthLocationTuple(el.getLatitude().getValue() + diff, el.getLongitude().getValue(), 0); return newtonLat(t, ++cnt); } } /** * Numerically arriving at the screen coordinates needed for the intersection between the wire frame axis * and the longitude. * * @param elt the earth location tuple. * @param cnt must maintain a count in case of numerical problems * @return the spatial coordinates * @throws RemoteException the remote exception * @throws VisADException the vis ad exception */ private double[] newtonLon(EarthLocationTuple elt, int cnt) throws RemoteException, VisADException { final double DELTA = 0.02; final double DELTA2 = 0.001; final int MAX_CNT = 10; double[] values = getSpatialCoordinates(elt).getValues(); values[1] = -1; EarthLocation el = getEarthLocation(values); // We are in no man's land so must adjust until we find Earth. while (Double.isNaN(el.getLatitude().getValue())) { values[1] = values[1] + DELTA2; el = getEarthLocation(values); if (values[1] > 1) { return new double[] { Double.NaN, Double.NaN, Double.NaN }; } } double diff = el.getLongitude().getValue() - elt.getLongitude().getValue(); if ((Math.abs(diff) < DELTA) || (cnt > MAX_CNT)) { // cnt > 10 in case solution does not converge. Safety valve. return getSpatialCoordinates(elt).getValues(); } else { EarthLocationTuple t = new EarthLocationTuple(el.getLatitude().getValue(), el.getLongitude().getValue() - diff, 0); return newtonLon(t, ++cnt); } } /** * Method to update the properties of an AxisScale * * @param scale AxisScale to update * @param title Title * @param maxmin max/min limits of axis * @param bottom value for lower limit * @param top value for upper limit * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void updateVertScale(AxisScale scale, String title, double[] maxmin, double bottom, double top) throws VisADException, RemoteException { scale.setVisible(getVerticalRangeVisible()); scale.setSnapToBox(true); scale.setTitle(title); Hashtable labelTable = new Hashtable(); labelTable.put(new Double(maxmin[0]), labelFormat.format(bottom)); labelTable.put(new Double(maxmin[1]), labelFormat.format(top)); scale.setLabelTable(labelTable); scale.setTickBase(maxmin[0]); scale.setMajorTickSpacing(Math.abs(maxmin[1] - maxmin[0])); scale.setMinorTickSpacing(Math.abs(maxmin[1] - maxmin[0])); } /** * Set the vertical axis scale * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void makeVerticalScale() throws VisADException, RemoteException { if (verticalScale == null) { return; } setDisplayInactive(); double[] zRange = zMap.getRange(); String title = verticalParameter.getName() + "(" + verticalRangeUnit.getIdentifier() + ")"; updateVertScale(verticalScale, title, zRange, minVerticalRange, maxVerticalRange); /* * verticalScale.setSnapToBox(true); * verticalScale.setTitle( * Hashtable labelTable = new Hashtable(); * labelTable.put(new Double(zRange[0]), * labelFormat.format(minVerticalRange)); * labelTable.put(new Double(zRange[1]), * labelFormat.format(maxVerticalRange)); * verticalScale.setLabelTable(labelTable); * verticalScale.setTickBase(zRange[0]); * verticalScale.setMajorTickSpacing(Math.abs(zRange[1]-zRange[0])); * verticalScale.setMinorTickSpacing(Math.abs(zRange[1]-zRange[0])); */ setDisplayActive(); } /** * Define the set of spatial scalar maps that this display will * use. Every time a new projection is set, a new set of DisplayTypes * is created with a coordinate system for transposing between * projection space and xyz space. The mappings are: *

    *
  • RealType.Latitude -> getDisplayLatitudeType() *
  • RealType.Longitude -> getDisplayLongitudeType() *
  • RealType.Altitude -> getDisplayAltitudeType() *
* This is called on construction of the display or with every rebuild. * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ private void setSpatialScalarMaps() throws VisADException, RemoteException { setDisplayInactive(); ScalarMapSet mapSet = new ScalarMapSet(); if (latitudeMap != null) { removeScalarMap(latitudeMap); } latitudeMap = new ScalarMap(RealType.Latitude, displayLatitudeType); mapSet.add(latitudeMap); latitudeMap.setRangeByUnits(); latitudeMap.setScaleEnable(true); if (longitudeMap != null) { removeScalarMap(longitudeMap); } longitudeMap = new ScalarMap(RealType.Longitude, displayLongitudeType); mapSet.add(longitudeMap); longitudeMap.setRangeByUnits(); longitudeMap.setScaleEnable(true); if (getDisplayMode() == MODE_3D) { ScalarMapSet newVertMaps = new ScalarMapSet(); if (verticalMapSet.size() > 0) { for (Iterator iter = verticalMapSet.iterator(); iter.hasNext(); ) { ScalarType r = ((ScalarMap) iter.next()).getScalar(); ScalarMap newMap = new ScalarMap(r, displayAltitudeType); newMap.setScaleEnable(true); if (r.equals(RealType.Altitude)) { altitudeMap = newMap; } newVertMaps.add(newMap); } } else { // add Altitude at least altitudeMap = new ScalarMap(RealType.Altitude, displayAltitudeType); altitudeMap.setScaleEnable(true); newVertMaps.add(altitudeMap); } removeScalarMaps(verticalMapSet); verticalMapSet.clear(); verticalMapSet.add(newVertMaps); setVerticalRange(minVerticalRange, maxVerticalRange); setVerticalRangeUnit(verticalRangeUnit); mapSet.add(verticalMapSet); } if ( !init) { xMap = new ScalarMap(RealType.XAxis, Display.XAxis); xMap.setRange(-1.0, 1.0); mapSet.add(xMap); xMap.setScaleEnable(true); lonScale = xMap.getAxisScale(); yMap = new ScalarMap(RealType.YAxis, Display.YAxis); yMap.setRange(-1.0, 1.0); mapSet.add(yMap); yMap.setScaleEnable(true); latScale = yMap.getAxisScale(); if (getDisplayMode() == MODE_3D) { zMap = new ScalarMap(RealType.ZAxis, Display.ZAxis); zMap.setRange(-1.0, 1.0); mapSet.add(zMap); zMap.setScaleEnable(true); verticalScale = zMap.getAxisScale(); } init = true; } addScalarMaps(mapSet); setDisplayActive(); } /** * Add a new mapping of this type to the vertical coordinate * * @param newVertType RealType of map * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ public void addVerticalMap(RealType newVertType) throws VisADException, RemoteException { if (getDisplayMode() == MODE_3D) { Unit u = newVertType.getDefaultUnit(); if ( !(Unit.canConvert(u, CommonUnit.meter) || Unit.canConvert( u, GeopotentialAltitude.getGeopotentialMeter()))) { throw new VisADException("Unable to handle units of " + newVertType); } ScalarMap newMap = new ScalarMap(newVertType, getDisplayAltitudeType()); setVerticalMapUnit(newMap, verticalRangeUnit); newMap.setRange(minVerticalRange, maxVerticalRange); verticalMapSet.add(newMap); addScalarMaps(verticalMapSet); } } /** * Remove a new mapping of this type to the vertical coordinate * * @param vertType RealType of map * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ public void removeVerticalMap(RealType vertType) throws VisADException, RemoteException { if (getDisplayMode() == MODE_3D) { ScalarMapSet sms = new ScalarMapSet(); for (Iterator iter = verticalMapSet.iterator(); iter.hasNext(); ) { ScalarMap s = (ScalarMap) iter.next(); if (((RealType) s.getScalar()).equals(vertType)) { sms.add(s); } } if ( !(sms.size() == 0)) { verticalMapSet.remove(sms); removeScalarMaps(sms); } } } /** * Set the Unit of the vertical range * * @param newUnit unit of range * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ public void setVerticalRangeUnit(Unit newUnit) throws VisADException, RemoteException { super.setVerticalRangeUnit(newUnit); if ((newUnit != null) && Unit.canConvert(newUnit, CommonUnit.meter)) { verticalMapSet.setVerticalUnit(newUnit); verticalRangeUnit = newUnit; } makeVerticalScale(); } /** * Sets the lat scale info. * * @param axisScaleInfo the new lat scale info * @throws RemoteException the remote exception * @throws VisADException the vis ad exception */ public void setLatScaleInfo(LatLonAxisScaleInfo axisScaleInfo) throws RemoteException, VisADException { this.latScaleInfo = axisScaleInfo; makeLatScales(); } /** * Sets the lon scale info. * * @param axisScaleInfo the new lon scale info * @throws RemoteException the remote exception * @throws VisADException the vis ad exception */ public void setLonScaleInfo(LatLonAxisScaleInfo axisScaleInfo) throws RemoteException, VisADException { this.lonScaleInfo = axisScaleInfo; makeLonScales(); } /** * Gets the lat scale info. * * @return the lat scale info */ public LatLonAxisScaleInfo getLatScaleInfo() { // The 2nd null check is kludgy, but sometimes LatLonAxisScaleInfo //deserialization will have problems in which case all fields //are null. if ((latScaleInfo == null) || (latScaleInfo.getBaseLabel() == null)) { LatLonAxisScaleInfo lsi = new LatLonAxisScaleInfo(); latScaleInfo = lsi; lsi.setLabel("Latitude"); lsi.setIncrement(10 + ""); lsi.setMinorDivision(1); lsi.setVisible(true); lsi.setCoordFormat(LatLonAxisScaleInfo.COORD_FORMATS[0]); lsi.setUse360(latScaleInfo.isUse360()); double base = calcLatBase(); double end = calcLatTop(); double inc = Math.abs(end - base) / 10d; lsi.setIncrement(makeIncrementNice(inc) + ""); base = (Math.floor(base / 10)) * 10; // Make base nice (i.e. multiple of 10) base = (base < -90) ? -90 : base; base = (base > 90) ? 90 : base; latScaleInfo.setBaseLabel(base + ""); } return latScaleInfo; } /** * Make increment nice for the user. * * @param inc the not nice inc * @return the nice increment */ private double makeIncrementNice(double inc) { //Somewhat arbitrary double[] niceNums = { 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 15, 20, 25, 30, 40, 50, 60, 70, 80 }; for (int i = 0; i < niceNums.length; i++) { if (inc < niceNums[i]) { return niceNums[i]; } } return 10; } /** * Gets the lon scale info. * * @return the lon scale info */ public LatLonAxisScaleInfo getLonScaleInfo() { // The 2nd null check is kludgy, but sometimes LatLonAxisScaleInfo //deserialization will have problems in which case all fields //are null. if ((lonScaleInfo == null) || (lonScaleInfo.getBaseLabel() == null)) { LatLonAxisScaleInfo lsi = new LatLonAxisScaleInfo(); lonScaleInfo = lsi; lsi.setLabel("Longitude"); lsi.setMinorDivision(1); lsi.setVisible(true); lsi.setCoordFormat(LatLonAxisScaleInfo.COORD_FORMATS[0]); lsi.setUse360(false); double base = calcLonBase(); double end = calcLonTop(); double inc; if (isSouthPole()) { inc = Math.abs((end - 360) - base) / 10d; } else { inc = Math.abs(end - ((base > end) ? base - 360 : base)) / 10d; } // Must deal with meridian lsi.setIncrement(makeIncrementNice(inc) + ""); // base = (Math.floor(base / 10)) * 10; // Make base nice (i.e. multiple of 10) base = (base < -180) ? -180 : base; base = (base > 180) ? (base - 360) : base; lonScaleInfo.setBaseLabel((isSouthPole() ? Math.floor(base) : Math.ceil(base)) + ""); } return lonScaleInfo; } /** * Rounding convenience method. * * @param unrounded the unrounded * @param precision the precision * @param roundingMode the rounding mode * @return the rounded value */ private static double round(double unrounded, int precision, int roundingMode) { BigDecimal bd = new BigDecimal(unrounded); BigDecimal rounded = bd.setScale(precision, roundingMode); return rounded.doubleValue(); } /** * Set the range of the vertical coordinate * * @param min minimum value for vertical axis * @param max maximum value for vertical axis * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ public void setVerticalRange(double min, double max) throws VisADException, RemoteException { super.setVerticalRange(min, max); verticalMapSet.setVerticalRange(min, max); minVerticalRange = min; maxVerticalRange = max; makeVerticalScale(); } /** * Get the range of the vertical coordinate (Altitude) * * @return array of {min, max} range. */ public double[] getVerticalRange() { ScalarMap vertMap = getAltitudeMap(); return (vertMap != null) ? vertMap.getRange() : new double[] { minVerticalRange, maxVerticalRange }; } /** * Sets the cursor range from center property. * * @param range The cursor range from center. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ protected void setCursorRange(Real range) throws VisADException, RemoteException { Real oldRange = cursorRange; cursorRange = range; firePropertyChange(CURSOR_RANGE, oldRange, cursorRange); } /** * Gets the cursor range from center property. * * @return The currently-selected range. May be * null. */ public Real getCursorRange() { return cursorRange; } /** * Sets the cursor bearing (degrees) from center property. * This implementation uses a great circle distance. * * @param bearing The cursor bearing from center. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ protected void setCursorBearing(Real bearing) throws VisADException, RemoteException { Real oldBearing = cursorBearing; cursorBearing = bearing; firePropertyChange(CURSOR_BEARING, oldBearing, cursorBearing); } /** * Gets the cursor bearing from center property. * * @return The currently-selected bearing. May be * null. */ public Real getCursorBearing() { return cursorBearing; } /** * Set the view for 3D. The views are based on the original display * as follows: *
     *                        NORTH
     *                      _________
     *                    W |       | E
     *                    E |  TOP  | A
     *                    S |       | S
     *                    T |_______| T
     *                        SOUTH
     * 
* @param view one of the static view fields (NORTH_VIEW, SOUTH_VIEW, .. * etc). */ public void setView(int view) { if (getDisplayMode() != MODE_3D) { return; } } /** * Accessor method for the DisplayLatitudeType (i.e., what * RealType.Latitude is mapped to). * * @return the DisplayRealType that RealType.Latitude is mapped to */ public DisplayRealType getDisplayLatitudeType() { return displayLatitudeType; } /** * Accessor method for the DisplayLongitudeType (i.e., what * RealType.Longitude is mapped to). * * @return the DisplayRealType that RealType.Longitude is mapped to */ public DisplayRealType getDisplayLongitudeType() { return displayLongitudeType; } /** * Accessor method for the DisplayAltitudeType (i.e., what * RealType.Altitude is mapped to). * * @return the DisplayRealType that RealType.Altitude is mapped to */ public DisplayRealType getDisplayAltitudeType() { return displayAltitudeType; } /** * Accessor method for the vertical coordinate ScalarMap (i.e., what return cs; * getDisplayAltitudeType is mapped from). * @return the ScalarMap that the vertical coordinate is mapped to */ protected ScalarMap getAltitudeMap() { return altitudeMap; } /** * Define the map projection using a Projection interface * * @param projection Projection to use * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ /** public void setMapProjection(ProjectionImpl projection) throws VisADException, RemoteException { setMapProjection(new ProjectionCoordinateSystem(projection), true); } /** * Define the map projection using a MapProjection type CoordinateSystem * * @param mapProjection map projection coordinate system * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ public void setMapProjection(MapProjection mapProjection) throws VisADException, RemoteException { setMapProjection(mapProjection, true); } /** * Define the map projection using a MapProjection type CoordinateSystem * * @param mapProjection map projection coordinate system * * @param resetDisplayProjMatrix yes/no change the current VisAD Display projection matrix * when the map projection is changed. * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ public void setMapProjection(MapProjection mapProjection, boolean resetDisplayProjMatrix) throws VisADException, RemoteException { if (mapProjection.equals(this.mapProjection)) { return; } this.mapProjection = mapProjection; coordinateSystem = makeCoordinateSystem(mapProjection); // Need to reset these for new projection. this.latScaleInfo = null; this.lonScaleInfo = null; resetMapParameters(resetDisplayProjMatrix); EarthLocation el = getEarthLocation(0, 0, 0); centerLLP.set(el.getLatitude().getValue(CommonUnit.degree), el.getLongitude().getValue(CommonUnit.degree)); } /** * Get the MapProjection that defines the xy mapping of this * MapProjectionDisplay. * * @return MapProjection being used. */ public MapProjection getMapProjection() { return mapProjection; } /** * Set the map area from the projection rectangle * * @param mapArea map area as lat/lon lines * * @throws RemoteException problem setting remote data * @throws VisADException problem creating VisAD data object */ public void setMapArea(ProjectionRect mapArea) throws VisADException, RemoteException { if (coordinateSystem == null) { throw new VisADException("Navigation hasn't been set yet"); } // System.out.println("Map Area = " + mapArea); MapProjection project = ((MapProjection3DAdapter) coordinateSystem).getMapProjection(); // get the corners in latlon coords ProjectionPoint ppMax = mapArea.getMaxPoint(); ProjectionPoint ppMin = mapArea.getMinPoint(); // System.out.println("ppMax:" + ppMax); // System.out.println("ppMin:" + ppMin); float[][] values = new float[2][2]; values[0][0] = (float) ppMax.getY(); values[0][1] = (float) ppMin.getY(); values[1][0] = (float) ppMax.getX(); values[1][1] = (float) ppMin.getX(); // values = project.toReference(values); Gridded2DSet region = new Gridded2DSet(RealTupleType.LatitudeLongitudeTuple, values, 2); setMapRegion(region); } * Set the map region to be displayed. The MathType of the domain * of the set must be either RealTupleType.SpatialCartesian2DTuple, * RealTupleType.SpatialEarth2DTuple, or * RealTupleType.LatitudeLongitudeTuple. * * @param region Gridded2DSet containing the range of for the axis. * * @throws VisADException invalid domain or null set * @throws RemoteException Couldn't create a remote object */ public void setMapRegion(Gridded2DSet region) throws VisADException, RemoteException { if (region == null) { throw new VisADException("Region can't be null"); } if (region.isMissing()) { return; } // Check the type. We need to work in XYZ coordinates Gridded2DSet xyRegion; RealTupleType regionType = ((SetType) region.getType()).getDomain(); if (regionType.equals(RealTupleType.SpatialCartesian2DTuple)) { xyRegion = region; } else if (regionType.equals(RealTupleType.SpatialEarth2DTuple) || regionType.equals( RealTupleType.LatitudeLongitudeTuple)) { // transform to x/y int latIndex = regionType.equals(RealTupleType.LatitudeLongitudeTuple) ? 0 : 1; int lonIndex = (latIndex == 0) ? 1 : 0; float[][] values = region.getSamples(true); float xy[][] = new float[3][values[0].length]; xy[0] = values[latIndex]; xy[1] = values[lonIndex]; xy = coordinateSystem.toReference(xy); values[0] = xy[0]; values[1] = xy[1]; xyRegion = new Gridded2DSet(RealTupleType.SpatialCartesian2DTuple, values, 2); } else { throw new VisADException("Invalid domain for region " + regionType); } // System.out.println(xyRegion); // Okay, now we have our region, let's get cracking // First, let's figure out our component size. Dimension d = getComponent().getSize(); // if running the isl non interactively, d is not assigned, the component is // one layer deeper. if ((d.width == 0) || (d.height == 0)) { JPanel jp = (JPanel) getComponent(); d = jp.getComponent(0).getSize(); } // System.out.println("Component size = " + d); int componentCenterX = d.width / 2; int componentCenterY = d.height / 2; /* * System.out.println( * "Component Center point = " + * componentCenterX +","+componentCenterY); */ // Now let's get the MouseBehavior so we can get some display coords MouseBehavior behavior = csUnits = cs.getCoordinateSystemUnits(); getDisplay().getDisplayRenderer().getMouseBehavior(); ProjectionControl proj = getDisplay().getProjectionControl(); double[] aspect = getDisplayAspect(); // Misc.printArray("aspect", aspect); // We have to figure the component coordinates of the region. // To do this, we calculate the number of display units per pixel // in the x and y. This logic comes from visad.MouseHelper. // Basically, we find out the current matrix, how much we should // scale, translate and rotate, and then apply the new matrix. double[] center_ray = behavior.findRay(componentCenterX, componentCenterY).position; // Misc.printArray("center_ray", center_ray); double[] center_ray_x = behavior.findRay(componentCenterX + 1, componentCenterY).position; // Misc.printArray("center_ray_x", center_ray_x); double[] center_ray_y = behavior.findRay(componentCenterX, componentCenterY + 1).position; // Misc.printArray("center_ray_y", center_ray_y); /* * TODO: test more to see if this makes a difference. The * rubber band box is actually at the Z=-1 position * double[] center_ray = getRayPositionAtZ(behavior.findRay(componentCenterX, * componentCenterY), -1); * Misc.printArray("center_ray @ -1", center_ray); * double[] center_ray_x = getRayPositionAtZ(behavior.findRay(componentCenterX + 1, * componentCenterY), -1); * Misc.printArray("center_ray_x @ -1", center_ray_x); * double[] center_ray_y = getRayPositionAtZ(behavior.findRay(componentCenterX, * componentCenterY + 1),-1); * Misc.printArray("center_ray_y @ -1", center_ray_y); */ double[] tstart = proj.getMatrix(); // printMatrix("tstart", tstart); double[] rot = new double[3]; double[] scale = new double[3]; double[] trans = new double[3]; behavior.instance_unmake_matrix(rot, scale, trans, tstart); double stx = scale[0]; double sty = scale[1]; // System.out.println("stx = " + stx); // System.out.println("sty = " + sty); double[] trot = behavior.make_matrix(rot[0], rot[1], rot[2], scale[0], scale[1], scale[2], // scale[0], 0.0, 0.0, 0.0); // printMatrix("trot", trot); // WLH 17 Aug 2000 double[] xmat = behavior.make_translate(center_ray_x[0] - center_ray[0], center_ray_x[1] - center_ray[1], center_ray_x[2] - center_ray[2]); // xmat = behavior.multiply_matrix(mult, xmat); double[] ymat = behavior.make_translate(center_ray_y[0] - center_ray[0], center_ray_y[1] - center_ray[1], center_ray_y[2] - center_ray[2]); // ymat = behavior.multiply_matrix(mult, ymat); double[] xmatmul = behavior.multiply_matrix(trot, xmat); double[] ymatmul = behavior.multiply_matrix(trot, ymat); /* * printMatrix("xmat", xmat); * printMatrix("ymat", ymat); * printMatrix("xmatmul", xmatmul); * printMatrix("ymatmul", ymatmul); */ behavior.instance_unmake_matrix(rot, scale, trans, xmatmul); double xmul = trans[0]; behavior.instance_unmake_matrix(rot, scale, trans, ymatmul); double ymul = trans[1]; // System.out.println("Multipliers = " + xmul + "," + ymul); // make sure that we don't divide by 0 (happens if display // component is not yet on screen if ((Math.abs(xmul) > 0) && (Math.abs(ymul) > 0)) { // Now we can get the box coordinates in component space float[] lows = xyRegion.getLow(); float[] highs = xyRegion.getHi(); float boxCenterDisplayX = (highs[0] + lows[0]) / 2.0f; float boxCenterDisplayY = (highs[1] + lows[1]) / 2.0f; /* * System.out.println( * "Box center point (XY) = " + * boxCenterDisplayX+","+boxCenterDisplayY); */ // Check to see if the box is big enough (at least 5x5 pixels) // *** might want to ammend this to be a percentage of // component size **** int boxWidth = (int) Math.abs((highs[0] - lows[0]) / xmul * stx); int boxHeight = (int) Math.abs((highs[1] - lows[1]) / ymul * sty); /* * System.out.println( * "Box size = " + boxWidth +"," + boxHeight); */ if ((boxWidth > 5) && (boxHeight > 5)) { int boxCenterX = componentCenterX + (int) ((boxCenterDisplayX - center_ray[0]) / xmul); int boxCenterY = componentCenterY - (int) ((boxCenterDisplayY - center_ray[1]) / ymul); /* * System.out.println( * "Box Center point = " + boxCenterX +","+boxCenterY); */ double transx = (componentCenterX - boxCenterX) * xmul * stx; double transy = (componentCenterY - boxCenterY) * ymul * sty; /* * System.out.println("transx = " + transx + * " transy = " + transy); */ // Now calculate zoom factor double zoom = (boxWidth / boxHeight >= d.width / d.height) ? d.getWidth() / boxWidth : d.getHeight() / boxHeight; // zoom out if this is a bigger region than the component // System.out.println("zoom factor = " + zoom); translate(transx, -transy); zoom(zoom); } } } /** * Scale vertical values using the range of the vertical * scalar map. * * @param altValues altitude map values * * @return z values (may transform in place); */ public float[] scaleVerticalValues(float[] altValues) { if (getAltitudeMap() == null) { return altValues; } return getAltitudeMap().scaleValues(altValues, false); } /** * Set up the DisplayTupleType. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ private void setDisplayTypes() throws VisADException, RemoteException { if (coordinateSystem == null) { System.out.println("coordSys == null"); displayLatitudeType = Display.YAxis; displayLongitudeType = Display.XAxis; displayAltitudeType = Display.ZAxis; displayTupleType = Display.DisplaySpatialCartesianTuple; } else { int myInstance; synchronized (INSTANCE_MUTEX) { myInstance = instance++; } // We need to set the range on the longitude axis // to be equal to the range of the projection if the // X coordinate is approximately equal to Longitude. // For now, this is only LatLonProjections and TrivalMP's double minLon = -360; double maxLon = 360.; double centerLon = 0; MapProjection mp = ((MapProjection3DAdapter) coordinateSystem) .getMapProjection(); boolean isLatLon = false; adjustLons = true; // HACK, HACK, HACK, HACK if (mp instanceof ProjectionCoordinateSystem) { ProjectionImpl proj = ((ProjectionCoordinateSystem) mp).getProjection(); if (proj instanceof LatLonProjection) { Rectangle2D r2d2 = mp.getDefaultMapArea(); minLon = r2d2.getX(); maxLon = minLon + r2d2.getWidth(); centerLon = minLon + r2d2.getWidth() / 2; isLatLon = true; } } else if (mp instanceof TrivialMapProjection) { Rectangle2D r2d2 = mp.getDefaultMapArea(); minLon = r2d2.getX(); maxLon = minLon + r2d2.getWidth(); centerLon = minLon + r2d2.getWidth() / 2; // isLatLon = true; // TODO: figure out this a little more. } else if (mp instanceof AREACoordinateSystem) { // minLon = -180; // maxLon = 180.; adjustLons = false; } // TODO: figure out what we should be doing here. use360 = !((minLon >= -185) && (maxLon <= 185)); if ((isLatLon && !use360) // lat/lon projections in +/-180 rang || !mp.isXYOrder() // Vis5D || ((minLon > -360) && (minLon < 0) && (maxLon > 180))) { // AVN grids adjustLons = false; } /* * System.out.println("DisplayProjectionLon" + myInstance * + " has range of " + minLon + " to " + maxLon * + " with center at " + centerLon * + "; use360 = " + use360 + "; adjust lons = " * + adjustLons); */ displayLatitudeType = new DisplayRealType("ProjectionLat" + myInstance, true, -90.0, 90.0, 0.0, CommonUnit.degree); displayLongitudeType = new DisplayRealType("ProjectionLon" + myInstance, true, minLon, maxLon, centerLon, CommonUnit.degree); double defaultZ = (getDisplayMode() != MODE_3D) ? 0.0 : -1.0; displayAltitudeType = new DisplayRealType("ProjectionAlt" + myInstance, true, -1.0, 1.0, defaultZ, null); displayTupleType = new DisplayTupleType(new DisplayRealType[] { displayLatitudeType, displayLongitudeType, displayAltitudeType }, coordinateSystem); } setSpatialScalarMaps(); } /** * Create the adapter coordinate system using a MapProjection * * @param mapProjection * * @return the adapted coordinate system * * @throws VisADException null mapProjection or other VisAD problem */ private CoordinateSystem makeCoordinateSystem(MapProjection mapProjection) throws VisADException { if (mapProjection == null) { throw new VisADException("MapProjection can't be null"); } CoordinateSystem cs = new MapProjection3DAdapter(mapProjection); } /** * Handles a change to the cursor position. * * @throws VisADException VisAD failure. * @throws RemoteException Java RMI failure. */ protected void cursorMoved() throws VisADException, RemoteException { double[] c = getDisplay().getDisplayRenderer().getCursor(); updateLocation( getEarthLocation(getDisplay().getDisplayRenderer().getCursor())); } /** * Update lat/lon/alt properties with the EarthLocation. * * @param el EarthLocation to use. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ protected void updateLocation(EarthLocation el) throws VisADException, RemoteException { super.updateLocation(el); cursorLLP.set(el.getLatitude().getValue(CommonUnit.degree), el.getLongitude().getValue(CommonUnit.degree)); Bearing.calculateBearing(centerLLP, cursorLLP, workBearing); setCursorRange(new Real(CURSOR_RANGE_TYPE, workBearing.getDistance())); setCursorBearing(new Real(CURSOR_BEARING_TYPE, workBearing.getAngle())); } /** * _more_ * * @return _more_ */ public LatLonPointImpl getCenterLLP() { return centerLLP; } /** * Handles a change in the position of the mouse-pointer. * * @param x x mouse position * @param y y mouse position * * @throws RemoteException Java RMI problem * @throws UnitException Unit conversion problem * @throws VisADException VisAD problem */ protected void pointerMoved(int x, int y) throws UnitException, VisADException, RemoteException { /* * Convert from (pixel, line) Java Component coordinates to (latitude, * longitude) */ /* * TODO: figure out why this won't work * updateLocation(getEarthLocation(getSpatialCoordinatesFromScreen(x, * y))); */ // TODO: java2d // if(true) return; VisADRay ray = getRay(x, y); EarthLocation el = getEarthLocation(ray.position[0], ray.position[1], ray.position[2]); updateLocation(el); } /** * Get the earth location from the VisAD xyz coodinates * * @param x x * @param y y * @param z z * @param setZToZeroIfOverhead If in the overhead view then set Z to 0 * * @return corresponding EarthLocation */ public EarthLocation getEarthLocation(double x, double y, double z, boolean setZToZeroIfOverhead) { EarthLocationTuple value = null; try { float[][] numbers = coordinateSystem.fromReference(new float[][] { new float[] { (float) (x) }, new float[] { (float) (y) }, new float[] { (float) (z) } }); Real lat = new Real(RealType.Latitude, getScaledValue(latitudeMap, numbers[0][0]), csUnits[0]); Real lon = new Real(RealType.Longitude, altValues getScaledValue(longitudeMap, numbers[1][0]), csUnits[1]); Real alt = null; if (getDisplayMode() == MODE_3D) { if (setZToZeroIfOverhead && Arrays.equals(getProjectionMatrix(), getSavedProjectionMatrix()) /* * && (alt * .getValue( * getVerticalRangeUnit()) != altitudeMap * .getRange()[0]) */ ) { alt = new Real(RealType.Altitude, altitudeMap.getRange()[0], getVerticalRangeUnit()); } else { alt = new Real(RealType.Altitude, getScaledValue(altitudeMap, numbers[2][0])); } } else { alt = new Real(RealType.Altitude, 0); } value = new EarthLocationTuple(lat, lon, alt); } catch (VisADException e) { e.printStackTrace(); } // can't happen catch (RemoteException e) { e.printStackTrace(); } // can't happen return value; } /** * Returns the spatial (XYZ) coordinates of the particular EarthLocation * * @param el earth location to transform * * @return RealTuple of display coordinates. */ public RealTuple getSpatialCoordinates(EarthLocation el) { if (el == null) { throw new NullPointerException( "MapProjectionDisplay.getSpatialCoorindate(): " + "null input EarthLocation"); } RealTuple spatialLoc = null; try { double[] xyz = getSpatialCoordinates(el, null); spatialLoc = new RealTuple(RealTupleType.SpatialCartesian3DTuple, xyz); } catch (VisADException e) { e.printStackTrace(); } // can't happen catch (RemoteException e) { e.printStackTrace(); } // can't happen return spatialLoc; } /** * Returns the spatial (XYZ) coordinates of the particular EarthLocation * * @param el earth location to transform * @param xyz The in value to set. May be null. * @param altitude altitude value * * @return xyz array * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ public double[] getSpatialCoordinates(EarthLocation el, double[] xyz, double altitude) throws VisADException, RemoteException { float[] altValues; if ((altitudeMap != null) && (el.getAltitude() != null) && !(Double.isNaN(altitude))) { altValues = altitudeMap.scaleValues(new double[] { altitude }); } else { altValues = new float[] { 0f }; } float[][] temp = coordinateSystem.toReference(new float[][] { latitudeMap.scaleValues(new double[] { el.getLatitude().getValue(CommonUnit.degree) }), longitudeMap.scaleValues(new double[] { el.getLongitude().getValue(CommonUnit.degree) }), }); if (xyz == null) { xyz = new double[3]; } xyz[0] = temp[0][0]; xyz[1] = temp[1][0]; xyz[2] = temp[2][0]; return xyz; } /** * Method called to reset all the map parameters after a change. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ private void resetMapParameters() throws VisADException, RemoteException { resetMapParameters(true); } /** * Method called to reset all the map parameters after a change. * * @param resetDisplayProjMatrix yes/no change the current VisAD Display projection matrix * when the map projection is changed. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ private void resetMapParameters(boolean resetDisplayProjMatrix) throws VisADException, RemoteException { setDisplayInactive(); setDisplayTypes(); if (resetDisplayProjMatrix) { resetProjection(); // make it the right size setAspect(); } makeLatScales(); makeLonScales(); setDisplayActive(); } /** * Set the aspect for the display. */ private void setAspect() { Rectangle2D mapArea = mapProjection.getDefaultMapArea(); double ratio = mapArea.getWidth() / mapArea.getHeight(); double[] myaspect = getDisplayAspect(); try { if (ratio == 1.0) { // height == width setDisplayAspect((getDisplayMode() != MODE_2D) ? new double[] { 1.0, 1.0, myaspect[2] } : new double[] { 1.0, 1.0 }); /* * guess this doesn't matter, just use the other * } else if (ratio < 1) { // height > width * setDisplayAspect( * (getDisplayMode() != MODE_2D) * ? new double[] { 1.0, ratio, myaspect[2] } * : new double[] { 1.0, ratio }); */ } else { // width > height setDisplayAspect((getDisplayMode() != MODE_2D) ? new double[] { ratio, 1.0, myaspect[2] } : new double[] { ratio, 1.0 }); } // Misc.printArray("aspect", getDisplayAspect()); } catch (Exception excp) { System.out.println( "MapProjectionDisplay.setDisplayAspect() got exception: " + excp); } } /** * Make the default projection. * * @return Default projectcion * * @throws VisADException couldn't create MapProjection */ protected static MapProjection makeDefaultMapProjection() throws VisADException { return new ProjectionCoordinateSystem( new LatLonProjection("Default Projection", // Use this to make the aspect ratio correct new ProjectionRect(-180., -180., 180., 180.))); } /** * Get the display coordinate system that turns lat/lon/alt to * x/y/z * * @return the coordinate system (may be null) */ public CoordinateSystem getDisplayCoordinateSystem() { return coordinateSystem; } /** * test by running java ucar.unidata.view.geoloc.MapProjectionDisplay * * @param args include an argument for a 3D display * * @throws Exception problem creating the display */ public static void main(String[] args) throws Exception { JFrame frame = new JFrame(); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); final MapProjectionDisplay navDisplay = ((args.length > 0) && visad.util.Util.canDoJava3D()) ? MapProjectionDisplay.getInstance(NavigatedDisplay.MODE_3D) : (visad.util.Util.canDoJava3D() == true) ? MapProjectionDisplay.getInstance(NavigatedDisplay.MODE_2Din3D) : MapProjectionDisplay.getInstance(NavigatedDisplay.MODE_2D); /* * double[]aspect = { 1.0, 1.0, 0.4 }; * navDisplay.setDisplayAspect((navDisplay.getDisplayMode() == NavigatedDisplay.MODE_2D) * ? new double[]{ 1.0, 1.0 } * : aspect); */ DisplayImpl display = (DisplayImpl) navDisplay.getDisplay(); navDisplay.setBackground(Color.white); navDisplay.setForeground(Color.black); // navDisplay.setCursorStringOn(true); MapLines mapLines = new MapLines("maplines"); URL mapSource = // new URL("ftp://www.ssec.wisc.edu/pub/visad-2.0/OUTLSUPW"); navDisplay.getClass().getResource("/auxdata/maps/OUTLSUPW"); try { BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource); mapLines.setMapLines(mapAdapter.getData()); mapLines.setColor(java.awt.Color.black); navDisplay.addDisplayable(mapLines); } catch (Exception excp) { System.out.println("Can't open map file " + mapSource); System.out.println(excp); } JPanel panel = new JPanel(new GridLayout(1, 0)); JButton pushme = new JButton("Map Projection Manager"); panel.add(pushme); frame.getContentPane().add(panel, BorderLayout.NORTH); ViewpointControl vpc = new ViewpointControl(navDisplay); panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.add(navDisplay.getComponent(), BorderLayout.CENTER); panel.add((navDisplay.getDisplayMode() == navDisplay.MODE_3D) ? (Component) ucar.unidata.util.GuiUtils.topCenterBottom( vpc.getToolBar(JToolBar.VERTICAL), new NavigatedDisplayToolBar( navDisplay, JToolBar.VERTICAL), GuiUtils.filler()) : (Component) new NavigatedDisplayToolBar(navDisplay, JToolBar.VERTICAL), BorderLayout.WEST); JPanel readout = new JPanel(); readout.add(new NavigatedDisplayCursorReadout(navDisplay)); readout.add(new RangeAndBearingReadout(navDisplay)); panel.add(readout, BorderLayout.SOUTH); navDisplay.draw(); frame.getContentPane().add(panel, BorderLayout.CENTER); if (navDisplay.getDisplayMode() == navDisplay.MODE_3D) { JMenuBar mb = new JMenuBar(); mb.add(vpc.getMenu()); frame.setJMenuBar(mb); } System.out.println("Using rectilinear projection"); final ProjectionManager pm = new ProjectionManager(); pm.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { if (e.getPropertyName().equals("ProjectionImpl")) { try { navDisplay.setMapProjection( (ProjectionImpl) e.getNewValue()); } catch (Exception exp) { System.out.println(exp); } } } }); /** pushme.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { pm.show(); } }); /* * EarthLocationTuple elt = new EarthLocationTuple(40,-105, 8000); * SelectorPoint sp = new SelectorPoint("foo", elt); * sp.setFixed(false,false,true); * sp.setColor(Color.black); * navDisplay.addDisplayable(sp); */ navDisplay.getDisplay().getGraphicsModeControl().setScaleEnable(true); frame.pack(); frame.setVisible(true); /* * if (navDisplay.getDisplayMode() != navDisplay.MODE_2D) { * final CurveDrawer cd = * //new CurveDrawer(RealType.XAxis, RealType.YAxis, * new CurveDrawer(RealType.Latitude, RealType.Longitude, * InputEvent.SHIFT_MASK); * cd.addAction(new ActionImpl("Curve Drawer") { * int numSets = 0; * public void doAction () * throws VisADException, RemoteException { * UnionSet curves = (UnionSet) cd.getData(); * if (curves != null) { * int num = curves.getSets().length; * if (num != numSets) { * numSets = num; * System.out.println("Data has type " + * curves.getType() + * " length = " + numSets); * } * } * } * }); * cd.setColor(Color.red); * cd.setLineWidth(2.0f); * navDisplay.addDisplayable(cd); * } */ /* * System.out.println("Setting projection GOES-E satellite"); * Thread.sleep(5000); * AreaAdapter aa = * new AreaAdapter( * "adde://adde.ucar.edu/imagedata?group=rtimages&descr=edfloater-i&compress=true"); * navDisplay.setMapProjection((MapProjection) aa.getCoordinateSystem()); * EarthLocation el = * navDisplay.getEarthLocation(new double[] {0.0, 0.0, 1.0}); * System.out.println("location = " + el); * System.out.println(navDisplay.getSpatialCoordinates(el)); */ } /** * An adapter for visad.georef.MapProjection coordinate systems (ie: * ones with * a reference of Lat/Lon). Allows for the conversion from * lat/lon to Display.DisplaySpatialCartesianTuple (XYZ). * Altitude (z) values are held constant. */ protected class MapProjection3DAdapter extends CoordinateSystem implements InverseLinearScaledCS { /** index of the latitude coordinate */ private final int latIndex; /** index of the longitude coordinate */ private final int lonIndex; /** map projection for xy -> lat/lon transformations */ private final MapProjection mapProjection; /** X offset */ private final double offsetX; /** Y offset */ private final double offsetY; /** X scaling factor */ private final double scaleX; /** Y scaling factor */ private final double scaleY; /** the coordinate system */ private CoordinateSystem theCoordinateSystem; /** index of the x coordinate */ private final int xIndex; /** index of the y coordinate */ private final int yIndex; * Construct a new CoordinateSystem which uses a MapProjection for * the transformations between x,y and lat/lon. * * @param mapProjection CoordinateSystem that transforms from xy * in the data space to lat/lon. * @exception VisADException can't create the necessary VisAD object */ public MapProjection3DAdapter(MapProjection mapProjection) throws VisADException { super(Display.DisplaySpatialCartesianTuple, new Unit[] { CommonUnit.degree, CommonUnit.degree, null }); this.mapProjection = mapProjection; this.theCoordinateSystem = new CachingCoordinateSystem(this.mapProjection); latIndex = mapProjection.getLatitudeIndex(); lonIndex = mapProjection.getLongitudeIndex(); if (mapProjection.isXYOrder()) { xIndex = 0; yIndex = 1; } else { xIndex = 1; yIndex = 0; } /* * System.out.println("latIndex = " + latIndex + * " lonIndex = " + lonIndex + * " xIndex = " + xIndex + * " yIndex = " + yIndex); */ java.awt.geom.Rectangle2D bounds = mapProjection.getDefaultMapArea(); /* * System.out.println("X = " + bounds.getX() + * " Y = "+ bounds.getY() + * " width = "+ bounds.getWidth() + * " height = "+ bounds.getHeight()); */ scaleX = bounds.getWidth() / 2.0; scaleY = bounds.getHeight() / 2.0; offsetX = bounds.getX() + scaleX; offsetY = bounds.getY() + scaleY; /* * System.out.println("scaleX = " + scaleX + * " scaleY = "+ scaleY + * " offsetX = "+ offsetX + * " offsetY = "+ offsetY); */ } /** * Transform latitude/longitude/altitude value to XYZ * * @param latlonalt array of latitude, longitude, altitude values * * @return array of display xyz values. * * @throws VisADException can't create the necessary VisAD object */ public double[][] toReference(double[][] latlonalt) throws VisADException { if ((latlonalt == null) || (latlonalt[0].length < 1)) { return latlonalt; } int numpoints = latlonalt[0].length; call1("toReference(d)", numpoints); double[][] t2 = new double[2][]; t2[latIndex] = latlonalt[0]; t2[lonIndex] = latlonalt[1]; /* */ if (adjustLons) { t2[lonIndex] = (use360) ? GeoUtils.normalizeLongitude360(latlonalt[1]) // ? latlonalt[1] : GeoUtils.normalizeLongitude(latlonalt[1]); } t2 = theCoordinateSystem.fromReference(t2); if (t2 == null) { throw new VisADException( "MapProjection.toReference: " + "Can't do (lat,lon) to (x,y) transformation"); } double x, y; double[] t2x = t2[xIndex]; double[] t2y = t2[yIndex]; for (int i = 0; i < numpoints; i++) { if (Double.isNaN(t2x[i]) || Double.isNaN(t2y[i])) { x = Double.NaN; y = Double.NaN; } else { x = (t2x[i] - offsetX) / scaleX; y = (t2y[i] - offsetY) / scaleY; } latlonalt[0][i] = x; latlonalt[1][i] = y; } call2("toReference(d)", numpoints); return latlonalt; } /** * debug * * @param msg debug * @param numpoints debug */ void call1(String msg, int numpoints) { if (numpoints > 10000) { // Misc.printStack(msg,5,null); Trace.call1("MapProjectionDisplay." + msg, " numpoints = " + numpoints); } } /** * debug * * @param msg debug * @param numpoints debug */ void call2(String msg, int numpoints) { if (numpoints > 10000) { Trace.call2("MapProjectionDisplay." + msg); } } /** * Transform latitude/longitude/altitude value to XYZ * * @param latlonalt array of latitude, longitude, altitude values * * @return array of display xyz values. * * @throws VisADException can't create the necessary VisAD object */ public float[][] toReference(float[][] latlonalt) throws VisADException { if ((latlonalt == null) || (latlonalt[0].length < 1)) { return latlonalt; } int numpoints = latlonalt[0].length; call1("toReference(f)", numpoints); float[][] t2 = new float[2][]; t2[latIndex] = latlonalt[0]; t2[lonIndex] = latlonalt[1]; /* */ if (adjustLons) { t2[lonIndex] = (use360) ? GeoUtils.normalizeLongitude360(latlonalt[1]) // ? latlonalt[1] : GeoUtils.normalizeLongitude(latlonalt[1]); } // call1("mapProjection.fromReference", numpoints); // Trace.msg("MapProjectionDisplay. class=" + mapProjection.getClass().getName()); t2 = theCoordinateSystem.fromReference(t2); // call2("mapProjection.fromReference", numpoints); if (t2 == null) { throw new VisADException( "MapProjection.toReference: " + "Can't do (lat,lon) to (x,y) transformation"); } float x, y; float[] t2ax = t2[xIndex]; float[] t2ay = t2[yIndex]; for (int i = 0; i < numpoints; i++) { float t2x = t2ax[i]; float t2y = t2ay[i]; if ((t2x != t2x) || (t2y != t2y)) { x = Float.NaN; y = Float.NaN; } else { x = (float) ((t2x - offsetX) / scaleX); y = (float) ((t2y - offsetY) / scaleY); } latlonalt[0][i] = x; latlonalt[1][i] = y; } call2("toReference(f)", numpoints); return latlonalt; } /** * Transform display XYZ values to latitude/longitude/altitude * * @param xyz array of Display.DisplaySpatialCartesianTuple XYZ values * @return array of display lat/lon/alt values. * * @throws VisADException can't create the necessary VisAD object */ public double[][] fromReference(double[][] xyz) throws VisADException { if ((xyz == null) || (xyz[0].length < 1)) { return xyz; } int numpoints = xyz[0].length; call1("fromReference(d)", numpoints); for (int i = 0; i < numpoints; i++) { if (Double.isNaN(xyz[0][i]) || Double.isNaN(xyz[0][i])) { continue; } xyz[0][i] = (xyz[0][i] * scaleX + offsetX); xyz[1][i] = (xyz[1][i] * scaleY + offsetY); } double[][] t2 = new double[][] { xyz[xIndex], xyz[yIndex] }; t2 = theCoordinateSystem.toReference(t2); if (t2 == null) { throw new VisADException( "MapProjection.toReference: " + "Can't do (x,y) to (lat,lon) transformation"); } xyz[0] = t2[latIndex]; xyz[1] = t2[lonIndex]; /* */ if (adjustLons) { xyz[1] = (use360) ? GeoUtils.normalizeLongitude360(t2[lonIndex]) // ? t2[lonIndex] : GeoUtils.normalizeLongitude(t2[lonIndex]); } call2("fromReference(d)", numpoints); return xyz; } /** * Transform display XYZ values to latitude/longitude/altitude * * @param xyz array of Display.DisplaySpatialCartesianTuple XYZ values * @return array of display lat/lon/alt values. * * @throws VisADException can't create the necessary VisAD object */ public float[][] fromReference(float[][] xyz) throws VisADException { if ((xyz == null) || (xyz[0].length < 1)) { return xyz; } int numpoints = xyz[0].length; call1("fromReference(f)", numpoints); for (int i = 0; i < numpoints; i++) { if (Float.isNaN(xyz[0][i]) || Float.isNaN(xyz[0][i])) { continue; } xyz[0][i] = (float) (xyz[0][i] * scaleX + offsetX); xyz[1][i] = (float) (xyz[1][i] * scaleY + offsetY); } float[][] t2 = new float[][] { xyz[xIndex], xyz[yIndex] }; t2 = theCoordinateSystem.toReference(t2); if (t2 == null) { throw new VisADException( "MapProjection.toReference: " + "Can't do (x,y) to (lat,lon) transformation"); } xyz[0] = t2[latIndex]; xyz[1] = t2[lonIndex]; /* */ if (adjustLons) { xyz[1] = (use360) ? GeoUtils.normalizeLongitude360(t2[lonIndex]) // ? t2[lonIndex] : GeoUtils.normalizeLongitude(t2[lonIndex]); } call2("fromReference(f)", numpoints); return xyz; } /** * See if this is equal to the object in question. * * @param obj object in question. * * @return true if they are equal. The two objects are equal if * their MapProjections are equal. */ public boolean equals(Object obj) { if ( !(obj instanceof MapProjection3DAdapter)) { return false; } MapProjection3DAdapter that = (MapProjection3DAdapter) obj; return (that.mapProjection).equals(mapProjection); } /** * Return the MapProjection being used by the CoordinateSystem. * * @return the MapProjection used in this instance */ public MapProjection getMapProjection() { return mapProjection; } /** * Get the scale * * @return the scale (x,y) */ public double[] getScale() { return new double[] { scaleX, scaleY }; } /** * Get the offset * * @return the offset (x_off, y_off) */ public double[] getOffset() { return new double[] { offsetX, offsetY }; } /** * Get the inverted coordinate system * * @return the inverted coordinate system */ public CoordinateSystem getInvertedCoordinateSystem() { return this.mapProjection; } } } ======= /* * Copyright 1997-2013 Unidata Program Center/University Corporation for * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307, * support@unidata.ucar.edu. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or (at /** * your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package ucar.unidata.view.geoloc; import ucar.unidata.geoloc.Bearing; import ucar.unidata.geoloc.LatLonPointImpl; import ucar.unidata.geoloc.ProjectionImpl; import ucar.unidata.geoloc.ProjectionPoint; import ucar.unidata.geoloc.ProjectionRect; import ucar.unidata.geoloc.projection.LatLonProjection; import ucar.unidata.ui.FontSelector; import ucar.unidata.util.GuiUtils; import ucar.unidata.util.LogUtil; import ucar.unidata.util.Misc; import ucar.unidata.util.Trace; import ucar.visad.GeoUtils; import ucar.visad.ProjectionCoordinateSystem; import ucar.visad.display.MapLines; import ucar.visad.display.ScalarMapSet; import ucar.visad.quantities.CommonUnits; import ucar.visad.quantities.GeopotentialAltitude; import visad.AxisScale; import visad.CachingCoordinateSystem; import visad.CommonUnit; import visad.CoordinateSystem; import visad.Display; import visad.DisplayImpl; import visad.DisplayRealType; import visad.DisplayTupleType; import visad.Gridded2DSet; import visad.InverseLinearScaledCS; import visad.KeyboardBehavior; import visad.MouseBehavior; import visad.ProjectionControl; import visad.Real; import visad.RealTuple; import visad.RealTupleType; import visad.RealType; import visad.ScalarMap; import visad.ScalarType; import visad.SetType; import visad.Unit; import visad.UnitException; import visad.VisADException; import visad.VisADRay; import visad.data.mcidas.AREACoordinateSystem; import visad.data.mcidas.BaseMapAdapter; import visad.georef.EarthLocation; import visad.georef.EarthLocationTuple; import visad.georef.MapProjection; import visad.georef.TrivialMapProjection; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.GraphicsDevice; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.geom.Rectangle2D; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.math.BigDecimal; import java.net.URL; import java.rmi.RemoteException; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JMenuBar; import javax.swing.JPanel; import javax.swing.JToolBar; * Provides a navigated VisAD DisplayImpl for displaying data. * The Projection or MapProjection provides the transformation from * lat/lon space to xy space. There are three modes that can be used * with this display - MODE_3D (Java 3D), MODE_2Din3D (2D in Java 3D), * MODE_2D (Java 2D). Performance is better in Java 3D modes. In the 3D * mode, RealType.Altitude is mapped to the display Z axis.

* Any displayable data must be able to map to RealType.Latitude, * RealType.Longitude and/or RealType.Altitude.

* * @author Don Murray */ public abstract class MapProjectionDisplay extends NavigatedDisplay { /** /** * The name of the bearing from center property. */ public static final String CURSOR_BEARING = "cursorBearing"; /** * The name of the range from center property. */ public static final String CURSOR_RANGE = "cursorRange"; /** instance counter */ private static int instance = 0; /** instance locking mutex */ private static Object INSTANCE_MUTEX = new Object(); /** logging category */ private static LogUtil.LogCategory log_ = LogUtil.getLogInstance(MapProjectionDisplay.class.getName()); /** * flag for forcing 2D */ public static boolean force2D = false; /** * The range from center RealType. */ public static RealType CURSOR_RANGE_TYPE = RealType.getRealType("Cursor_Range", CommonUnits.KILOMETER); /** * The bearing from center RealType. */ public static RealType CURSOR_BEARING_TYPE = RealType.getRealType("Cursor_Bearing", CommonUnit.degree); /** ScalarMapf for altitude -> displayAltitudeType */ private ScalarMap altitudeMap = null; /** coordinate system units */ private Unit[] csUnits = null; /** y axis scale */ private AxisScale latScale = null; /** ScalarMapf for latitude -> displayLatitudeType */ private ScalarMap latitudeMap = null; /** x axis scale */ private AxisScale lonScale = null; /** ScalarMapf for longitude -> displayLongitudeType */ private ScalarMap longitudeMap = null; /** default maximum vertical range value */ private double maxVerticalRange = 16000; /** default minimum vertical range value */ private double minVerticalRange = 0; /** vertical axis scale */ private AxisScale verticalScale = null; /** ScalarMap to Display.XAxis */ private ScalarMap xMap = null; /** ScalarMap to Display.YAxis */ private ScalarMap yMap = null; /** ScalarMap to Display.ZAxis */ private ScalarMap zMap = null; /** bearing class for bearing calculations */ private Bearing workBearing = new Bearing(); /** default vertical range unit */ private Unit verticalRangeUnit = CommonUnit.meter; /** Vertical type */ private RealType verticalParameter = RealType.Altitude; /** Set of vertical maps */ private VerticalMapSet verticalMapSet = new VerticalMapSet(); /** use 0-360 for longitude range */ private boolean use360 = true; /** number format for axis labels */ DecimalFormat labelFormat = new DecimalFormat("####0.0"); /** flag for whether we've been initialized */ private boolean init = false; /** cursor location for bearing calculations */ private LatLonPointImpl cursorLLP = new LatLonPointImpl(); /** centerpoint for bearing calculations */ private LatLonPointImpl centerLLP = new LatLonPointImpl(); /** flag for adjusting lons or not */ private boolean adjustLons = false; /** The coordinate system for the display */ private CoordinateSystem coordinateSystem; * The cursor altitude. * @serial */ private volatile Real cursorBearing; /** * The cursor altitude. * @serial */ private volatile Real cursorRange; /** The display's Altitude DisplayRealType */ private DisplayRealType displayAltitudeType; /** The display's Latitude DisplayRealType */ private DisplayRealType displayLatitudeType; /** The display's Longitude DisplayRealType */ private DisplayRealType displayLongitudeType; /** the display tuple type */ private DisplayTupleType displayTupleType; /** The lat scale info */ private LatLonAxisScaleInfo latScaleInfo; /** The lon scale info */ private LatLonAxisScaleInfo lonScaleInfo; /** The MapProjection */ private MapProjection mapProjection; /** * Constructs an instance with the specified MapProjection */ protected MapProjectionDisplay() {} /** * Constructs an instance with the specified MapProjection * CoordinateSystem and display. * * @param projection map projection CS * @param display display to use * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ protected MapProjectionDisplay(MapProjection projection, DisplayImpl display) throws VisADException, RemoteException { super(display); // mapProjection = projection; // coordinateSystem = makeCoordinateSystem(projection); setMapProjection(projection); initializeClass(); } /** * Initializes an instance with the specified MapProjection * CoordinateSystem and display. * * @param projection map projection CS * @param display display to use * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ protected void init(MapProjection projection, DisplayImpl display) throws VisADException, RemoteException { super.init(display); setMapProjection(projection); initializeClass(); } /** * Set up the display. Any additional work should be done in * a subclass's intializeClass() method, which should call * super.initializeClass() first. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ protected void initializeClass() throws VisADException, RemoteException { super.initializeClass(); setDisplayTypes(); } /** * Get an instance of a MapProjectionDisplay using the mode specified * and the default projection. * * @param mode mode to use * * @return a MapProjectionDisplay * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ public static MapProjectionDisplay getInstance(int mode) throws VisADException, RemoteException { return getInstance(null, mode); } /** * Get an instance of a MapProjectionDisplay using the mode specified * and the MapProjection. * * @param mode mode to use * @param p initial MapProjection for display * * @return a MapProjection display of the correct mode and projection * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ public static MapProjectionDisplay getInstance(MapProjection p, int mode) throws VisADException, RemoteException { return getInstance(p, mode, false, null); } /** * Get an instance of a MapProjectionDisplay using the mode specified * and the MapProjection. * * @param p map projection * @param mode mode * @param offscreen true if offscreen * @param dimension dimension of display * * @return the instance * * @throws RemoteException Java RMI problem * @throws VisADException problem creating the display or some component */ public static MapProjectionDisplay getInstance(MapProjection p, int mode, boolean offscreen, Dimension dimension) throws VisADException, RemoteException { return getInstance(p, mode, offscreen, dimension, null); } /** * Get an instance of a MapProjectionDisplay using the mode specified * and the MapProjection. * * @param p map projection * @param mode mode * @param offscreen true if offscreen * @param dimension dimension of display * @param screen screen to display it on * * @return the instance * * @throws RemoteException Java RMI problem * @throws VisADException problem creating the display or some component */ public static MapProjectionDisplay getInstance(MapProjection p, int mode, boolean offscreen, Dimension dimension, GraphicsDevice screen) throws VisADException, RemoteException { if (p == null) { Trace.call1("MapProjectionDisplay.getInstance:makeProjection"); p = makeDefaultMapProjection(); Trace.call2("MapProjectionDisplay.getInstance:makeProjection"); } if (((mode == MODE_3D) || (mode == MODE_2Din3D)) && !force2D) { Trace.call1( "MapProjectionDisplay.getInstance:new MapProjectionDisplayJ3D"); MapProjectionDisplay mpd = new MapProjectionDisplayJ3D(p, mode, offscreen, dimension, screen); Trace.call2( "MapProjectionDisplay.getInstance:new MapProjectionDisplayJ3D"); return mpd; } else { return new MapProjectionDisplayJ2D(p); } } /** * Destroy this class */ public void destroy() { super.destroy(); } // private List keyboardBehaviors; /** * Add a KeyboardBehavior to this class * * @param behavior behavior to add */ public abstract void addKeyboardBehavior(KeyboardBehavior behavior); /** * Set the make lat scale * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void makeLatScales() throws VisADException, RemoteException { setDisplayInactive(); if (latScale != null) { latScale.setVisible(getLatScaleInfo().isVisible()); latScale.setLabelRelief(getLatScaleInfo().isLabelRelief()); updateLatScale(latScale); } setDisplayActive(); } /** * Calculate minimum latitude according to VisAD. * * @return the lat base */ private double calcLatBase() { double[] xRange = xMap.getRange(); double[] yRange = yMap.getRange(); double[] zRange = (zMap != null) ? zMap.getRange() : new double[] { 0, 0 }; EarthLocation el = getEarthLocation(xRange[0], yRange[0], zRange[0]); double base = el.getLatitude().getValue(); final double DELTA = (xRange[1] - xRange[0]) / 100; if (Double.isNaN(base)) { outerloop: for (double y = yRange[0]; y < yRange[1]; y = y + DELTA) { for (double x = xRange[0]; x < xRange[1]; x = x + DELTA) { el = getEarthLocation(x, y, zRange[0]); base = el.getLatitude().getValue(); if ( !Double.isNaN(base)) { break outerloop; } } } } return base; } /** * Calculate maximum latitude according to VisAD. * * @return the lat top */ private double calcLatTop() { final double LAT_MAX = 90; double[] xRange = xMap.getRange(); double[] yRange = yMap.getRange(); double[] zRange = (zMap != null) ? zMap.getRange() : new double[] { 0, 0 }; EarthLocation el = getEarthLocation(xRange[0], yRange[1], zRange[0]); double top = el.getLatitude().getValue(); final double DELTA = (xRange[1] - xRange[0]) / 100; if (Double.isNaN(top)) { outerloop: for (double y = yRange[1]; y > yRange[0]; y = y - DELTA) { for (double x = xRange[0]; x < xRange[1]; x = x + DELTA) { el = getEarthLocation(x, y, zRange[0]); top = el.getLatitude().getValue(); if ( !Double.isNaN(top)) { break outerloop; } } } } return (top > LAT_MAX) ? LAT_MAX : top; } /** * Calculate minimum longitude according to VisAD. * * @return the lon base */ private double calcLonBase() { double[] xRange = xMap.getRange(); double[] yRange = yMap.getRange(); double[] zRange = (zMap != null) ? zMap.getRange() : new double[] { 0, 0 }; EarthLocation el = getEarthLocation(xRange[0], yRange[0], zRange[0]); double base = el.getLongitude().getValue(); final double DELTA = (xRange[1] - xRange[0]) / 100; if (Double.isNaN(base)) { outerloop: for (double x = xRange[0]; x < xRange[1]; x = x + DELTA) { for (double y = yRange[0]; y < yRange[1]; y = y + DELTA) { el = getEarthLocation(x, y, zRange[0]); base = el.getLongitude().getValue(); if ( !Double.isNaN(base)) { break outerloop; } } } } return base; } /** * Calculate maximum longitude according to VisAD. * * @return the lon top */ private double calcLonTop() { double[] xRange = xMap.getRange(); double[] yRange = yMap.getRange(); double[] zRange = (zMap != null) ? zMap.getRange() : new double[] { 0, 0 }; EarthLocation el = getEarthLocation(xRange[1], yRange[1], zRange[0]); double top = el.getLongitude().getValue(); final double DELTA = (xRange[1] - xRange[0]) / 100; if (Double.isNaN(top)) { outerloop: for (double x = xRange[1]; x > xRange[0]; x = x - DELTA) { for (double y = yRange[1]; y > yRange[0]; y = y - DELTA) { el = getEarthLocation(x, y, zRange[0]); top = el.getLongitude().getValue(); if ( !Double.isNaN(top)) { break outerloop; } } } } return top; } /** * Set the lon scales * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void makeLonScales() throws VisADException, RemoteException { setDisplayInactive(); if (lonScale != null) { lonScale.setVisible(getLonScaleInfo().isVisible()); lonScale.setLabelRelief(getLonScaleInfo().isLabelRelief()); if (isSouthPole()) { updateSouthPoleLonScale(lonScale); } else { updateLonScale(lonScale); } } setDisplayActive(); } /** * Checks if current project is over the South Pole. * * @return true, if is south pole */ private boolean isSouthPole() { double[] xRange = xMap.getRange(); double[] yRange = yMap.getRange(); double[] zRange = (zMap != null) ? zMap.getRange() : new double[] { 0, 0 }; EarthLocation ll = getEarthLocation(xRange[0], yRange[0], zRange[0]); EarthLocation lr = getEarthLocation(xRange[1], yRange[0], zRange[0]); EarthLocation ur = getEarthLocation(xRange[1], yRange[1], zRange[0]); EarthLocation ul = getEarthLocation(xRange[0], yRange[1], zRange[0]); return ((ll.getLongitude().getValue() < ul.getLongitude().getValue()) && (ul.getLongitude().getValue() < ur.getLongitude().getValue()) && (ur.getLongitude() .getValue() < lr.getLongitude().getValue())); } /** * Method to update lat scale. * * @param scale AxisScale to update * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void updateLatScale(AxisScale scale) throws VisADException, RemoteException { final int LAT_MIN = -90; final int LAT_MAX = 90; final double DELTA = 0.0001; double bottomLat = calcLatBase(); double topLat = calcLatTop(); Hashtable labelTable = new Hashtable(); double base = getLatScaleInfo().getBaseLabel(); List majorTicks = new ArrayList(); int minorTickInc = getLatScaleInfo().getMinorDivision(); List minorTicks = new ArrayList(); double inc = getLatScaleInfo().getIncrement(); // In case user inputs something bogus. if ((base < LAT_MIN) || (base > LAT_MAX)) { base = bottomLat; } outerloop: for (double i = base; i < topLat; i += inc / minorTickInc) { if (i < bottomLat) { // Latitudes that are not in this range are not visible. continue; } EarthLocationTuple elt = new EarthLocationTuple(i, calcLonBase(), 0); double[] values = newtonLat(elt, 0); for (int j = 0; j < values.length; j++) { if (Double.isNaN(values[j])) { continue outerloop; } } Double d = round(values[1], 3, BigDecimal.ROUND_HALF_UP); double mm = (i - base) % inc; if ((mm < DELTA) || (mm > (inc - DELTA))) { // Must account for numerical leeway. majorTicks.add(d); labelTable.put(d, CoordinateFormat.formatLatitude(i, latScaleInfo.getCoordFormat())); } else { minorTicks.add(d); } } finalizeAxis(scale, getLatScaleInfo().getLabel(), labelTable, majorTicks, minorTicks, getLatScaleInfo().getFont()); } /** * Method to update lon scale. * * @param scale AxisScale to update * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void updateLonScale(AxisScale scale) throws VisADException, RemoteException { final int MAX_LON = 360; final int MIN_LON = -180; final double DELTA = 0.0001; double leftLon = calcLonBase(); double rightLon = calcLonTop(); boolean isMeridianCross = leftLon > rightLon; Hashtable labelTable = new Hashtable(); double base = getLonScaleInfo().getBaseLabel(); List majorTicks = new ArrayList(); int minorTickInc = getLonScaleInfo().getMinorDivision(); List minorTicks = new ArrayList(); double inc = getLonScaleInfo().getIncrement(); List increment = new LinkedList(); //Need to normalize the base in some circumstances if (rightLon - base > 360) { base += 360; } // Northern hemisphere meridian cross leftLon = isMeridianCross ? leftLon - MAX_LON : leftLon; // In case user inputs something bogus. if ((base < MIN_LON) || (base > MAX_LON)) { base = leftLon; } for (double i = base; i < rightLon; i += inc / minorTickInc) { if (i < leftLon) { // Longitudes that are not in this range are not visible. continue; } increment.add(i); } outerloop: for (Double i : increment) { EarthLocationTuple elt = new EarthLocationTuple(calcLatBase(), i, 0); double[] values = newtonLon(elt, 0); for (int j = 0; j < values.length; j++) { if (Double.isNaN(values[j])) { continue outerloop; } } Double d = round(values[0], 3, BigDecimal.ROUND_HALF_UP); double mm = (i - base) % inc; if ((mm < DELTA) || (mm > (inc - DELTA))) { // Must account for numerical leeway. majorTicks.add(d); labelTable.put(d, CoordinateFormat.formatLongitude(i, lonScaleInfo.getCoordFormat(), lonScaleInfo.isUse360())); } else { minorTicks.add(d); } } finalizeAxis(scale, getLonScaleInfo().getLabel(), labelTable, majorTicks, minorTicks, getLonScaleInfo().getFont()); } /** * Update south pole lon scale. * * @param scale the scale * @throws VisADException the vis ad exception * @throws RemoteException the remote exception */ private void updateSouthPoleLonScale(AxisScale scale) throws VisADException, RemoteException { final int MAX_LON = 360; final int MIN_LON = -180; final double DELTA = 0.0001; double leftLon = calcLonBase(); double rightLon = calcLonTop() - MAX_LON; double bottomLat = calcLatBase(); Hashtable labelTable = new Hashtable(); double base = getLonScaleInfo().getBaseLabel(); List majorTicks = new ArrayList(); int minorTickInc = getLonScaleInfo().getMinorDivision(); List minorTicks = new ArrayList(); double inc = getLonScaleInfo().getIncrement(); List increment = new LinkedList(); // In case the user enters something bogus if ((base < MIN_LON) || (base > MAX_LON)) { base = leftLon; } for (double i = base; i > rightLon; i -= inc / minorTickInc) { if (i > leftLon) { // Longitudes that are not in this range are not visible. continue; } increment.add(i); } for (Double i : increment) { EarthLocationTuple elt = new EarthLocationTuple(bottomLat, i, 0); double[] values = newtonLon(elt, 0); Double d = new Double(values[0]); double mm = (base - i) % inc; if ((mm < DELTA) || (mm > (inc - DELTA))) { // Must account for numerical leeway. majorTicks.add(d); labelTable.put(d, CoordinateFormat.formatLongitude(i, lonScaleInfo.getCoordFormat(), lonScaleInfo.isUse360())); } else { minorTicks.add(d); } } finalizeAxis(scale, getLonScaleInfo().getLabel(), labelTable, majorTicks, minorTicks, getLonScaleInfo().getFont()); } /** * Finalize axis labeling. * * @param scale the scale * @param title the title * @param labelTable the label table * @param majorTicks the major ticks * @param minorTicks the minor ticks * @param axisFont the axis font * @throws VisADException the vis ad exception */ private void finalizeAxis( AxisScale scale, String title, Hashtable labelTable, List majorTicks, List minorTicks, Font axisFont) throws VisADException { double[] mjt = new double[majorTicks.size()]; double[] mnt = new double[minorTicks.size()]; for (int i = 0; i < mjt.length; i++) { mjt[i] = majorTicks.get(i); } for (int i = 0; i < mnt.length; i++) { mnt[i] = minorTicks.get(i); } scale.setAutoComputeTicks(false); scale.setSnapToBox(true); scale.setMajorTicks(mjt); scale.setMinorTicks(mnt); scale.setTitle(title); scale.setLabelTable(labelTable); scale.setTicksVisible(true); scale.setMajorTickSpacing(0); scale.setMinorTickSpacing(0); if ((axisFont != null) && axisFont.getName().equals( FontSelector.DEFAULT_FONT.getName())) { scale.setFont((Font) null); scale.setLabelSize(axisFont.getSize()); } else { scale.setFont(axisFont); } } /** * Numerically arriving at the screen coordinates needed for the intersection between the wire frame axis * and the latitude. * * @param elt the earth location tuple. * @param cnt must maintain a count in case of numerical problems * @return the spatial coordinates * @throws RemoteException the remote exception * @throws VisADException the vis ad exception */ private double[] newtonLat(EarthLocationTuple elt, int cnt) throws RemoteException, VisADException { final double DELTA = 0.02; final double DELTA2 = 0.001; final int MAX_CNT = 10; double[] values = getSpatialCoordinates(elt).getValues(); values[0] = -1; EarthLocation el = getEarthLocation(values); // We are in no man's land so must adjust until we find Earth. while (Double.isNaN(el.getLatitude().getValue())) { values[0] = values[0] + DELTA2; el = getEarthLocation(values); if (values[0] > 1) { return new double[] { Double.NaN, Double.NaN, Double.NaN }; } } double diff = elt.getLatitude().getValue() - el.getLatitude().getValue(); if ((Math.abs(diff) < DELTA) || (cnt > MAX_CNT)) { // cnt > 10 in case solution does not converge. Safety valve. return getSpatialCoordinates(el).getValues(); } else { EarthLocationTuple t = new EarthLocationTuple(el.getLatitude().getValue() + diff, el.getLongitude().getValue(), 0); return newtonLat(t, ++cnt); } } /** * Numerically arriving at the screen coordinates needed for the intersection between the wire frame axis * and the longitude. * * @param elt the earth location tuple. * @param cnt must maintain a count in case of numerical problems * @return the spatial coordinates * @throws RemoteException the remote exception * @throws VisADException the vis ad exception */ private double[] newtonLon(EarthLocationTuple elt, int cnt) throws RemoteException, VisADException { final double DELTA = 0.02; final double DELTA2 = 0.001; final int MAX_CNT = 10; double[] values = getSpatialCoordinates(elt).getValues(); values[1] = -1; EarthLocation el = getEarthLocation(values); // We are in no man's land so must adjust until we find Earth. while (Double.isNaN(el.getLatitude().getValue())) { values[1] = values[1] + DELTA2; el = getEarthLocation(values); if (values[1] > 1) { return new double[] { Double.NaN, Double.NaN, Double.NaN }; } } double diff = el.getLongitude().getValue() - elt.getLongitude().getValue(); if ((Math.abs(diff) < DELTA) || (cnt > MAX_CNT)) { // cnt > 10 in case solution does not converge. Safety valve. return getSpatialCoordinates(elt).getValues(); } else { EarthLocationTuple t = new EarthLocationTuple(el.getLatitude().getValue(), el.getLongitude().getValue() - diff, 0); return newtonLon(t, ++cnt); } } /** * Method to update the properties of an AxisScale * * @param scale AxisScale to update * @param title Title * @param maxmin max/min limits of axis * @param bottom value for lower limit * @param top value for upper limit * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void updateVertScale(AxisScale scale, String title, double[] maxmin, double bottom, double top) throws VisADException, RemoteException { scale.setVisible(getVerticalRangeVisible()); scale.setSnapToBox(true); scale.setTitle(title); Hashtable labelTable = new Hashtable(); labelTable.put(new Double(maxmin[0]), labelFormat.format(bottom)); labelTable.put(new Double(maxmin[1]), labelFormat.format(top)); scale.setLabelTable(labelTable); scale.setTickBase(maxmin[0]); scale.setMajorTickSpacing(Math.abs(maxmin[1] - maxmin[0])); scale.setMinorTickSpacing(Math.abs(maxmin[1] - maxmin[0])); } /** * Set the vertical axis scale * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void makeVerticalScale() throws VisADException, RemoteException { if (verticalScale == null) { return; } setDisplayInactive(); double[] zRange = zMap.getRange(); String title = verticalParameter.getName() + "(" + verticalRangeUnit.getIdentifier() + ")"; updateVertScale(verticalScale, title, zRange, minVerticalRange, maxVerticalRange); /* * verticalScale.setSnapToBox(true); * verticalScale.setTitle( * Hashtable labelTable = new Hashtable(); * labelTable.put(new Double(zRange[0]), * labelFormat.format(minVerticalRange)); * labelTable.put(new Double(zRange[1]), * labelFormat.format(maxVerticalRange)); * verticalScale.setLabelTable(labelTable); * verticalScale.setTickBase(zRange[0]); * verticalScale.setMajorTickSpacing(Math.abs(zRange[1]-zRange[0])); } * verticalScale.setMinorTickSpacing(Math.abs(zRange[1]-zRange[0])); */ setDisplayActive(); } /** * Define the set of spatial scalar maps that this display will * use. Every time a new projection is set, a new set of DisplayTypes * is created with a coordinate system for transposing between * projection space and xyz space. The mappings are: *

    *
  • RealType.Latitude -> getDisplayLatitudeType() *
  • RealType.Longitude -> getDisplayLongitudeType() *
  • RealType.Altitude -> getDisplayAltitudeType() *
* This is called on construction of the display or with every rebuild. * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ private void setSpatialScalarMaps() throws VisADException, RemoteException { setDisplayInactive(); ScalarMapSet mapSet = new ScalarMapSet(); if (latitudeMap != null) { removeScalarMap(latitudeMap); } latitudeMap = new ScalarMap(RealType.Latitude, displayLatitudeType); mapSet.add(latitudeMap); latitudeMap.setRangeByUnits(); latitudeMap.setScaleEnable(true); if (longitudeMap != null) { removeScalarMap(longitudeMap); } longitudeMap = new ScalarMap(RealType.Longitude, displayLongitudeType); mapSet.add(longitudeMap); longitudeMap.setRangeByUnits(); longitudeMap.setScaleEnable(true); if (getDisplayMode() == MODE_3D) { ScalarMapSet newVertMaps = new ScalarMapSet(); if (verticalMapSet.size() > 0) { for (Iterator iter = verticalMapSet.iterator(); iter.hasNext(); ) { ScalarType r = ((ScalarMap) iter.next()).getScalar(); ScalarMap newMap = new ScalarMap(r, displayAltitudeType); newMap.setScaleEnable(true); if (r.equals(RealType.Altitude)) { altitudeMap = newMap; } newVertMaps.add(newMap); } } else { // add Altitude at least altitudeMap = new ScalarMap(RealType.Altitude, displayAltitudeType); altitudeMap.setScaleEnable(true); newVertMaps.add(altitudeMap); } removeScalarMaps(verticalMapSet); verticalMapSet.clear(); verticalMapSet.add(newVertMaps); setVerticalRange(minVerticalRange, maxVerticalRange); setVerticalRangeUnit(verticalRangeUnit); mapSet.add(verticalMapSet); } if ( !init) { xMap = new ScalarMap(RealType.XAxis, Display.XAxis); xMap.setRange(-1.0, 1.0); mapSet.add(xMap); xMap.setScaleEnable(true); lonScale = xMap.getAxisScale(); yMap = new ScalarMap(RealType.YAxis, Display.YAxis); yMap.setRange(-1.0, 1.0); mapSet.add(yMap); yMap.setScaleEnable(true); latScale = yMap.getAxisScale(); if (getDisplayMode() == MODE_3D) { zMap = new ScalarMap(RealType.ZAxis, Display.ZAxis); zMap.setRange(-1.0, 1.0); mapSet.add(zMap); zMap.setScaleEnable(true); verticalScale = zMap.getAxisScale(); init = true; } addScalarMaps(mapSet); setDisplayActive(); } /** * Add a new mapping of this type to the vertical coordinate * * @param newVertType RealType of map * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ public void addVerticalMap(RealType newVertType) throws VisADException, RemoteException { if (getDisplayMode() == MODE_3D) { Unit u = newVertType.getDefaultUnit(); if ( !(Unit.canConvert(u, CommonUnit.meter) || Unit.canConvert( u, GeopotentialAltitude.getGeopotentialMeter()))) { throw new VisADException("Unable to handle units of " + newVertType); } ScalarMap newMap = new ScalarMap(newVertType, getDisplayAltitudeType()); setVerticalMapUnit(newMap, verticalRangeUnit); newMap.setRange(minVerticalRange, maxVerticalRange); verticalMapSet.add(newMap); addScalarMaps(verticalMapSet); } } /** * Remove a new mapping of this type to the vertical coordinate * * @param vertType RealType of map * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ public void removeVerticalMap(RealType vertType) throws VisADException, RemoteException { if (getDisplayMode() == MODE_3D) { ScalarMapSet sms = new ScalarMapSet(); for (Iterator iter = verticalMapSet.iterator(); iter.hasNext(); ) { ScalarMap s = (ScalarMap) iter.next(); if (((RealType) s.getScalar()).equals(vertType)) { sms.add(s); } } if ( !(sms.size() == 0)) { verticalMapSet.remove(sms); removeScalarMaps(sms); } } } /** * Set the Unit of the vertical range * * @param newUnit unit of range * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ public void setVerticalRangeUnit(Unit newUnit) throws VisADException, RemoteException { super.setVerticalRangeUnit(newUnit); if ((newUnit != null) && Unit.canConvert(newUnit, CommonUnit.meter)) { verticalMapSet.setVerticalUnit(newUnit); verticalRangeUnit = newUnit; } makeVerticalScale(); } /** * Sets the lat scale info. * * @param axisScaleInfo the new lat scale info * @throws RemoteException the remote exception * @throws VisADException the vis ad exception */ public void setLatScaleInfo(LatLonAxisScaleInfo axisScaleInfo) throws RemoteException, VisADException { this.latScaleInfo = axisScaleInfo; makeLatScales(); } /** * Sets the lon scale info. * * @param axisScaleInfo the new lon scale info * @throws RemoteException the remote exception * @throws VisADException the vis ad exception */ public void setLonScaleInfo(LatLonAxisScaleInfo axisScaleInfo) throws RemoteException, VisADException { this.lonScaleInfo = axisScaleInfo; makeLonScales(); } /** * Gets the lat scale info. * * @return the lat scale info */ public LatLonAxisScaleInfo getLatScaleInfo() { // The 2nd null check is kludgy, but sometimes LatLonAxisScaleInfo //deserialization will have problems in which case all fields //are null. //if ((latScaleInfo == null) || (latScaleInfo.getBaseLabel() == null)) { if (latScaleInfo == null) { LatLonAxisScaleInfo lsi = new LatLonAxisScaleInfo(); latScaleInfo = lsi; lsi.setLabel("Latitude"); lsi.setIncrement(10); lsi.setMinorDivision(1); lsi.setVisible(true); lsi.setCoordFormat(LatLonAxisScaleInfo.COORD_FORMATS[0]); lsi.setUse360(latScaleInfo.isUse360()); double base = calcLatBase(); double end = calcLatTop(); double inc = Math.abs(end - base) / 10d; lsi.setIncrement(makeIncrementNice(inc)); base = (Math.floor(base / 10)) * 10; // Make base nice (i.e. multiple of 10) base = (base < -90) ? -90 : base; base = (base > 90) ? 90 : base; latScaleInfo.setBaseLabel(base); } return latScaleInfo; } /** * Make increment nice for the user. * * @param inc the not nice inc * @return the nice increment */ private double makeIncrementNice(double inc) { //Somewhat arbitrary double[] niceNums = { 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 15, 20, 25, 30, 40, 50, 60, 70, 80 }; for (int i = 0; i < niceNums.length; i++) { if (inc < niceNums[i]) { return niceNums[i]; } } return 10; } /** * Gets the lon scale info. * * @return the lon scale info */ public LatLonAxisScaleInfo getLonScaleInfo() { // The 2nd null check is kludgy, but sometimes LatLonAxisScaleInfo //deserialization will have problems in which case all fields //are null. //if ((lonScaleInfo == null) || (lonScaleInfo.getBaseLabel() == null)) { if (lonScaleInfo == null) { LatLonAxisScaleInfo lsi = new LatLonAxisScaleInfo(); lonScaleInfo = lsi; lsi.setLabel("Longitude"); lsi.setMinorDivision(1); lsi.setVisible(true); lsi.setCoordFormat(LatLonAxisScaleInfo.COORD_FORMATS[0]); lsi.setUse360(false); double base = calcLonBase(); double end = calcLonTop(); double inc; if (isSouthPole()) { inc = Math.abs((end - 360) - base) / 10d; } else { inc = Math.abs(end - ((base > end) ? base - 360 : base)) / 10d; } // Must deal with meridian lsi.setIncrement(makeIncrementNice(inc)); // base = (Math.floor(base / 10)) * 10; // Make base nice (i.e. multiple of 10) base = (base < -180) ? -180 : base; base = (base > 180) ? (base - 360) : base; lonScaleInfo.setBaseLabel((isSouthPole() ? Math.floor(base) : Math.ceil(base))); } return lonScaleInfo; } /** * Rounding convenience method. * * @param unrounded the unrounded * @param precision the precision * @param roundingMode the rounding mode * @return the rounded value */ private static double round(double unrounded, int precision, int roundingMode) { BigDecimal bd = new BigDecimal(unrounded); BigDecimal rounded = bd.setScale(precision, roundingMode); return rounded.doubleValue(); } /** * Set the range of the vertical coordinate * * @param min minimum value for vertical axis * @param max maximum value for vertical axis * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ public void setVerticalRange(double min, double max) throws VisADException, RemoteException { super.setVerticalRange(min, max); verticalMapSet.setVerticalRange(min, max); minVerticalRange = min; maxVerticalRange = max; makeVerticalScale(); } /** * Get the range of the vertical coordinate (Altitude) * * @return array of {min, max} range. */ public double[] getVerticalRange() { ScalarMap vertMap = getAltitudeMap(); return (vertMap != null) ? vertMap.getRange() : new double[] { minVerticalRange, maxVerticalRange }; } /** * Sets the cursor range from center property. * * @param range The cursor range from center. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ protected void setCursorRange(Real range) throws VisADException, RemoteException { Real oldRange = cursorRange; cursorRange = range; firePropertyChange(CURSOR_RANGE, oldRange, cursorRange); } /** * Gets the cursor range from center property. * * @return The currently-selected range. May be * null. */ public Real getCursorRange() { return cursorRange; } /** * Sets the cursor bearing (degrees) from center property. * This implementation uses a great circle distance. * * @param bearing The cursor bearing from center. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ protected void setCursorBearing(Real bearing) throws VisADException, RemoteException { Real oldBearing = cursorBearing; cursorBearing = bearing; * componentCenterY), -1); firePropertyChange(CURSOR_BEARING, oldBearing, cursorBearing); } /** * Gets the cursor bearing from center property. * * @return The currently-selected bearing. May be * null. */ public Real getCursorBearing() { return cursorBearing; } /** * Set the view for 3D. The views are based on the original display * as follows: *
     *                        NORTH
     *                      _________
     *                    W |       | E
     *                    E |  TOP  | A
     *                    S |       | S
     *                    T |_______| T
     *                        SOUTH
     * 
* @param view one of the static view fields (NORTH_VIEW, SOUTH_VIEW, .. * etc). */ public void setView(int view) { if (getDisplayMode() != MODE_3D) { return; } } /** * Accessor method for the DisplayLatitudeType (i.e., what * RealType.Latitude is mapped to). * * @return the DisplayRealType that RealType.Latitude is mapped to */ public DisplayRealType getDisplayLatitudeType() { return displayLatitudeType; } /** * Accessor method for the DisplayLongitudeType (i.e., what * RealType.Longitude is mapped to). * * @return the DisplayRealType that RealType.Longitude is mapped to */ public DisplayRealType getDisplayLongitudeType() { return displayLongitudeType; } /** * Accessor method for the DisplayAltitudeType (i.e., what * RealType.Altitude is mapped to). * * @return the DisplayRealType that RealType.Altitude is mapped to */ public DisplayRealType getDisplayAltitudeType() { return displayAltitudeType; } /** * Accessor method for the vertical coordinate ScalarMap (i.e., what * getDisplayAltitudeType is mapped from). * @return the ScalarMap that the vertical coordinate is mapped to */ protected ScalarMap getAltitudeMap() { return altitudeMap; } /** * Define the map projection using a Projection interface * * @param projection Projection to use * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ public void setMapProjection(ProjectionImpl projection) throws VisADException, RemoteException { setMapProjection(new ProjectionCoordinateSystem(projection), true); } /** * Define the map projection using a MapProjection type CoordinateSystem * * @param mapProjection map projection coordinate system * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ public void setMapProjection(MapProjection mapProjection) throws VisADException, RemoteException { setMapProjection(mapProjection, true); } /** * Define the map projection using a MapProjection type CoordinateSystem * * @param mapProjection map projection coordinate system * * @param resetDisplayProjMatrix yes/no change the current VisAD Display projection matrix * when the map projection is changed. * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ public void setMapProjection(MapProjection mapProjection, boolean resetDisplayProjMatrix) throws VisADException, RemoteException { if (mapProjection.equals(this.mapProjection)) { return; } this.mapProjection = mapProjection; coordinateSystem = makeCoordinateSystem(mapProjection); // Need to reset these for new projection. this.latScaleInfo = null; this.lonScaleInfo = null; resetMapParameters(resetDisplayProjMatrix); EarthLocation el = getEarthLocation(0, 0, 0); centerLLP.set(el.getLatitude().getValue(CommonUnit.degree), el.getLongitude().getValue(CommonUnit.degree)); } /** * Get the MapProjection that defines the xy mapping of this * MapProjectionDisplay. * * @return MapProjection being used. */ public MapProjection getMapProjection() { return mapProjection; } /** * Set the map area from the projection rectangle * * @param mapArea map area as lat/lon lines * * @throws RemoteException problem setting remote data * @throws VisADException problem creating VisAD data object */ public void setMapArea(ProjectionRect mapArea) throws VisADException, RemoteException { if (coordinateSystem == null) { throw new VisADException("Navigation hasn't been set yet"); } // System.out.println("Map Area = " + mapArea); MapProjection project = ((MapProjection3DAdapter) coordinateSystem).getMapProjection(); // get the corners in latlon coords ProjectionPoint ppMax = mapArea.getMaxPoint(); ProjectionPoint ppMin = mapArea.getMinPoint(); // System.out.println("ppMax:" + ppMax); // System.out.println("ppMin:" + ppMin); float[][] values = new float[2][2]; values[0][0] = (float) ppMax.getY(); values[0][1] = (float) ppMin.getY(); values[1][0] = (float) ppMax.getX(); values[1][1] = (float) ppMin.getX(); // values = project.toReference(values); Gridded2DSet region = new Gridded2DSet(RealTupleType.LatitudeLongitudeTuple, values, 2); setMapRegion(region); } /** * Set the map region to be displayed. The MathType of the domain * of the set must be either RealTupleType.SpatialCartesian2DTuple, * RealTupleType.SpatialEarth2DTuple, or * RealTupleType.LatitudeLongitudeTuple. * /* * @param region Gridded2DSet containing the range of for the axis. * * @throws VisADException invalid domain or null set * @throws RemoteException Couldn't create a remote object */ public void setMapRegion(Gridded2DSet region) throws VisADException, RemoteException { if (region == null) { throw new VisADException("Region can't be null"); } if (region.isMissing()) { return; } /** // Check the type. We need to work in XYZ coordinates Gridded2DSet xyRegion; RealTupleType regionType = ((SetType) region.getType()).getDomain(); if (regionType.equals(RealTupleType.SpatialCartesian2DTuple)) { xyRegion = region; } else if (regionType.equals(RealTupleType.SpatialEarth2DTuple) || regionType.equals( RealTupleType.LatitudeLongitudeTuple)) { // transform to x/y int latIndex = regionType.equals(RealTupleType.LatitudeLongitudeTuple) ? 0 : 1; int lonIndex = (latIndex == 0) ? 1 : 0; float[][] values = region.getSamples(true); float xy[][] = new float[3][values[0].length]; xy[0] = values[latIndex]; xy[1] = values[lonIndex]; xy = coordinateSystem.toReference(xy); values[0] = xy[0]; values[1] = xy[1]; xyRegion = new Gridded2DSet(RealTupleType.SpatialCartesian2DTuple, values, 2); } else { throw new VisADException("Invalid domain for region " + regionType); } // System.out.println(xyRegion); // Okay, now we have our region, let's get cracking // First, let's figure out our component size. Dimension d = getComponent().getSize(); // if running the isl non interactively, d is not assigned, the component is // one layer deeper. if ((d.width == 0) || (d.height == 0)) { JPanel jp = (JPanel) getComponent(); d = jp.getComponent(0).getSize(); } // System.out.println("Component size = " + d); int componentCenterX = d.width / 2; int componentCenterY = d.height / 2; /* * System.out.println( * "Component Center point = " + * componentCenterX +","+componentCenterY); */ // Now let's get the MouseBehavior so we can get some display coords MouseBehavior behavior = getDisplay().getDisplayRenderer().getMouseBehavior(); ProjectionControl proj = getDisplay().getProjectionControl(); double[] aspect = getDisplayAspect(); // Misc.printArray("aspect", aspect); // We have to figure the component coordinates of the region. // To do this, we calculate the number of display units per pixel // in the x and y. This logic comes from visad.MouseHelper. // Basically, we find out the current matrix, how much we should // scale, translate and rotate, and then apply the new matrix. double[] center_ray = behavior.findRay(componentCenterX, componentCenterY).position; // Misc.printArray("center_ray", center_ray); double[] center_ray_x = behavior.findRay(componentCenterX + 1, componentCenterY).position; */ // Misc.printArray("center_ray_x", center_ray_x); double[] center_ray_y = behavior.findRay(componentCenterX, componentCenterY + 1).position; // Misc.printArray("center_ray_y", center_ray_y); /* * TODO: test more to see if this makes a difference. The * rubber band box is actually at the Z=-1 position * double[] center_ray = getRayPositionAtZ(behavior.findRay(componentCenterX, * componentCenterY), -1); * Misc.printArray("center_ray @ -1", center_ray); * double[] center_ray_x = getRayPositionAtZ(behavior.findRay(componentCenterX + 1, * Misc.printArray("center_ray_x @ -1", center_ray_x); * double[] center_ray_y = getRayPositionAtZ(behavior.findRay(componentCenterX, * componentCenterY + 1),-1); * Misc.printArray("center_ray_y @ -1", center_ray_y); */ double[] tstart = proj.getMatrix(); // printMatrix("tstart", tstart); double[] rot = new double[3]; double[] scale = new double[3]; double[] trans = new double[3]; behavior.instance_unmake_matrix(rot, scale, trans, tstart); double stx = scale[0]; double sty = scale[1]; // System.out.println("stx = " + stx); // System.out.println("sty = " + sty); double[] trot = behavior.make_matrix(rot[0], rot[1], rot[2], scale[0], scale[1], scale[2], // scale[0], 0.0, 0.0, 0.0); // printMatrix("trot", trot); // WLH 17 Aug 2000 double[] xmat = behavior.make_translate(center_ray_x[0] - center_ray[0], center_ray_x[1] - center_ray[1], center_ray_x[2] - center_ray[2]); // xmat = behavior.multiply_matrix(mult, xmat); double[] ymat = behavior.make_translate(center_ray_y[0] - center_ray[0], center_ray_y[1] - center_ray[1], center_ray_y[2] - center_ray[2]); // ymat = behavior.multiply_matrix(mult, ymat); double[] xmatmul = behavior.multiply_matrix(trot, xmat); double[] ymatmul = behavior.multiply_matrix(trot, ymat); /* * printMatrix("xmat", xmat); * printMatrix("ymat", ymat); * printMatrix("xmatmul", xmatmul); * printMatrix("ymatmul", ymatmul); */ behavior.instance_unmake_matrix(rot, scale, trans, xmatmul); double xmul = trans[0]; behavior.instance_unmake_matrix(rot, scale, trans, ymatmul); double ymul = trans[1]; // System.out.println("Multipliers = " + xmul + "," + ymul); // make sure that we don't divide by 0 (happens if display // component is not yet on screen if ((Math.abs(xmul) > 0) && (Math.abs(ymul) > 0)) { // Now we can get the box coordinates in component space float[] lows = xyRegion.getLow(); float[] highs = xyRegion.getHi(); float boxCenterDisplayX = (highs[0] + lows[0]) / 2.0f; float boxCenterDisplayY = (highs[1] + lows[1]) / 2.0f; /* * System.out.println( * "Box center point (XY) = " + * boxCenterDisplayX+","+boxCenterDisplayY); */ // Check to see if the box is big enough (at least 5x5 pixels) // *** might want to ammend this to be a percentage of // component size **** int boxWidth = (int) Math.abs((highs[0] - lows[0]) / xmul * stx); int boxHeight = (int) Math.abs((highs[1] - lows[1]) / ymul * sty); * System.out.println( * "Box size = " + boxWidth +"," + boxHeight); */ if ((boxWidth > 5) && (boxHeight > 5)) { int boxCenterX = componentCenterX + (int) ((boxCenterDisplayX - center_ray[0]) / xmul); int boxCenterY = componentCenterY - (int) ((boxCenterDisplayY - center_ray[1]) / ymul); /* * System.out.println( * "Box Center point = " + boxCenterX +","+boxCenterY); */ double transx = (componentCenterX - boxCenterX) * xmul * stx; double transy = (componentCenterY - boxCenterY) * ymul * sty; /* * System.out.println("transx = " + transx + * " transy = " + transy); */ // Now calculate zoom factor double zoom = (boxWidth / boxHeight >= d.width / d.height) ? d.getWidth() / boxWidth : d.getHeight() / boxHeight; // zoom out if this is a bigger region than the component // System.out.println("zoom factor = " + zoom); translate(transx, -transy); zoom(zoom); } } } /** * Scale vertical values using the range of the vertical * scalar map. * * @param altValues altitude map values * * @return z values (may transform in place); */ public float[] scaleVerticalValues(float[] altValues) { if (getAltitudeMap() == null) { return altValues; } return getAltitudeMap().scaleValues(altValues, false); } /** * Set up the DisplayTupleType. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ private void setDisplayTypes() throws VisADException, RemoteException { if (coordinateSystem == null) { System.out.println("coordSys == null"); displayLatitudeType = Display.YAxis; displayLongitudeType = Display.XAxis; displayAltitudeType = Display.ZAxis; displayTupleType = Display.DisplaySpatialCartesianTuple; } else { int myInstance; synchronized (INSTANCE_MUTEX) { myInstance = instance++; } // We need to set the range on the longitude axis // to be equal to the range of the projection if the // X coordinate is approximately equal to Longitude. // For now, this is only LatLonProjections and TrivalMP's double minLon = -360; double maxLon = 360.; double centerLon = 0; MapProjection mp = ((MapProjection3DAdapter) coordinateSystem) .getMapProjection(); boolean isLatLon = false; adjustLons = true; // HACK, HACK, HACK, HACK if (mp instanceof ProjectionCoordinateSystem) { ProjectionImpl proj = ((ProjectionCoordinateSystem) mp).getProjection(); if (proj instanceof LatLonProjection) { Rectangle2D r2d2 = mp.getDefaultMapArea(); minLon = r2d2.getX(); maxLon = minLon + r2d2.getWidth(); centerLon = minLon + r2d2.getWidth() / 2; isLatLon = true; } } else if (mp instanceof TrivialMapProjection) { Rectangle2D r2d2 = mp.getDefaultMapArea(); minLon = r2d2.getX(); maxLon = minLon + r2d2.getWidth(); centerLon = minLon + r2d2.getWidth() / 2; // isLatLon = true; // TODO: figure out this a little more. } else if (mp instanceof AREACoordinateSystem) { // minLon = -180; // maxLon = 180.; adjustLons = false; } // TODO: figure out what we should be doing here. use360 = !((minLon >= -185) && (maxLon <= 185)); if ((isLatLon && !use360) // lat/lon projections in +/-180 rang || !mp.isXYOrder() // Vis5D || ((minLon > -360) && (minLon < 0) && (maxLon > 180))) { // AVN grids adjustLons = false; } /* * System.out.println("DisplayProjectionLon" + myInstance * + " has range of " + minLon + " to " + maxLon * + " with center at " + centerLon * + "; use360 = " + use360 + "; adjust lons = " * + adjustLons); */ displayLatitudeType = new DisplayRealType("ProjectionLat" + myInstance, true, -90.0, 90.0, 0.0, CommonUnit.degree); displayLongitudeType = new DisplayRealType("ProjectionLon" + myInstance, true, minLon, maxLon, centerLon, CommonUnit.degree); double defaultZ = (getDisplayMode() != MODE_3D) ? 0.0 : -1.0; displayAltitudeType = new DisplayRealType("ProjectionAlt" + myInstance, true, -1.0, 1.0, defaultZ, null); displayTupleType = new DisplayTupleType(new DisplayRealType[] { displayLatitudeType, displayLongitudeType, displayAltitudeType }, coordinateSystem); } setSpatialScalarMaps(); } /** * Create the adapter coordinate system using a MapProjection * * @param mapProjection * * @return the adapted coordinate system * * @throws VisADException null mapProjection or other VisAD problem */ private CoordinateSystem makeCoordinateSystem(MapProjection mapProjection) throws VisADException { if (mapProjection == null) { throw new VisADException("MapProjection can't be null"); } CoordinateSystem cs = new MapProjection3DAdapter(mapProjection); csUnits = cs.getCoordinateSystemUnits(); return cs; } /** * Handles a change to the cursor position. * * @throws VisADException VisAD failure. * @throws RemoteException Java RMI failure. */ protected void cursorMoved() throws VisADException, RemoteException { double[] c = getDisplay().getDisplayRenderer().getCursor(); updateLocation( getEarthLocation(getDisplay().getDisplayRenderer().getCursor())); } /** * Update lat/lon/alt properties with the EarthLocation. * * @param el EarthLocation to use. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ protected void updateLocation(EarthLocation el) throws VisADException, RemoteException { super.updateLocation(el); cursorLLP.set(el.getLatitude().getValue(CommonUnit.degree), el.getLongitude().getValue(CommonUnit.degree)); Bearing.calculateBearing(centerLLP, cursorLLP, workBearing); setCursorRange(new Real(CURSOR_RANGE_TYPE, workBearing.getDistance())); setCursorBearing(new Real(CURSOR_BEARING_TYPE, workBearing.getAngle())); } * Handles a change in the position of the mouse-pointer. * * @param x x mouse position * @param y y mouse position * * @throws RemoteException Java RMI problem * @throws UnitException Unit conversion problem * @throws VisADException VisAD problem */ protected void pointerMoved(int x, int y) throws UnitException, VisADException, RemoteException { /* * Convert from (pixel, line) Java Component coordinates to (latitude, * longitude) */ /* * TODO: figure out why this won't work * updateLocation(getEarthLocation(getSpatialCoordinatesFromScreen(x, * y))); */ // TODO: java2d // if(true) return; VisADRay ray = getRay(x, y); EarthLocation el = getEarthLocation(ray.position[0], ray.position[1], ray.position[2]); updateLocation(el); } /** * Get the earth location from the VisAD xyz coodinates * * @param x x * @param y y * @param z z * @param setZToZeroIfOverhead If in the overhead view then set Z to 0 * * @return corresponding EarthLocation */ public EarthLocation getEarthLocation(double x, double y, double z, boolean setZToZeroIfOverhead) { EarthLocationTuple value = null; try { float[][] numbers = coordinateSystem.fromReference(new float[][] { new float[] { (float) (x) }, new float[] { (float) (y) }, new float[] { (float) (z) } }); Real lat = new Real(RealType.Latitude, getScaledValue(latitudeMap, numbers[0][0]), csUnits[0]); Real lon = new Real(RealType.Longitude, getScaledValue(longitudeMap, numbers[1][0]), csUnits[1]); Real alt = null; if (getDisplayMode() == MODE_3D) { if (setZToZeroIfOverhead && Arrays.equals(getProjectionMatrix(), getSavedProjectionMatrix()) /* * && (alt * .getValue( * getVerticalRangeUnit()) != altitudeMap * .getRange()[0]) */ ) { alt = new Real(RealType.Altitude, altitudeMap.getRange()[0], getVerticalRangeUnit()); } else { alt = new Real(RealType.Altitude, getScaledValue(altitudeMap, numbers[2][0])); } } else { alt = new Real(RealType.Altitude, 0); } value = new EarthLocationTuple(lat, lon, alt); } catch (VisADException e) { e.printStackTrace(); } // can't happen catch (RemoteException e) { e.printStackTrace(); } // can't happen return value; } /** * Returns the spatial (XYZ) coordinates of the particular EarthLocation * * @param el earth location to transform * * @return RealTuple of display coordinates. */ public RealTuple getSpatialCoordinates(EarthLocation el) { if (el == null) { throw new NullPointerException( "MapProjectionDisplay.getSpatialCoorindate(): " + "null input EarthLocation"); } RealTuple spatialLoc = null; try { double[] xyz = getSpatialCoordinates(el, null); spatialLoc = new RealTuple(RealTupleType.SpatialCartesian3DTuple, xyz); } catch (VisADException e) { e.printStackTrace(); } // can't happen catch (RemoteException e) { e.printStackTrace(); } // can't happen return spatialLoc; } /** * Returns the spatial (XYZ) coordinates of the particular EarthLocation * * @param el earth location to transform * @param xyz The in value to set. May be null. * @param altitude altitude value * * @return xyz array * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ public double[] getSpatialCoordinates(EarthLocation el, double[] xyz, double altitude) throws VisADException, RemoteException { float[] altValues; if ((altitudeMap != null) && (el.getAltitude() != null) && !(Double.isNaN(altitude))) { altValues = altitudeMap.scaleValues(new double[] { altitude }); } else { altValues = new float[] { 0f }; } float[][] temp = coordinateSystem.toReference(new float[][] { latitudeMap.scaleValues(new double[] { el.getLatitude().getValue(CommonUnit.degree) }), longitudeMap.scaleValues(new double[] { el.getLongitude().getValue(CommonUnit.degree) }), altValues }); if (xyz == null) { xyz = new double[3]; } xyz[0] = temp[0][0]; xyz[1] = temp[1][0]; xyz[2] = temp[2][0]; return xyz; } /** * Method called to reset all the map parameters after a change. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ private void resetMapParameters() throws VisADException, RemoteException { resetMapParameters(true); } /** * Method called to reset all the map parameters after a change. * * @param resetDisplayProjMatrix yes/no change the current VisAD Display projection matrix * when the map projection is changed. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ private void resetMapParameters(boolean resetDisplayProjMatrix) throws VisADException, RemoteException { setDisplayInactive(); setDisplayTypes(); if (resetDisplayProjMatrix) { resetProjection(); // make it the right size setAspect(); } makeLatScales(); makeLonScales(); setDisplayActive(); } /** * Set the aspect for the display. */ private void setAspect() { Rectangle2D mapArea = mapProjection.getDefaultMapArea(); double ratio = mapArea.getWidth() / mapArea.getHeight(); double[] myaspect = getDisplayAspect(); try { if (ratio == 1.0) { // height == width setDisplayAspect((getDisplayMode() != MODE_2D) ? new double[] { 1.0, 1.0, myaspect[2] } : new double[] { 1.0, 1.0 }); /* * guess this doesn't matter, just use the other * } else if (ratio < 1) { // height > width * setDisplayAspect( * (getDisplayMode() != MODE_2D) * ? new double[] { 1.0, ratio, myaspect[2] } * : new double[] { 1.0, ratio }); */ } else { // width > height setDisplayAspect((getDisplayMode() != MODE_2D) ? new double[] { ratio, 1.0, myaspect[2] } : new double[] { ratio, 1.0 }); } // Misc.printArray("aspect", getDisplayAspect()); } catch (Exception excp) { System.out.println( "MapProjectionDisplay.setDisplayAspect() got exception: " + excp); } } /** * Make the default projection. * * @return Default projectcion * * @throws VisADException couldn't create MapProjection */ protected static MapProjection makeDefaultMapProjection() throws VisADException { return new ProjectionCoordinateSystem( new LatLonProjection("Default Projection", // Use this to make the aspect ratio correct new ProjectionRect(-180., -180., 180., 180.))); } /** * Get the display coordinate system that turns lat/lon/alt to * x/y/z * * @return the coordinate system (may be null) */ public CoordinateSystem getDisplayCoordinateSystem() { return coordinateSystem; } /** * test by running java ucar.unidata.view.geoloc.MapProjectionDisplay * * @param args include an argument for a 3D display * * @throws Exception problem creating the display */ public static void main(String[] args) throws Exception { JFrame frame = new JFrame(); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); final MapProjectionDisplay navDisplay = ((args.length > 0) && visad.util.Util.canDoJava3D()) ? MapProjectionDisplay.getInstance(NavigatedDisplay.MODE_3D) : (visad.util.Util.canDoJava3D() == true) ? MapProjectionDisplay.getInstance(NavigatedDisplay.MODE_2Din3D) : MapProjectionDisplay.getInstance(NavigatedDisplay.MODE_2D); /* * double[]aspect = { 1.0, 1.0, 0.4 }; * navDisplay.setDisplayAspect((navDisplay.getDisplayMode() == NavigatedDisplay.MODE_2D) * ? new double[]{ 1.0, 1.0 } * : aspect); */ DisplayImpl display = (DisplayImpl) navDisplay.getDisplay(); navDisplay.setBackground(Color.white); navDisplay.setForeground(Color.black); // navDisplay.setCursorStringOn(true); MapLines mapLines = new MapLines("maplines"); URL mapSource = // new URL("ftp://www.ssec.wisc.edu/pub/visad-2.0/OUTLSUPW"); navDisplay.getClass().getResource("/auxdata/maps/OUTLSUPW"); try { BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource); mapLines.setMapLines(mapAdapter.getData()); mapLines.setColor(java.awt.Color.black); navDisplay.addDisplayable(mapLines); } catch (Exception excp) { System.out.println("Can't open map file " + mapSource); System.out.println(excp); } JPanel panel = new JPanel(new GridLayout(1, 0)); JButton pushme = new JButton("Map Projection Manager"); panel.add(pushme); frame.getContentPane().add(panel, BorderLayout.NORTH); ViewpointControl vpc = new ViewpointControl(navDisplay); panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.add(navDisplay.getComponent(), BorderLayout.CENTER); panel.add((navDisplay.getDisplayMode() == navDisplay.MODE_3D) ? (Component) ucar.unidata.util.GuiUtils.topCenterBottom( vpc.getToolBar(JToolBar.VERTICAL), new NavigatedDisplayToolBar( navDisplay, JToolBar.VERTICAL), GuiUtils.filler()) : (Component) new NavigatedDisplayToolBar(navDisplay, JToolBar.VERTICAL), BorderLayout.WEST); JPanel readout = new JPanel(); readout.add(new NavigatedDisplayCursorReadout(navDisplay)); readout.add(new RangeAndBearingReadout(navDisplay)); panel.add(readout, BorderLayout.SOUTH); navDisplay.draw(); frame.getContentPane().add(panel, BorderLayout.CENTER); if (navDisplay.getDisplayMode() == navDisplay.MODE_3D) { JMenuBar mb = new JMenuBar(); mb.add(vpc.getMenu()); frame.setJMenuBar(mb); } System.out.println("Using rectilinear projection"); final ProjectionManager pm = new ProjectionManager(); pm.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { if (e.getPropertyName().equals("ProjectionImpl")) { try { navDisplay.setMapProjection( (ProjectionImpl) e.getNewValue()); } catch (Exception exp) { System.out.println(exp); } } } }); pushme.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { pm.show(); } }); /* * EarthLocationTuple elt = new EarthLocationTuple(40,-105, 8000); * SelectorPoint sp = new SelectorPoint("foo", elt); * sp.setFixed(false,false,true); * sp.setColor(Color.black); * navDisplay.addDisplayable(sp); */ navDisplay.getDisplay().getGraphicsModeControl().setScaleEnable(true); frame.pack(); frame.setVisible(true); /* * if (navDisplay.getDisplayMode() != navDisplay.MODE_2D) { * final CurveDrawer cd = * //new CurveDrawer(RealType.XAxis, RealType.YAxis, * new CurveDrawer(RealType.Latitude, RealType.Longitude, * InputEvent.SHIFT_MASK); * cd.addAction(new ActionImpl("Curve Drawer") { * int numSets = 0; * public void doAction () * throws VisADException, RemoteException { * UnionSet curves = (UnionSet) cd.getData(); * if (curves != null) { * int num = curves.getSets().length; * if (num != numSets) { * numSets = num; * System.out.println("Data has type " + * curves.getType() + * " length = " + numSets); * } * } * } * }); * cd.setColor(Color.red); * cd.setLineWidth(2.0f); * navDisplay.addDisplayable(cd); * } */ /* * System.out.println("Setting projection GOES-E satellite"); * Thread.sleep(5000); * AreaAdapter aa = * new AreaAdapter( * "adde://adde.ucar.edu/imagedata?group=rtimages&descr=edfloater-i&compress=true"); * navDisplay.setMapProjection((MapProjection) aa.getCoordinateSystem()); * EarthLocation el = * navDisplay.getEarthLocation(new double[] {0.0, 0.0, 1.0}); * System.out.println("location = " + el); * System.out.println(navDisplay.getSpatialCoordinates(el)); */ } /** * An adapter for visad.georef.MapProjection coordinate systems (ie: * ones with * a reference of Lat/Lon). Allows for the conversion from * lat/lon to Display.DisplaySpatialCartesianTuple (XYZ). * Altitude (z) values are held constant. */ protected class MapProjection3DAdapter extends CoordinateSystem implements InverseLinearScaledCS { /** index of the latitude coordinate */ private final int latIndex; /** index of the longitude coordinate */ private final int lonIndex; /** map projection for xy -> lat/lon transformations */ private final MapProjection mapProjection; /** X offset */ private final double offsetX; /** Y offset */ private final double offsetY; /** X scaling factor */ private final double scaleX; /** Y scaling factor */ private final double scaleY; /** the coordinate system */ private CoordinateSystem theCoordinateSystem; /** index of the x coordinate */ private final int xIndex; /** index of the y coordinate */ private final int yIndex; /** * Construct a new CoordinateSystem which uses a MapProjection for * the transformations between x,y and lat/lon. * * @param mapProjection CoordinateSystem that transforms from xy * in the data space to lat/lon. * @exception VisADException can't create the necessary VisAD object */ public MapProjection3DAdapter(MapProjection mapProjection) throws VisADException { super(Display.DisplaySpatialCartesianTuple, new Unit[] { CommonUnit.degree, CommonUnit.degree, null }); this.mapProjection = mapProjection; this.theCoordinateSystem = new CachingCoordinateSystem(this.mapProjection); latIndex = mapProjection.getLatitudeIndex(); lonIndex = mapProjection.getLongitudeIndex(); if (mapProjection.isXYOrder()) { xIndex = 0; yIndex = 1; } else { xIndex = 1; yIndex = 0; } /* * System.out.println("latIndex = " + latIndex + * " lonIndex = " + lonIndex + * " xIndex = " + xIndex + * " yIndex = " + yIndex); */ java.awt.geom.Rectangle2D bounds = mapProjection.getDefaultMapArea(); /* * System.out.println("X = " + bounds.getX() + * " Y = "+ bounds.getY() + * " width = "+ bounds.getWidth() + * " height = "+ bounds.getHeight()); */ scaleX = bounds.getWidth() / 2.0; scaleY = bounds.getHeight() / 2.0; offsetX = bounds.getX() + scaleX; offsetY = bounds.getY() + scaleY; /* * System.out.println("scaleX = " + scaleX + * " scaleY = "+ scaleY + * " offsetX = "+ offsetX + * " offsetY = "+ offsetY); */ } /** * Transform latitude/longitude/altitude value to XYZ * * @param latlonalt array of latitude, longitude, altitude values * * @return array of display xyz values. * * @throws VisADException can't create the necessary VisAD object */ public double[][] toReference(double[][] latlonalt) throws VisADException { if ((latlonalt == null) || (latlonalt[0].length < 1)) { return latlonalt; } int numpoints = latlonalt[0].length; call1("toReference(d)", numpoints); double[][] t2 = new double[2][]; t2[latIndex] = latlonalt[0]; t2[lonIndex] = latlonalt[1]; /* */ if (adjustLons) { t2[lonIndex] = (use360) ? GeoUtils.normalizeLongitude360(latlonalt[1]) // ? latlonalt[1] : GeoUtils.normalizeLongitude(latlonalt[1]); } t2 = theCoordinateSystem.fromReference(t2); if (t2 == null) { throw new VisADException( "MapProjection.toReference: " + "Can't do (lat,lon) to (x,y) transformation"); } double x, y; double[] t2x = t2[xIndex]; double[] t2y = t2[yIndex]; for (int i = 0; i < numpoints; i++) { if (Double.isNaN(t2x[i]) || Double.isNaN(t2y[i])) { x = Double.NaN; y = Double.NaN; } else { x = (t2x[i] - offsetX) / scaleX; y = (t2y[i] - offsetY) / scaleY; } latlonalt[0][i] = x; latlonalt[1][i] = y; } call2("toReference(d)", numpoints); return latlonalt; } /** * debug * * @param msg debug * @param numpoints debug */ void call1(String msg, int numpoints) { if (numpoints > 10000) { // Misc.printStack(msg,5,null); Trace.call1("MapProjectionDisplay." + msg, " numpoints = " + numpoints); } } /** * debug * * @param msg debug * @param numpoints debug */ void call2(String msg, int numpoints) { if (numpoints > 10000) { Trace.call2("MapProjectionDisplay." + msg); } } /** * Transform latitude/longitude/altitude value to XYZ * * @param latlonalt array of latitude, longitude, altitude values * * @return array of display xyz values. * * @throws VisADException can't create the necessary VisAD object */ public float[][] toReference(float[][] latlonalt) throws VisADException { if ((latlonalt == null) || (latlonalt[0].length < 1)) { return latlonalt; } int numpoints = latlonalt[0].length; call1("toReference(f)", numpoints); float[][] t2 = new float[2][]; t2[latIndex] = latlonalt[0]; t2[lonIndex] = latlonalt[1]; /* */ if (adjustLons) { t2[lonIndex] = (use360) ? GeoUtils.normalizeLongitude360(latlonalt[1]) // ? latlonalt[1] : GeoUtils.normalizeLongitude(latlonalt[1]); } // call1("mapProjection.fromReference", numpoints); // Trace.msg("MapProjectionDisplay. class=" + mapProjection.getClass().getName()); t2 = theCoordinateSystem.fromReference(t2); // call2("mapProjection.fromReference", numpoints); if (t2 == null) { throw new VisADException( "MapProjection.toReference: " + "Can't do (lat,lon) to (x,y) transformation"); } float x, y; float[] t2ax = t2[xIndex]; float[] t2ay = t2[yIndex]; for (int i = 0; i < numpoints; i++) { float t2x = t2ax[i]; float t2y = t2ay[i]; if ((t2x != t2x) || (t2y != t2y)) { x = Float.NaN; y = Float.NaN; } else { x = (float) ((t2x - offsetX) / scaleX); y = (float) ((t2y - offsetY) / scaleY); } latlonalt[0][i] = x; latlonalt[1][i] = y; } call2("toReference(f)", numpoints); return latlonalt; } /** * Transform display XYZ values to latitude/longitude/altitude * * @param xyz array of Display.DisplaySpatialCartesianTuple XYZ values * @return array of display lat/lon/alt values. * * @throws VisADException can't create the necessary VisAD object */ public double[][] fromReference(double[][] xyz) throws VisADException { if ((xyz == null) || (xyz[0].length < 1)) { return xyz; } int numpoints = xyz[0].length; call1("fromReference(d)", numpoints); for (int i = 0; i < numpoints; i++) { if (Double.isNaN(xyz[0][i]) || Double.isNaN(xyz[0][i])) { continue; } xyz[0][i] = (xyz[0][i] * scaleX + offsetX); xyz[1][i] = (xyz[1][i] * scaleY + offsetY); } double[][] t2 = new double[][] { xyz[xIndex], xyz[yIndex] }; t2 = theCoordinateSystem.toReference(t2); if (t2 == null) { throw new VisADException( "MapProjection.toReference: " + "Can't do (x,y) to (lat,lon) transformation"); } xyz[0] = t2[latIndex]; xyz[1] = t2[lonIndex]; /* */ if (adjustLons) { xyz[1] = (use360) ? GeoUtils.normalizeLongitude360(t2[lonIndex]) // ? t2[lonIndex] : GeoUtils.normalizeLongitude(t2[lonIndex]); } call2("fromReference(d)", numpoints); return xyz; } /** * Transform display XYZ values to latitude/longitude/altitude * * @param xyz array of Display.DisplaySpatialCartesianTuple XYZ values * @return array of display lat/lon/alt values. * * @throws VisADException can't create the necessary VisAD object */ public float[][] fromReference(float[][] xyz) throws VisADException { if ((xyz == null) || (xyz[0].length < 1)) { return xyz; } int numpoints = xyz[0].length; call1("fromReference(f)", numpoints); for (int i = 0; i < numpoints; i++) { if (Float.isNaN(xyz[0][i]) || Float.isNaN(xyz[0][i])) { continue; } xyz[0][i] = (float) (xyz[0][i] * scaleX + offsetX); xyz[1][i] = (float) (xyz[1][i] * scaleY + offsetY); } float[][] t2 = new float[][] { xyz[xIndex], xyz[yIndex] }; t2 = theCoordinateSystem.toReference(t2); if (t2 == null) { throw new VisADException( "MapProjection.toReference: " + "Can't do (x,y) to (lat,lon) transformation"); } xyz[0] = t2[latIndex]; xyz[1] = t2[lonIndex]; /* if (adjustLons) { xyz[1] = (use360) ? GeoUtils.normalizeLongitude360(t2[lonIndex]) // ? t2[lonIndex] : GeoUtils.normalizeLongitude(t2[lonIndex]); } call2("fromReference(f)", numpoints); return xyz; } /** * See if this is equal to the object in question. * * @param obj object in question. * * @return true if they are equal. The two objects are equal if * their MapProjections are equal. */ public boolean equals(Object obj) { if ( !(obj instanceof MapProjection3DAdapter)) { return false; } MapProjection3DAdapter that = (MapProjection3DAdapter) obj; return (that.mapProjection).equals(mapProjection); } /** * Return the MapProjection being used by the CoordinateSystem. * * @return the MapProjection used in this instance */ public MapProjection getMapProjection() { return mapProjection; } /** * Get the scale * * @return the scale (x,y) */ public double[] getScale() { return new double[] { scaleX, scaleY }; } /** * Get the offset * * @return the offset (x_off, y_off) */ public double[] getOffset() { return new double[] { offsetX, offsetY }; } /** * Get the inverted coordinate system * * @return the inverted coordinate system */ public CoordinateSystem getInvertedCoordinateSystem() { return this.mapProjection; } } } >>>>>>> 72c1c20662f16e1b98193bc09dd2baf4024fb28a
Solution content
import visad.RealType;
/*
 * Copyright 1997-2013 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 * 
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package ucar.unidata.view.geoloc;



import ucar.unidata.geoloc.Bearing;
import ucar.unidata.geoloc.LatLonPointImpl;
import ucar.unidata.geoloc.ProjectionImpl;
import ucar.unidata.geoloc.ProjectionPoint;
import ucar.unidata.geoloc.ProjectionRect;
import ucar.unidata.geoloc.projection.LatLonProjection;
import ucar.unidata.ui.FontSelector;
import ucar.unidata.util.GuiUtils;
import ucar.unidata.util.LogUtil;
import ucar.unidata.util.Misc;
import ucar.unidata.util.Trace;

import ucar.visad.GeoUtils;
import ucar.visad.ProjectionCoordinateSystem;
import ucar.visad.display.MapLines;
import ucar.visad.display.ScalarMapSet;
import ucar.visad.quantities.CommonUnits;
import ucar.visad.quantities.GeopotentialAltitude;

import visad.AxisScale;
import visad.CachingCoordinateSystem;
import visad.CommonUnit;
import visad.CoordinateSystem;
import visad.Display;
import visad.DisplayImpl;
import visad.DisplayRealType;
import visad.DisplayTupleType;
import visad.Gridded2DSet;
import visad.InverseLinearScaledCS;
import visad.KeyboardBehavior;
import visad.MouseBehavior;
import visad.ProjectionControl;
import visad.Real;
import visad.RealTuple;
import visad.RealTupleType;
import visad.ScalarMap;
import visad.ScalarType;
import visad.SetType;
import visad.Unit;
import visad.UnitException;
import visad.VisADException;
import visad.VisADRay;

import visad.data.mcidas.AREACoordinateSystem;
import visad.data.mcidas.BaseMapAdapter;

import visad.georef.EarthLocation;
import visad.georef.EarthLocationTuple;
import visad.georef.MapProjection;
import visad.georef.TrivialMapProjection;


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GraphicsDevice;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Rectangle2D;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import java.math.BigDecimal;

import java.net.URL;

import java.rmi.RemoteException;

import java.text.DecimalFormat;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JToolBar;


/**
 * Provides a navigated VisAD DisplayImpl for displaying data.
 * The Projection or MapProjection provides the transformation from
 * lat/lon space to xy space.  There are three modes that can be used
 * with this display - MODE_3D (Java 3D), MODE_2Din3D (2D in Java 3D),
 * MODE_2D (Java 2D).  Performance is better in Java 3D modes. In the 3D
 * mode, RealType.Altitude is mapped to the display Z axis.

* Any displayable data must be able to map to RealType.Latitude, * RealType.Longitude and/or RealType.Altitude.

* * @author Don Murray */ public abstract class MapProjectionDisplay extends NavigatedDisplay { /** * The name of the bearing from center property. */ public static final String CURSOR_BEARING = "cursorBearing"; /** * The name of the range from center property. */ public static final String CURSOR_RANGE = "cursorRange"; /** instance counter */ private static int instance = 0; /** instance locking mutex */ private static Object INSTANCE_MUTEX = new Object(); /** logging category */ private static LogUtil.LogCategory log_ = LogUtil.getLogInstance(MapProjectionDisplay.class.getName()); /** * flag for forcing 2D */ public static boolean force2D = false; /** * The range from center RealType. */ public static RealType CURSOR_RANGE_TYPE = RealType.getRealType("Cursor_Range", CommonUnits.KILOMETER); /** * The bearing from center RealType. */ public static RealType CURSOR_BEARING_TYPE = RealType.getRealType("Cursor_Bearing", CommonUnit.degree); /** ScalarMapf for altitude -> displayAltitudeType */ private ScalarMap altitudeMap = null; /** coordinate system units */ private Unit[] csUnits = null; /** y axis scale */ private AxisScale latScale = null; /** ScalarMapf for latitude -> displayLatitudeType */ private ScalarMap latitudeMap = null; /** x axis scale */ private AxisScale lonScale = null; /** ScalarMapf for longitude -> displayLongitudeType */ private ScalarMap longitudeMap = null; /** default maximum vertical range value */ private double maxVerticalRange = 16000; /** default minimum vertical range value */ private double minVerticalRange = 0; /** vertical axis scale */ private AxisScale verticalScale = null; /** ScalarMap to Display.XAxis */ private ScalarMap xMap = null; /** ScalarMap to Display.YAxis */ private ScalarMap yMap = null; /** ScalarMap to Display.ZAxis */ private ScalarMap zMap = null; /** bearing class for bearing calculations */ private Bearing workBearing = new Bearing(); /** default vertical range unit */ private Unit verticalRangeUnit = CommonUnit.meter; /** Vertical type */ private RealType verticalParameter = RealType.Altitude; /** Set of vertical maps */ private VerticalMapSet verticalMapSet = new VerticalMapSet(); /** use 0-360 for longitude range */ private boolean use360 = true; /** number format for axis labels */ DecimalFormat labelFormat = new DecimalFormat("####0.0"); /** flag for whether we've been initialized */ private boolean init = false; /** cursor location for bearing calculations */ private LatLonPointImpl cursorLLP = new LatLonPointImpl(); /** centerpoint for bearing calculations */ private LatLonPointImpl centerLLP = new LatLonPointImpl(); /** flag for adjusting lons or not */ private boolean adjustLons = false; /** The coordinate system for the display */ private CoordinateSystem coordinateSystem; /** * The cursor altitude. * @serial */ private volatile Real cursorBearing; /** * The cursor altitude. * @serial */ private volatile Real cursorRange; /** The display's Altitude DisplayRealType */ private DisplayRealType displayAltitudeType; /** The display's Latitude DisplayRealType */ private DisplayRealType displayLatitudeType; /** The display's Longitude DisplayRealType */ private DisplayRealType displayLongitudeType; /** the display tuple type */ private DisplayTupleType displayTupleType; /** The lat scale info */ private LatLonAxisScaleInfo latScaleInfo; /** The lon scale info */ private LatLonAxisScaleInfo lonScaleInfo; /** The MapProjection */ private MapProjection mapProjection; /** * Constructs an instance with the specified MapProjection */ protected MapProjectionDisplay() {} /** * Constructs an instance with the specified MapProjection * CoordinateSystem and display. * * @param projection map projection CS * @param display display to use * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ protected MapProjectionDisplay(MapProjection projection, DisplayImpl display) throws VisADException, RemoteException { super(display); // mapProjection = projection; // coordinateSystem = makeCoordinateSystem(projection); setMapProjection(projection); initializeClass(); } /** * Initializes an instance with the specified MapProjection * CoordinateSystem and display. * * @param projection map projection CS * @param display display to use * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ protected void init(MapProjection projection, DisplayImpl display) throws VisADException, RemoteException { super.init(display); setMapProjection(projection); initializeClass(); } /** * Set up the display. Any additional work should be done in * a subclass's intializeClass() method, which should call * super.initializeClass() first. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ protected void initializeClass() throws VisADException, RemoteException { super.initializeClass(); setDisplayTypes(); } /** * Get an instance of a MapProjectionDisplay using the mode specified * and the default projection. * * @param mode mode to use * * @return a MapProjectionDisplay * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ public static MapProjectionDisplay getInstance(int mode) throws VisADException, RemoteException { return getInstance(null, mode); } /** * Get an instance of a MapProjectionDisplay using the mode specified * and the MapProjection. * * @param mode mode to use * @param p initial MapProjection for display * * @return a MapProjection display of the correct mode and projection * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ public static MapProjectionDisplay getInstance(MapProjection p, int mode) throws VisADException, RemoteException { return getInstance(p, mode, false, null); } /** * Get an instance of a MapProjectionDisplay using the mode specified * and the MapProjection. * * @param p map projection * @param mode mode * @param offscreen true if offscreen * @param dimension dimension of display * * @return the instance * * @throws RemoteException Java RMI problem * @throws VisADException problem creating the display or some component */ public static MapProjectionDisplay getInstance(MapProjection p, int mode, boolean offscreen, Dimension dimension) throws VisADException, RemoteException { return getInstance(p, mode, offscreen, dimension, null); } /** * Get an instance of a MapProjectionDisplay using the mode specified * and the MapProjection. * * @param p map projection * @param mode mode * @param offscreen true if offscreen * @param dimension dimension of display * @param screen screen to display it on * * @return the instance * * @throws RemoteException Java RMI problem * @throws VisADException problem creating the display or some component */ public static MapProjectionDisplay getInstance(MapProjection p, int mode, boolean offscreen, Dimension dimension, GraphicsDevice screen) throws VisADException, RemoteException { if (p == null) { Trace.call1("MapProjectionDisplay.getInstance:makeProjection"); p = makeDefaultMapProjection(); Trace.call2("MapProjectionDisplay.getInstance:makeProjection"); } if (((mode == MODE_3D) || (mode == MODE_2Din3D)) && !force2D) { Trace.call1( "MapProjectionDisplay.getInstance:new MapProjectionDisplayJ3D"); MapProjectionDisplay mpd = new MapProjectionDisplayJ3D(p, mode, offscreen, dimension, screen); Trace.call2( "MapProjectionDisplay.getInstance:new MapProjectionDisplayJ3D"); return mpd; } else { return new MapProjectionDisplayJ2D(p); } } /** * Destroy this class */ public void destroy() { super.destroy(); } // private List keyboardBehaviors; /** * Add a KeyboardBehavior to this class * * @param behavior behavior to add */ public abstract void addKeyboardBehavior(KeyboardBehavior behavior); /** * Set the make lat scale * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void makeLatScales() throws VisADException, RemoteException { setDisplayInactive(); if (latScale != null) { latScale.setVisible(getLatScaleInfo().isVisible()); latScale.setLabelRelief(getLatScaleInfo().isLabelRelief()); updateLatScale(latScale); } setDisplayActive(); } /** * Calculate minimum latitude according to VisAD. * * @return the lat base */ private double calcLatBase() { double[] xRange = xMap.getRange(); double[] yRange = yMap.getRange(); double[] zRange = (zMap != null) ? zMap.getRange() : new double[] { 0, 0 }; EarthLocation el = getEarthLocation(xRange[0], yRange[0], zRange[0]); double base = el.getLatitude().getValue(); final double DELTA = (xRange[1] - xRange[0]) / 100; if (Double.isNaN(base)) { outerloop: for (double y = yRange[0]; y < yRange[1]; y = y + DELTA) { for (double x = xRange[0]; x < xRange[1]; x = x + DELTA) { el = getEarthLocation(x, y, zRange[0]); base = el.getLatitude().getValue(); if ( !Double.isNaN(base)) { break outerloop; } } } } return base; } /** * Calculate maximum latitude according to VisAD. * * @return the lat top */ private double calcLatTop() { final double LAT_MAX = 90; double[] xRange = xMap.getRange(); double[] yRange = yMap.getRange(); double[] zRange = (zMap != null) ? zMap.getRange() : new double[] { 0, 0 }; EarthLocation el = getEarthLocation(xRange[0], yRange[1], zRange[0]); double top = el.getLatitude().getValue(); final double DELTA = (xRange[1] - xRange[0]) / 100; if (Double.isNaN(top)) { outerloop: for (double y = yRange[1]; y > yRange[0]; y = y - DELTA) { for (double x = xRange[0]; x < xRange[1]; x = x + DELTA) { el = getEarthLocation(x, y, zRange[0]); top = el.getLatitude().getValue(); if ( !Double.isNaN(top)) { break outerloop; } } } } return (top > LAT_MAX) ? LAT_MAX : top; } /** * Calculate minimum longitude according to VisAD. * * @return the lon base */ private double calcLonBase() { double[] xRange = xMap.getRange(); double[] yRange = yMap.getRange(); double[] zRange = (zMap != null) ? zMap.getRange() : new double[] { 0, 0 }; EarthLocation el = getEarthLocation(xRange[0], yRange[0], zRange[0]); double base = el.getLongitude().getValue(); final double DELTA = (xRange[1] - xRange[0]) / 100; if (Double.isNaN(base)) { outerloop: for (double x = xRange[0]; x < xRange[1]; x = x + DELTA) { for (double y = yRange[0]; y < yRange[1]; y = y + DELTA) { el = getEarthLocation(x, y, zRange[0]); base = el.getLongitude().getValue(); if ( !Double.isNaN(base)) { break outerloop; } } } } return base; } /** * Calculate maximum longitude according to VisAD. * * @return the lon top */ private double calcLonTop() { double[] xRange = xMap.getRange(); double[] yRange = yMap.getRange(); double[] zRange = (zMap != null) ? zMap.getRange() : new double[] { 0, 0 }; EarthLocation el = getEarthLocation(xRange[1], yRange[1], zRange[0]); double top = el.getLongitude().getValue(); final double DELTA = (xRange[1] - xRange[0]) / 100; if (Double.isNaN(top)) { outerloop: for (double x = xRange[1]; x > xRange[0]; x = x - DELTA) { for (double y = yRange[1]; y > yRange[0]; y = y - DELTA) { el = getEarthLocation(x, y, zRange[0]); top = el.getLongitude().getValue(); if ( !Double.isNaN(top)) { break outerloop; } } } } return top; } /** * Set the lon scales * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void makeLonScales() throws VisADException, RemoteException { setDisplayInactive(); if (lonScale != null) { lonScale.setVisible(getLonScaleInfo().isVisible()); lonScale.setLabelRelief(getLonScaleInfo().isLabelRelief()); if (isSouthPole()) { updateSouthPoleLonScale(lonScale); } else { updateLonScale(lonScale); } } setDisplayActive(); } /** * Checks if current project is over the South Pole. * * @return true, if is south pole */ private boolean isSouthPole() { double[] xRange = xMap.getRange(); double[] yRange = yMap.getRange(); double[] zRange = (zMap != null) ? zMap.getRange() : new double[] { 0, 0 }; EarthLocation ll = getEarthLocation(xRange[0], yRange[0], zRange[0]); EarthLocation lr = getEarthLocation(xRange[1], yRange[0], zRange[0]); EarthLocation ur = getEarthLocation(xRange[1], yRange[1], zRange[0]); EarthLocation ul = getEarthLocation(xRange[0], yRange[1], zRange[0]); return ((ll.getLongitude().getValue() < ul.getLongitude().getValue()) && (ul.getLongitude().getValue() < ur.getLongitude().getValue()) && (ur.getLongitude() .getValue() < lr.getLongitude().getValue())); } /** * Method to update lat scale. * * @param scale AxisScale to update * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void updateLatScale(AxisScale scale) throws VisADException, RemoteException { final int LAT_MIN = -90; final int LAT_MAX = 90; final double DELTA = 0.0001; double bottomLat = calcLatBase(); double topLat = calcLatTop(); Hashtable labelTable = new Hashtable(); double base = Misc.parseNumber(getLatScaleInfo().getBaseLabel()); List majorTicks = new ArrayList(); int minorTickInc = getLatScaleInfo().getMinorDivision(); List minorTicks = new ArrayList(); double inc = Misc.parseNumber(getLatScaleInfo().getIncrement()); // In case user inputs something bogus. if ((base < LAT_MIN) || (base > LAT_MAX)) { base = bottomLat; } outerloop: for (double i = base; i < topLat; i += inc / minorTickInc) { if (i < bottomLat) { // Latitudes that are not in this range are not visible. continue; } EarthLocationTuple elt = new EarthLocationTuple(i, calcLonBase(), 0); double[] values = newtonLat(elt, 0); for (int j = 0; j < values.length; j++) { if (Double.isNaN(values[j])) { continue outerloop; } } Double d = round(values[1], 3, BigDecimal.ROUND_HALF_UP); double mm = (i - base) % inc; if ((mm < DELTA) || (mm > (inc - DELTA))) { // Must account for numerical leeway. } majorTicks.add(d); labelTable.put(d, CoordinateFormat.formatLatitude(i, latScaleInfo.getCoordFormat())); } else { minorTicks.add(d); } } finalizeAxis(scale, getLatScaleInfo().getLabel(), labelTable, majorTicks, minorTicks, getLatScaleInfo().getFont()); } /** * Method to update lon scale. * * @param scale AxisScale to update * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void updateLonScale(AxisScale scale) throws VisADException, RemoteException { final int MAX_LON = 360; final int MIN_LON = -180; final double DELTA = 0.0001; double leftLon = calcLonBase(); double rightLon = calcLonTop(); boolean isMeridianCross = leftLon > rightLon; Hashtable labelTable = new Hashtable(); double base = Misc.parseNumber(getLonScaleInfo().getBaseLabel()); List majorTicks = new ArrayList(); int minorTickInc = getLonScaleInfo().getMinorDivision(); List minorTicks = new ArrayList(); double inc = Misc.parseNumber(getLonScaleInfo().getIncrement()); List increment = new LinkedList(); //Need to normalize the base in some circumstances if (rightLon - base > 360) { base += 360; } // Northern hemisphere meridian cross leftLon = isMeridianCross ? leftLon - MAX_LON : leftLon; // In case user inputs something bogus. if ((base < MIN_LON) || (base > MAX_LON)) { base = leftLon; } for (double i = base; i < rightLon; i += inc / minorTickInc) { if (i < leftLon) { // Longitudes that are not in this range are not visible. continue; } increment.add(i); } outerloop: for (Double i : increment) { EarthLocationTuple elt = new EarthLocationTuple(calcLatBase(), i, 0); double[] values = newtonLon(elt, 0); for (int j = 0; j < values.length; j++) { if (Double.isNaN(values[j])) { continue outerloop; } } Double d = round(values[0], 3, BigDecimal.ROUND_HALF_UP); double mm = (i - base) % inc; if ((mm < DELTA) || (mm > (inc - DELTA))) { // Must account for numerical leeway. majorTicks.add(d); labelTable.put(d, CoordinateFormat.formatLongitude(i, lonScaleInfo.getCoordFormat(), lonScaleInfo.isUse360())); } else { minorTicks.add(d); } } finalizeAxis(scale, getLonScaleInfo().getLabel(), labelTable, majorTicks, minorTicks, getLonScaleInfo().getFont()); } /** * Update south pole lon scale. * * @param scale the scale * @throws VisADException the vis ad exception * @throws RemoteException the remote exception */ private void updateSouthPoleLonScale(AxisScale scale) throws VisADException, RemoteException { final int MAX_LON = 360; final int MIN_LON = -180; final double DELTA = 0.0001; double leftLon = calcLonBase(); double rightLon = calcLonTop() - MAX_LON; double bottomLat = calcLatBase(); Hashtable labelTable = new Hashtable(); double base = Misc.parseNumber(getLonScaleInfo().getBaseLabel()); List majorTicks = new ArrayList(); int minorTickInc = getLonScaleInfo().getMinorDivision(); List minorTicks = new ArrayList(); double inc = Misc.parseNumber(getLonScaleInfo().getIncrement()); List increment = new LinkedList(); // In case the user enters something bogus if ((base < MIN_LON) || (base > MAX_LON)) { base = leftLon; } for (double i = base; i > rightLon; i -= inc / minorTickInc) { if (i > leftLon) { // Longitudes that are not in this range are not visible. continue; } increment.add(i); } } for (Double i : increment) { EarthLocationTuple elt = new EarthLocationTuple(bottomLat, i, 0); double[] values = newtonLon(elt, 0); Double d = new Double(values[0]); double mm = (base - i) % inc; if ((mm < DELTA) || (mm > (inc - DELTA))) { // Must account for numerical leeway. majorTicks.add(d); labelTable.put(d, CoordinateFormat.formatLongitude(i, lonScaleInfo.getCoordFormat(), lonScaleInfo.isUse360())); } else { minorTicks.add(d); } } finalizeAxis(scale, getLonScaleInfo().getLabel(), labelTable, majorTicks, minorTicks, getLonScaleInfo().getFont()); } /** * Finalize axis labeling. * * @param scale the scale * @param title the title * @param labelTable the label table * @param majorTicks the major ticks * @param minorTicks the minor ticks * @param axisFont the axis font * @throws VisADException the vis ad exception */ private void finalizeAxis( AxisScale scale, String title, Hashtable labelTable, List majorTicks, List minorTicks, Font axisFont) throws VisADException { double[] mjt = new double[majorTicks.size()]; double[] mnt = new double[minorTicks.size()]; for (int i = 0; i < mjt.length; i++) { mjt[i] = majorTicks.get(i); } for (int i = 0; i < mnt.length; i++) { mnt[i] = minorTicks.get(i); } scale.setAutoComputeTicks(false); scale.setSnapToBox(true); scale.setMajorTicks(mjt); scale.setMinorTicks(mnt); scale.setTitle(title); scale.setLabelTable(labelTable); scale.setTicksVisible(true); scale.setMajorTickSpacing(0); scale.setMinorTickSpacing(0); if ((axisFont != null) && axisFont.getName().equals( FontSelector.DEFAULT_FONT.getName())) { scale.setFont((Font) null); scale.setLabelSize(axisFont.getSize()); } else { scale.setFont(axisFont); } } /** * Numerically arriving at the screen coordinates needed for the intersection between the wire frame axis * and the latitude. * * @param elt the earth location tuple. * @param cnt must maintain a count in case of numerical problems * @return the spatial coordinates * @throws RemoteException the remote exception * @throws VisADException the vis ad exception */ private double[] newtonLat(EarthLocationTuple elt, int cnt) throws RemoteException, VisADException { final double DELTA = 0.02; final double DELTA2 = 0.001; final int MAX_CNT = 10; double[] values = getSpatialCoordinates(elt).getValues(); values[0] = -1; EarthLocation el = getEarthLocation(values); // We are in no man's land so must adjust until we find Earth. while (Double.isNaN(el.getLatitude().getValue())) { values[0] = values[0] + DELTA2; el = getEarthLocation(values); if (values[0] > 1) { return new double[] { Double.NaN, Double.NaN, Double.NaN }; } double diff = elt.getLatitude().getValue() - el.getLatitude().getValue(); if ((Math.abs(diff) < DELTA) || (cnt > MAX_CNT)) { // cnt > 10 in case solution does not converge. Safety valve. return getSpatialCoordinates(el).getValues(); } else { EarthLocationTuple t = new EarthLocationTuple(el.getLatitude().getValue() + diff, el.getLongitude().getValue(), 0); return newtonLat(t, ++cnt); } } /** * Numerically arriving at the screen coordinates needed for the intersection between the wire frame axis * and the longitude. * * @param elt the earth location tuple. * @param cnt must maintain a count in case of numerical problems * @return the spatial coordinates * @throws RemoteException the remote exception * @throws VisADException the vis ad exception */ private double[] newtonLon(EarthLocationTuple elt, int cnt) throws RemoteException, VisADException { final double DELTA = 0.02; final double DELTA2 = 0.001; final int MAX_CNT = 10; double[] values = getSpatialCoordinates(elt).getValues(); values[1] = -1; EarthLocation el = getEarthLocation(values); // We are in no man's land so must adjust until we find Earth. while (Double.isNaN(el.getLatitude().getValue())) { values[1] = values[1] + DELTA2; el = getEarthLocation(values); if (values[1] > 1) { return new double[] { Double.NaN, Double.NaN, Double.NaN }; } } double diff = el.getLongitude().getValue() - elt.getLongitude().getValue(); if ((Math.abs(diff) < DELTA) || (cnt > MAX_CNT)) { // cnt > 10 in case solution does not converge. Safety valve. return getSpatialCoordinates(elt).getValues(); } else { EarthLocationTuple t = new EarthLocationTuple(el.getLatitude().getValue(), el.getLongitude().getValue() - diff, 0); return newtonLon(t, ++cnt); } /** * Method to update the properties of an AxisScale * * @param scale AxisScale to update * @param title Title * @param maxmin max/min limits of axis * @param bottom value for lower limit * @param top value for upper limit * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void updateVertScale(AxisScale scale, String title, double[] maxmin, double bottom, double top) throws VisADException, RemoteException { scale.setVisible(getVerticalRangeVisible()); scale.setSnapToBox(true); scale.setTitle(title); Hashtable labelTable = new Hashtable(); labelTable.put(new Double(maxmin[0]), labelFormat.format(bottom)); labelTable.put(new Double(maxmin[1]), labelFormat.format(top)); scale.setLabelTable(labelTable); scale.setTickBase(maxmin[0]); scale.setMajorTickSpacing(Math.abs(maxmin[1] - maxmin[0])); scale.setMinorTickSpacing(Math.abs(maxmin[1] - maxmin[0])); } /** * Set the vertical axis scale * * @throws VisADException problem creating some VisAD object * @throws RemoteException problem creating remote object */ private void makeVerticalScale() throws VisADException, RemoteException { if (verticalScale == null) { return; } setDisplayInactive(); double[] zRange = zMap.getRange(); String title = verticalParameter.getName() + "(" + verticalRangeUnit.getIdentifier() + ")"; updateVertScale(verticalScale, title, zRange, minVerticalRange, maxVerticalRange); /* * verticalScale.setSnapToBox(true); * verticalScale.setTitle( * Hashtable labelTable = new Hashtable(); * labelTable.put(new Double(zRange[0]), * labelFormat.format(minVerticalRange)); * labelTable.put(new Double(zRange[1]), * labelFormat.format(maxVerticalRange)); * verticalScale.setLabelTable(labelTable); * verticalScale.setTickBase(zRange[0]); * verticalScale.setMajorTickSpacing(Math.abs(zRange[1]-zRange[0])); * verticalScale.setMinorTickSpacing(Math.abs(zRange[1]-zRange[0])); */ setDisplayActive(); } /** * Define the set of spatial scalar maps that this display will * use. Every time a new projection is set, a new set of DisplayTypes * is created with a coordinate system for transposing between * projection space and xyz space. The mappings are: *

    *
  • RealType.Latitude -> getDisplayLatitudeType() *
  • RealType.Longitude -> getDisplayLongitudeType() *
  • RealType.Altitude -> getDisplayAltitudeType() *
* This is called on construction of the display or with every rebuild. * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ private void setSpatialScalarMaps() throws VisADException, RemoteException { setDisplayInactive(); ScalarMapSet mapSet = new ScalarMapSet(); if (latitudeMap != null) { removeScalarMap(latitudeMap); } latitudeMap = new ScalarMap(RealType.Latitude, displayLatitudeType); mapSet.add(latitudeMap); latitudeMap.setRangeByUnits(); latitudeMap.setScaleEnable(true); if (longitudeMap != null) { removeScalarMap(longitudeMap); } longitudeMap = new ScalarMap(RealType.Longitude, displayLongitudeType); mapSet.add(longitudeMap); longitudeMap.setRangeByUnits(); longitudeMap.setScaleEnable(true); if (getDisplayMode() == MODE_3D) { ScalarMapSet newVertMaps = new ScalarMapSet(); if (verticalMapSet.size() > 0) { for (Iterator iter = verticalMapSet.iterator(); iter.hasNext(); ) { ScalarType r = ((ScalarMap) iter.next()).getScalar(); ScalarMap newMap = new ScalarMap(r, displayAltitudeType); newMap.setScaleEnable(true); if (r.equals(RealType.Altitude)) { altitudeMap = newMap; } newVertMaps.add(newMap); } } else { // add Altitude at least altitudeMap = new ScalarMap(RealType.Altitude, displayAltitudeType); altitudeMap.setScaleEnable(true); newVertMaps.add(altitudeMap); } removeScalarMaps(verticalMapSet); verticalMapSet.clear(); verticalMapSet.add(newVertMaps); setVerticalRange(minVerticalRange, maxVerticalRange); setVerticalRangeUnit(verticalRangeUnit); mapSet.add(verticalMapSet); } if ( !init) { xMap = new ScalarMap(RealType.XAxis, Display.XAxis); xMap.setRange(-1.0, 1.0); mapSet.add(xMap); xMap.setScaleEnable(true); lonScale = xMap.getAxisScale(); yMap = new ScalarMap(RealType.YAxis, Display.YAxis); yMap.setRange(-1.0, 1.0); mapSet.add(yMap); yMap.setScaleEnable(true); latScale = yMap.getAxisScale(); if (getDisplayMode() == MODE_3D) { zMap = new ScalarMap(RealType.ZAxis, Display.ZAxis); zMap.setRange(-1.0, 1.0); mapSet.add(zMap); zMap.setScaleEnable(true); verticalScale = zMap.getAxisScale(); } init = true; } addScalarMaps(mapSet); setDisplayActive(); } /** * Add a new mapping of this type to the vertical coordinate * * @param newVertType RealType of map * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ public void addVerticalMap(RealType newVertType) throws VisADException, RemoteException { if (getDisplayMode() == MODE_3D) { Unit u = newVertType.getDefaultUnit(); if ( !(Unit.canConvert(u, CommonUnit.meter) || Unit.canConvert( u, GeopotentialAltitude.getGeopotentialMeter()))) { throw new VisADException("Unable to handle units of " + newVertType); } ScalarMap newMap = new ScalarMap(newVertType, getDisplayAltitudeType()); setVerticalMapUnit(newMap, verticalRangeUnit); newMap.setRange(minVerticalRange, maxVerticalRange); verticalMapSet.add(newMap); addScalarMaps(verticalMapSet); } } /** * Remove a new mapping of this type to the vertical coordinate * * @param vertType RealType of map * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ public void removeVerticalMap(RealType vertType) throws VisADException, RemoteException { if (getDisplayMode() == MODE_3D) { ScalarMapSet sms = new ScalarMapSet(); for (Iterator iter = verticalMapSet.iterator(); iter.hasNext(); ) { ScalarMap s = (ScalarMap) iter.next(); if (((RealType) s.getScalar()).equals(vertType)) { sms.add(s); } } if ( !(sms.size() == 0)) { verticalMapSet.remove(sms); removeScalarMaps(sms); } } } /** * Set the Unit of the vertical range * * @param newUnit unit of range * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ public void setVerticalRangeUnit(Unit newUnit) throws VisADException, RemoteException { super.setVerticalRangeUnit(newUnit); if ((newUnit != null) && Unit.canConvert(newUnit, CommonUnit.meter)) { verticalMapSet.setVerticalUnit(newUnit); verticalRangeUnit = newUnit; } makeVerticalScale(); } /** * Sets the lat scale info. * * @param axisScaleInfo the new lat scale info * @throws RemoteException the remote exception * @throws VisADException the vis ad exception */ public void setLatScaleInfo(LatLonAxisScaleInfo axisScaleInfo) throws RemoteException, VisADException { this.latScaleInfo = axisScaleInfo; makeLatScales(); } /** * Sets the lon scale info. * * @param axisScaleInfo the new lon scale info * @throws RemoteException the remote exception * @throws VisADException the vis ad exception */ public void setLonScaleInfo(LatLonAxisScaleInfo axisScaleInfo) throws RemoteException, VisADException { this.lonScaleInfo = axisScaleInfo; makeLonScales(); } /** * Gets the lat scale info. * * @return the lat scale info */ public LatLonAxisScaleInfo getLatScaleInfo() { // The 2nd null check is kludgy, but sometimes LatLonAxisScaleInfo //deserialization will have problems in which case all fields //are null. if ((latScaleInfo == null) || (latScaleInfo.getBaseLabel() == null)) { LatLonAxisScaleInfo lsi = new LatLonAxisScaleInfo(); latScaleInfo = lsi; lsi.setLabel("Latitude"); lsi.setIncrement(10 + ""); lsi.setMinorDivision(1); lsi.setVisible(true); lsi.setCoordFormat(LatLonAxisScaleInfo.COORD_FORMATS[0]); lsi.setUse360(latScaleInfo.isUse360()); double base = calcLatBase(); double end = calcLatTop(); double inc = Math.abs(end - base) / 10d; lsi.setIncrement(makeIncrementNice(inc) + ""); base = (Math.floor(base / 10)) * 10; // Make base nice (i.e. multiple of 10) base = (base < -90) ? -90 : base; base = (base > 90) ? 90 : base; latScaleInfo.setBaseLabel(base + ""); } return latScaleInfo; } /** * Make increment nice for the user. * * @param inc the not nice inc * @return the nice increment */ private double makeIncrementNice(double inc) { //Somewhat arbitrary double[] niceNums = { 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 15, 20, 25, 30, 40, 50, 60, 70, 80 }; for (int i = 0; i < niceNums.length; i++) { if (inc < niceNums[i]) { return niceNums[i]; } } return 10; } /** * Gets the lon scale info. * * @return the lon scale info */ public LatLonAxisScaleInfo getLonScaleInfo() { // The 2nd null check is kludgy, but sometimes LatLonAxisScaleInfo //deserialization will have problems in which case all fields //are null. if ((lonScaleInfo == null) || (lonScaleInfo.getBaseLabel() == null)) { LatLonAxisScaleInfo lsi = new LatLonAxisScaleInfo(); lonScaleInfo = lsi; lsi.setLabel("Longitude"); lsi.setMinorDivision(1); lsi.setVisible(true); lsi.setCoordFormat(LatLonAxisScaleInfo.COORD_FORMATS[0]); lsi.setUse360(false); double base = calcLonBase(); double end = calcLonTop(); double inc; if (isSouthPole()) { inc = Math.abs((end - 360) - base) / 10d; } else { inc = Math.abs(end - ((base > end) ? base - 360 : base)) / 10d; } // Must deal with meridian lsi.setIncrement(makeIncrementNice(inc) + ""); // base = (Math.floor(base / 10)) * 10; // Make base nice (i.e. multiple of 10) base = (base < -180) ? -180 : base; base = (base > 180) ? (base - 360) : base; lonScaleInfo.setBaseLabel((isSouthPole() ? Math.floor(base) : Math.ceil(base)) + ""); } return lonScaleInfo; } /** * Rounding convenience method. * * @param unrounded the unrounded * @param precision the precision * @param roundingMode the rounding mode * @return the rounded value */ private static double round(double unrounded, int precision, int roundingMode) { BigDecimal bd = new BigDecimal(unrounded); BigDecimal rounded = bd.setScale(precision, roundingMode); return rounded.doubleValue(); } /** * Set the range of the vertical coordinate * * @param min minimum value for vertical axis * @param max maximum value for vertical axis * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ public void setVerticalRange(double min, double max) throws VisADException, RemoteException { super.setVerticalRange(min, max); verticalMapSet.setVerticalRange(min, max); minVerticalRange = min; maxVerticalRange = max; makeVerticalScale(); } /** * Get the range of the vertical coordinate (Altitude) * * @return array of {min, max} range. */ public double[] getVerticalRange() { ScalarMap vertMap = getAltitudeMap(); return (vertMap != null) ? vertMap.getRange() : new double[] { minVerticalRange, maxVerticalRange }; } /** * Sets the cursor range from center property. * * @param range The cursor range from center. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ protected void setCursorRange(Real range) throws VisADException, RemoteException { Real oldRange = cursorRange; cursorRange = range; firePropertyChange(CURSOR_RANGE, oldRange, cursorRange); } /** * Gets the cursor range from center property. * * @return The currently-selected range. May be * null. */ public Real getCursorRange() { return cursorRange; } /** * Sets the cursor bearing (degrees) from center property. * This implementation uses a great circle distance. * * @param bearing The cursor bearing from center. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ protected void setCursorBearing(Real bearing) throws VisADException, RemoteException { Real oldBearing = cursorBearing; cursorBearing = bearing; firePropertyChange(CURSOR_BEARING, oldBearing, cursorBearing); } /** * Gets the cursor bearing from center property. * * @return The currently-selected bearing. May be * null. */ public Real getCursorBearing() { return cursorBearing; } /** * Set the view for 3D. The views are based on the original display * as follows: *
     *                        NORTH
     *                      _________
     *                    W |       | E
     *                    E |  TOP  | A
     *                    S |       | S
     *                    T |_______| T
     *                        SOUTH
     * 
* @param view one of the static view fields (NORTH_VIEW, SOUTH_VIEW, .. * etc). */ public void setView(int view) { if (getDisplayMode() != MODE_3D) { return; } } /** * Accessor method for the DisplayLatitudeType (i.e., what * RealType.Latitude is mapped to). * * @return the DisplayRealType that RealType.Latitude is mapped to */ public DisplayRealType getDisplayLatitudeType() { return displayLatitudeType; } /** * Accessor method for the DisplayLongitudeType (i.e., what * RealType.Longitude is mapped to). * * @return the DisplayRealType that RealType.Longitude is mapped to */ public DisplayRealType getDisplayLongitudeType() { return displayLongitudeType; } /** * Accessor method for the DisplayAltitudeType (i.e., what * RealType.Altitude is mapped to). * * @return the DisplayRealType that RealType.Altitude is mapped to */ public DisplayRealType getDisplayAltitudeType() { return displayAltitudeType; } /** * Accessor method for the vertical coordinate ScalarMap (i.e., what * getDisplayAltitudeType is mapped from). * @return the ScalarMap that the vertical coordinate is mapped to */ protected ScalarMap getAltitudeMap() { return altitudeMap; } /** * Define the map projection using a Projection interface * * @param projection Projection to use * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ public void setMapProjection(ProjectionImpl projection) throws VisADException, RemoteException { setMapProjection(new ProjectionCoordinateSystem(projection), true); } /** * Define the map projection using a MapProjection type CoordinateSystem * * @param mapProjection map projection coordinate system * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ public void setMapProjection(MapProjection mapProjection) throws VisADException, RemoteException { setMapProjection(mapProjection, true); } /** * Define the map projection using a MapProjection type CoordinateSystem * * @param mapProjection map projection coordinate system * * @param resetDisplayProjMatrix yes/no change the current VisAD Display projection matrix * when the map projection is changed. * * @throws VisADException Couldn't create necessary VisAD object * @throws RemoteException Couldn't create a remote object */ public void setMapProjection(MapProjection mapProjection, boolean resetDisplayProjMatrix) throws VisADException, RemoteException { if (mapProjection.equals(this.mapProjection)) { return; } this.mapProjection = mapProjection; coordinateSystem = makeCoordinateSystem(mapProjection); // Need to reset these for new projection. this.latScaleInfo = null; this.lonScaleInfo = null; resetMapParameters(resetDisplayProjMatrix); EarthLocation el = getEarthLocation(0, 0, 0); centerLLP.set(el.getLatitude().getValue(CommonUnit.degree), el.getLongitude().getValue(CommonUnit.degree)); } /** * Get the MapProjection that defines the xy mapping of this * MapProjectionDisplay. * * @return MapProjection being used. */ public MapProjection getMapProjection() { return mapProjection; } /** * Set the map area from the projection rectangle * * @param mapArea map area as lat/lon lines * * @throws RemoteException problem setting remote data * @throws VisADException problem creating VisAD data object */ public void setMapArea(ProjectionRect mapArea) throws VisADException, RemoteException { if (coordinateSystem == null) { throw new VisADException("Navigation hasn't been set yet"); } // System.out.println("Map Area = " + mapArea); MapProjection project = ((MapProjection3DAdapter) coordinateSystem).getMapProjection(); // get the corners in latlon coords ProjectionPoint ppMax = mapArea.getMaxPoint(); ProjectionPoint ppMin = mapArea.getMinPoint(); // System.out.println("ppMax:" + ppMax); // System.out.println("ppMin:" + ppMin); float[][] values = new float[2][2]; values[0][0] = (float) ppMax.getY(); values[0][1] = (float) ppMin.getY(); values[1][0] = (float) ppMax.getX(); values[1][1] = (float) ppMin.getX(); // values = project.toReference(values); Gridded2DSet region = new Gridded2DSet(RealTupleType.LatitudeLongitudeTuple, values, 2); setMapRegion(region); } /** * Set the map region to be displayed. The MathType of the domain * of the set must be either RealTupleType.SpatialCartesian2DTuple, * RealTupleType.SpatialEarth2DTuple, or * RealTupleType.LatitudeLongitudeTuple. * * @param region Gridded2DSet containing the range of for the axis. * * @throws VisADException invalid domain or null set * @throws RemoteException Couldn't create a remote object */ public void setMapRegion(Gridded2DSet region) throws VisADException, RemoteException { if (region == null) { throw new VisADException("Region can't be null"); } if (region.isMissing()) { return; } // Check the type. We need to work in XYZ coordinates Gridded2DSet xyRegion; RealTupleType regionType = ((SetType) region.getType()).getDomain(); if (regionType.equals(RealTupleType.SpatialCartesian2DTuple)) { xyRegion = region; } else if (regionType.equals(RealTupleType.SpatialEarth2DTuple) || regionType.equals( RealTupleType.LatitudeLongitudeTuple)) { // transform to x/y int latIndex = regionType.equals(RealTupleType.LatitudeLongitudeTuple) ? 0 : 1; int lonIndex = (latIndex == 0) ? 1 : 0; float[][] values = region.getSamples(true); float xy[][] = new float[3][values[0].length]; xy[0] = values[latIndex]; xy[1] = values[lonIndex]; xy = coordinateSystem.toReference(xy); values[0] = xy[0]; values[1] = xy[1]; xyRegion = new Gridded2DSet(RealTupleType.SpatialCartesian2DTuple, values, 2); } else { throw new VisADException("Invalid domain for region " + regionType); } // System.out.println(xyRegion); // Okay, now we have our region, let's get cracking // First, let's figure out our component size. Dimension d = getComponent().getSize(); // if running the isl non interactively, d is not assigned, the component is // one layer deeper. if ((d.width == 0) || (d.height == 0)) { JPanel jp = (JPanel) getComponent(); d = jp.getComponent(0).getSize(); } // System.out.println("Component size = " + d); int componentCenterX = d.width / 2; int componentCenterY = d.height / 2; /* * System.out.println( * "Component Center point = " + * componentCenterX +","+componentCenterY); */ // Now let's get the MouseBehavior so we can get some display coords MouseBehavior behavior = getDisplay().getDisplayRenderer().getMouseBehavior(); ProjectionControl proj = getDisplay().getProjectionControl(); double[] aspect = getDisplayAspect(); // Misc.printArray("aspect", aspect); // We have to figure the component coordinates of the region. // To do this, we calculate the number of display units per pixel // in the x and y. This logic comes from visad.MouseHelper. // Basically, we find out the current matrix, how much we should // scale, translate and rotate, and then apply the new matrix. double[] center_ray = behavior.findRay(componentCenterX, componentCenterY).position; // Misc.printArray("center_ray", center_ray); double[] center_ray_x = behavior.findRay(componentCenterX + 1, componentCenterY).position; // Misc.printArray("center_ray_x", center_ray_x); double[] center_ray_y = behavior.findRay(componentCenterX, componentCenterY + 1).position; // Misc.printArray("center_ray_y", center_ray_y); /* * TODO: test more to see if this makes a difference. The * rubber band box is actually at the Z=-1 position * double[] center_ray = getRayPositionAtZ(behavior.findRay(componentCenterX, * componentCenterY), -1); * Misc.printArray("center_ray @ -1", center_ray); * double[] center_ray_x = getRayPositionAtZ(behavior.findRay(componentCenterX + 1, * componentCenterY), -1); * Misc.printArray("center_ray_x @ -1", center_ray_x); * double[] center_ray_y = getRayPositionAtZ(behavior.findRay(componentCenterX, * componentCenterY + 1),-1); * Misc.printArray("center_ray_y @ -1", center_ray_y); */ double[] tstart = proj.getMatrix(); // printMatrix("tstart", tstart); double[] rot = new double[3]; double[] scale = new double[3]; double[] trans = new double[3]; behavior.instance_unmake_matrix(rot, scale, trans, tstart); double stx = scale[0]; double sty = scale[1]; // System.out.println("stx = " + stx); // System.out.println("sty = " + sty); double[] trot = behavior.make_matrix(rot[0], rot[1], rot[2], scale[0], scale[1], scale[2], // scale[0], 0.0, 0.0, 0.0); // printMatrix("trot", trot); // WLH 17 Aug 2000 double[] xmat = behavior.make_translate(center_ray_x[0] - center_ray[0], center_ray_x[1] - center_ray[1], center_ray_x[2] - center_ray[2]); // xmat = behavior.multiply_matrix(mult, xmat); double[] ymat = behavior.make_translate(center_ray_y[0] - center_ray[0], center_ray_y[1] - center_ray[1], center_ray_y[2] - center_ray[2]); // ymat = behavior.multiply_matrix(mult, ymat); double[] xmatmul = behavior.multiply_matrix(trot, xmat); double[] ymatmul = behavior.multiply_matrix(trot, ymat); /* * printMatrix("xmat", xmat); * printMatrix("ymat", ymat); * printMatrix("xmatmul", xmatmul); * printMatrix("ymatmul", ymatmul); */ behavior.instance_unmake_matrix(rot, scale, trans, xmatmul); double xmul = trans[0]; behavior.instance_unmake_matrix(rot, scale, trans, ymatmul); double ymul = trans[1]; // System.out.println("Multipliers = " + xmul + "," + ymul); // make sure that we don't divide by 0 (happens if display // component is not yet on screen if ((Math.abs(xmul) > 0) && (Math.abs(ymul) > 0)) { // Now we can get the box coordinates in component space float[] lows = xyRegion.getLow(); float[] highs = xyRegion.getHi(); float boxCenterDisplayX = (highs[0] + lows[0]) / 2.0f; float boxCenterDisplayY = (highs[1] + lows[1]) / 2.0f; /* * System.out.println( * "Box center point (XY) = " + * boxCenterDisplayX+","+boxCenterDisplayY); */ // Check to see if the box is big enough (at least 5x5 pixels) // *** might want to ammend this to be a percentage of // component size **** int boxWidth = (int) Math.abs((highs[0] - lows[0]) / xmul * stx); int boxHeight = (int) Math.abs((highs[1] - lows[1]) / ymul * sty); /* * System.out.println( * "Box size = " + boxWidth +"," + boxHeight); */ if ((boxWidth > 5) && (boxHeight > 5)) { int boxCenterX = componentCenterX + (int) ((boxCenterDisplayX - center_ray[0]) / xmul); int boxCenterY = componentCenterY - (int) ((boxCenterDisplayY - center_ray[1]) / ymul); /* * System.out.println( * "Box Center point = " + boxCenterX +","+boxCenterY); */ double transx = (componentCenterX - boxCenterX) * xmul * stx; double transy = (componentCenterY - boxCenterY) * ymul * sty; /* * System.out.println("transx = " + transx + * " transy = " + transy); */ // Now calculate zoom factor double zoom = (boxWidth / boxHeight >= d.width / d.height) ? d.getWidth() / boxWidth : d.getHeight() / boxHeight; // zoom out if this is a bigger region than the component // System.out.println("zoom factor = " + zoom); translate(transx, -transy); zoom(zoom); } } } /** * Scale vertical values using the range of the vertical * scalar map. * * @param altValues altitude map values * * @return z values (may transform in place); */ public float[] scaleVerticalValues(float[] altValues) { if (getAltitudeMap() == null) { return altValues; } return getAltitudeMap().scaleValues(altValues, false); } /** * Set up the DisplayTupleType. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ private void setDisplayTypes() throws VisADException, RemoteException { if (coordinateSystem == null) { System.out.println("coordSys == null"); displayLatitudeType = Display.YAxis; displayLongitudeType = Display.XAxis; displayAltitudeType = Display.ZAxis; displayTupleType = Display.DisplaySpatialCartesianTuple; } else { int myInstance; synchronized (INSTANCE_MUTEX) { myInstance = instance++; } // We need to set the range on the longitude axis // to be equal to the range of the projection if the // X coordinate is approximately equal to Longitude. // For now, this is only LatLonProjections and TrivalMP's double minLon = -360; double maxLon = 360.; double centerLon = 0; MapProjection mp = ((MapProjection3DAdapter) coordinateSystem) .getMapProjection(); boolean isLatLon = false; adjustLons = true; // HACK, HACK, HACK, HACK if (mp instanceof ProjectionCoordinateSystem) { ProjectionImpl proj = ((ProjectionCoordinateSystem) mp).getProjection(); if (proj instanceof LatLonProjection) { Rectangle2D r2d2 = mp.getDefaultMapArea(); minLon = r2d2.getX(); maxLon = minLon + r2d2.getWidth(); centerLon = minLon + r2d2.getWidth() / 2; isLatLon = true; } } else if (mp instanceof TrivialMapProjection) { Rectangle2D r2d2 = mp.getDefaultMapArea(); minLon = r2d2.getX(); maxLon = minLon + r2d2.getWidth(); centerLon = minLon + r2d2.getWidth() / 2; // isLatLon = true; // TODO: figure out this a little more. } else if (mp instanceof AREACoordinateSystem) { // minLon = -180; // maxLon = 180.; adjustLons = false; } // TODO: figure out what we should be doing here. use360 = !((minLon >= -185) && (maxLon <= 185)); if ((isLatLon && !use360) // lat/lon projections in +/-180 rang || !mp.isXYOrder() // Vis5D || ((minLon > -360) && (minLon < 0) && (maxLon > 180))) { // AVN grids adjustLons = false; } /* * System.out.println("DisplayProjectionLon" + myInstance * + " has range of " + minLon + " to " + maxLon * + " with center at " + centerLon * + "; use360 = " + use360 + "; adjust lons = " * + adjustLons); */ displayLatitudeType = new DisplayRealType("ProjectionLat" + myInstance, true, -90.0, 90.0, 0.0, CommonUnit.degree); displayLongitudeType = new DisplayRealType("ProjectionLon" + myInstance, true, minLon, maxLon, centerLon, CommonUnit.degree); double defaultZ = (getDisplayMode() != MODE_3D) ? 0.0 : -1.0; displayAltitudeType = new DisplayRealType("ProjectionAlt" + myInstance, true, -1.0, 1.0, defaultZ, null); displayTupleType = new DisplayTupleType(new DisplayRealType[] { displayLatitudeType, displayLongitudeType, displayAltitudeType }, coordinateSystem); } setSpatialScalarMaps(); } /** * Create the adapter coordinate system using a MapProjection * * @param mapProjection * * @return the adapted coordinate system * * @throws VisADException null mapProjection or other VisAD problem */ private CoordinateSystem makeCoordinateSystem(MapProjection mapProjection) throws VisADException { if (mapProjection == null) { throw new VisADException("MapProjection can't be null"); } CoordinateSystem cs = new MapProjection3DAdapter(mapProjection); csUnits = cs.getCoordinateSystemUnits(); return cs; } /** * Handles a change to the cursor position. * * @throws VisADException VisAD failure. * @throws RemoteException Java RMI failure. */ protected void cursorMoved() throws VisADException, RemoteException { double[] c = getDisplay().getDisplayRenderer().getCursor(); updateLocation( getEarthLocation(getDisplay().getDisplayRenderer().getCursor())); } /** * Update lat/lon/alt properties with the EarthLocation. * * @param el EarthLocation to use. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ protected void updateLocation(EarthLocation el) throws VisADException, RemoteException { super.updateLocation(el); cursorLLP.set(el.getLatitude().getValue(CommonUnit.degree), el.getLongitude().getValue(CommonUnit.degree)); Bearing.calculateBearing(centerLLP, cursorLLP, workBearing); setCursorRange(new Real(CURSOR_RANGE_TYPE, workBearing.getDistance())); setCursorBearing(new Real(CURSOR_BEARING_TYPE, workBearing.getAngle())); } /** * _more_ * * @return _more_ */ public LatLonPointImpl getCenterLLP() { return centerLLP; } /** * Handles a change in the position of the mouse-pointer. * * @param x x mouse position * @param y y mouse position * * @throws RemoteException Java RMI problem * @throws UnitException Unit conversion problem * @throws VisADException VisAD problem */ protected void pointerMoved(int x, int y) throws UnitException, VisADException, RemoteException { /* * Convert from (pixel, line) Java Component coordinates to (latitude, * longitude) */ /* * TODO: figure out why this won't work * updateLocation(getEarthLocation(getSpatialCoordinatesFromScreen(x, * y))); */ // TODO: java2d // if(true) return; VisADRay ray = getRay(x, y); /* EarthLocation el = getEarthLocation(ray.position[0], ray.position[1], ray.position[2]); updateLocation(el); } /** * Get the earth location from the VisAD xyz coodinates * * @param x x * @param y y * @param z z * @param setZToZeroIfOverhead If in the overhead view then set Z to 0 * * @return corresponding EarthLocation */ public EarthLocation getEarthLocation(double x, double y, double z, boolean setZToZeroIfOverhead) { EarthLocationTuple value = null; try { float[][] numbers = coordinateSystem.fromReference(new float[][] { new float[] { (float) (x) }, new float[] { (float) (y) }, new float[] { (float) (z) } }); Real lat = new Real(RealType.Latitude, getScaledValue(latitudeMap, numbers[0][0]), csUnits[0]); Real lon = new Real(RealType.Longitude, getScaledValue(longitudeMap, numbers[1][0]), csUnits[1]); Real alt = null; if (getDisplayMode() == MODE_3D) { if (setZToZeroIfOverhead && Arrays.equals(getProjectionMatrix(), getSavedProjectionMatrix()) /* * && (alt * .getValue( * getVerticalRangeUnit()) != altitudeMap * .getRange()[0]) */ ) { alt = new Real(RealType.Altitude, altitudeMap.getRange()[0], getVerticalRangeUnit()); } else { alt = new Real(RealType.Altitude, getScaledValue(altitudeMap, numbers[2][0])); } } else { alt = new Real(RealType.Altitude, 0); } value = new EarthLocationTuple(lat, lon, alt); } catch (VisADException e) { e.printStackTrace(); } // can't happen catch (RemoteException e) { e.printStackTrace(); } // can't happen return value; } /** * Returns the spatial (XYZ) coordinates of the particular EarthLocation * * @param el earth location to transform * * @return RealTuple of display coordinates. */ public RealTuple getSpatialCoordinates(EarthLocation el) { if (el == null) { throw new NullPointerException( "MapProjectionDisplay.getSpatialCoorindate(): " + "null input EarthLocation"); } RealTuple spatialLoc = null; try { double[] xyz = getSpatialCoordinates(el, null); spatialLoc = new RealTuple(RealTupleType.SpatialCartesian3DTuple, xyz); } catch (VisADException e) { e.printStackTrace(); } // can't happen catch (RemoteException e) { e.printStackTrace(); } // can't happen return spatialLoc; } /** * Returns the spatial (XYZ) coordinates of the particular EarthLocation * * @param el earth location to transform * @param xyz The in value to set. May be null. * @param altitude altitude value * * @return xyz array * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ public double[] getSpatialCoordinates(EarthLocation el, double[] xyz, double altitude) throws VisADException, RemoteException { float[] altValues; if ((altitudeMap != null) && (el.getAltitude() != null) && !(Double.isNaN(altitude))) { altValues = altitudeMap.scaleValues(new double[] { altitude }); } else { altValues = new float[] { 0f }; } float[][] temp = coordinateSystem.toReference(new float[][] { latitudeMap.scaleValues(new double[] { el.getLatitude().getValue(CommonUnit.degree) }), longitudeMap.scaleValues(new double[] { el.getLongitude().getValue(CommonUnit.degree) }), altValues }); if (xyz == null) { xyz = new double[3]; } xyz[0] = temp[0][0]; xyz[1] = temp[1][0]; xyz[2] = temp[2][0]; return xyz; } /** * Method called to reset all the map parameters after a change. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ private void resetMapParameters() throws VisADException, RemoteException { resetMapParameters(true); } /** * Method called to reset all the map parameters after a change. * * @param resetDisplayProjMatrix yes/no change the current VisAD Display projection matrix * when the map projection is changed. * * @throws RemoteException Java RMI problem * @throws VisADException VisAD problem */ private void resetMapParameters(boolean resetDisplayProjMatrix) throws VisADException, RemoteException { setDisplayInactive(); setDisplayTypes(); if (resetDisplayProjMatrix) { resetProjection(); // make it the right size setAspect(); } makeLatScales(); makeLonScales(); setDisplayActive(); } /** * Set the aspect for the display. */ private void setAspect() { Rectangle2D mapArea = mapProjection.getDefaultMapArea(); double ratio = mapArea.getWidth() / mapArea.getHeight(); double[] myaspect = getDisplayAspect(); try { if (ratio == 1.0) { // height == width setDisplayAspect((getDisplayMode() != MODE_2D) ? new double[] { 1.0, 1.0, myaspect[2] } : new double[] { 1.0, 1.0 }); /* * guess this doesn't matter, just use the other * } else if (ratio < 1) { // height > width * setDisplayAspect( * (getDisplayMode() != MODE_2D) * ? new double[] { 1.0, ratio, myaspect[2] } * : new double[] { 1.0, ratio }); */ } else { // width > height setDisplayAspect((getDisplayMode() != MODE_2D) ? new double[] { ratio, 1.0, myaspect[2] } : new double[] { ratio, 1.0 }); } // Misc.printArray("aspect", getDisplayAspect()); } catch (Exception excp) { System.out.println( "MapProjectionDisplay.setDisplayAspect() got exception: " + excp); } } /** * Make the default projection. * * @return Default projectcion * * @throws VisADException couldn't create MapProjection */ protected static MapProjection makeDefaultMapProjection() throws VisADException { return new ProjectionCoordinateSystem( new LatLonProjection("Default Projection", // Use this to make the aspect ratio correct new ProjectionRect(-180., -180., 180., 180.))); } /** * Get the display coordinate system that turns lat/lon/alt to * x/y/z * * @return the coordinate system (may be null) */ public CoordinateSystem getDisplayCoordinateSystem() { return coordinateSystem; } /** * test by running java ucar.unidata.view.geoloc.MapProjectionDisplay * * @param args include an argument for a 3D display * * @throws Exception problem creating the display */ public static void main(String[] args) throws Exception { JFrame frame = new JFrame(); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); final MapProjectionDisplay navDisplay = ((args.length > 0) && visad.util.Util.canDoJava3D()) ? MapProjectionDisplay.getInstance(NavigatedDisplay.MODE_3D) : (visad.util.Util.canDoJava3D() == true) ? MapProjectionDisplay.getInstance(NavigatedDisplay.MODE_2Din3D) : MapProjectionDisplay.getInstance(NavigatedDisplay.MODE_2D); /* * double[]aspect = { 1.0, 1.0, 0.4 }; * navDisplay.setDisplayAspect((navDisplay.getDisplayMode() == NavigatedDisplay.MODE_2D) * ? new double[]{ 1.0, 1.0 } * : aspect); */ DisplayImpl display = (DisplayImpl) navDisplay.getDisplay(); navDisplay.setBackground(Color.white); navDisplay.setForeground(Color.black); // navDisplay.setCursorStringOn(true); MapLines mapLines = new MapLines("maplines"); URL mapSource = // new URL("ftp://www.ssec.wisc.edu/pub/visad-2.0/OUTLSUPW"); navDisplay.getClass().getResource("/auxdata/maps/OUTLSUPW"); try { BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource); mapLines.setMapLines(mapAdapter.getData()); mapLines.setColor(java.awt.Color.black); navDisplay.addDisplayable(mapLines); } catch (Exception excp) { System.out.println("Can't open map file " + mapSource); System.out.println(excp); } JPanel panel = new JPanel(new GridLayout(1, 0)); JButton pushme = new JButton("Map Projection Manager"); panel.add(pushme); frame.getContentPane().add(panel, BorderLayout.NORTH); ViewpointControl vpc = new ViewpointControl(navDisplay); panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.add(navDisplay.getComponent(), BorderLayout.CENTER); panel.add((navDisplay.getDisplayMode() == navDisplay.MODE_3D) ? (Component) ucar.unidata.util.GuiUtils.topCenterBottom( vpc.getToolBar(JToolBar.VERTICAL), new NavigatedDisplayToolBar( navDisplay, JToolBar.VERTICAL), GuiUtils.filler()) : (Component) new NavigatedDisplayToolBar(navDisplay, JToolBar.VERTICAL), BorderLayout.WEST); JPanel readout = new JPanel(); readout.add(new NavigatedDisplayCursorReadout(navDisplay)); readout.add(new RangeAndBearingReadout(navDisplay)); panel.add(readout, BorderLayout.SOUTH); navDisplay.draw(); frame.getContentPane().add(panel, BorderLayout.CENTER); if (navDisplay.getDisplayMode() == navDisplay.MODE_3D) { JMenuBar mb = new JMenuBar(); mb.add(vpc.getMenu()); frame.setJMenuBar(mb); } System.out.println("Using rectilinear projection"); final ProjectionManager pm = new ProjectionManager(); pm.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { if (e.getPropertyName().equals("ProjectionImpl")) { try { navDisplay.setMapProjection( (ProjectionImpl) e.getNewValue()); } catch (Exception exp) { System.out.println(exp); } } } }); pushme.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { pm.show(); } }); /* * EarthLocationTuple elt = new EarthLocationTuple(40,-105, 8000); * SelectorPoint sp = new SelectorPoint("foo", elt); * sp.setFixed(false,false,true); * sp.setColor(Color.black); * navDisplay.addDisplayable(sp); */ navDisplay.getDisplay().getGraphicsModeControl().setScaleEnable(true); frame.pack(); frame.setVisible(true); /* * if (navDisplay.getDisplayMode() != navDisplay.MODE_2D) { * final CurveDrawer cd = * //new CurveDrawer(RealType.XAxis, RealType.YAxis, * new CurveDrawer(RealType.Latitude, RealType.Longitude, * InputEvent.SHIFT_MASK); * cd.addAction(new ActionImpl("Curve Drawer") { * int numSets = 0; * public void doAction () * throws VisADException, RemoteException { * UnionSet curves = (UnionSet) cd.getData(); * if (curves != null) { * int num = curves.getSets().length; * if (num != numSets) { * numSets = num; * System.out.println("Data has type " + * curves.getType() + * " length = " + numSets); * } * } * } * }); * cd.setColor(Color.red); * cd.setLineWidth(2.0f); * navDisplay.addDisplayable(cd); * } */ /* * System.out.println("Setting projection GOES-E satellite"); * Thread.sleep(5000); * AreaAdapter aa = * new AreaAdapter( * * "adde://adde.ucar.edu/imagedata?group=rtimages&descr=edfloater-i&compress=true"); * navDisplay.setMapProjection((MapProjection) aa.getCoordinateSystem()); * EarthLocation el = * navDisplay.getEarthLocation(new double[] {0.0, 0.0, 1.0}); * System.out.println("location = " + el); * System.out.println(navDisplay.getSpatialCoordinates(el)); */ } /** * An adapter for visad.georef.MapProjection coordinate systems (ie: * ones with * a reference of Lat/Lon). Allows for the conversion from * lat/lon to Display.DisplaySpatialCartesianTuple (XYZ). * Altitude (z) values are held constant. */ protected class MapProjection3DAdapter extends CoordinateSystem implements InverseLinearScaledCS { /** index of the latitude coordinate */ private final int latIndex; /** index of the longitude coordinate */ private final int lonIndex; /** map projection for xy -> lat/lon transformations */ private final MapProjection mapProjection; /** X offset */ private final double offsetX; /** Y offset */ private final double offsetY; /** X scaling factor */ private final double scaleX; /** Y scaling factor */ private final double scaleY; /** the coordinate system */ private CoordinateSystem theCoordinateSystem; /** index of the x coordinate */ private final int xIndex; /** index of the y coordinate */ private final int yIndex; /** * Construct a new CoordinateSystem which uses a MapProjection for * the transformations between x,y and lat/lon. * * @param mapProjection CoordinateSystem that transforms from xy * in the data space to lat/lon. * @exception VisADException can't create the necessary VisAD object */ public MapProjection3DAdapter(MapProjection mapProjection) throws VisADException { super(Display.DisplaySpatialCartesianTuple, new Unit[] { CommonUnit.degree, CommonUnit.degree, null }); this.mapProjection = mapProjection; this.theCoordinateSystem = new CachingCoordinateSystem(this.mapProjection); latIndex = mapProjection.getLatitudeIndex(); lonIndex = mapProjection.getLongitudeIndex(); if (mapProjection.isXYOrder()) { xIndex = 0; yIndex = 1; } else { xIndex = 1; yIndex = 0; } /* * System.out.println("latIndex = " + latIndex + * " lonIndex = " + lonIndex + * " xIndex = " + xIndex + * " yIndex = " + yIndex); */ java.awt.geom.Rectangle2D bounds = mapProjection.getDefaultMapArea(); /* * System.out.println("X = " + bounds.getX() + * " Y = "+ bounds.getY() + * " width = "+ bounds.getWidth() + * " height = "+ bounds.getHeight()); */ scaleX = bounds.getWidth() / 2.0; scaleY = bounds.getHeight() / 2.0; offsetX = bounds.getX() + scaleX; offsetY = bounds.getY() + scaleY; * System.out.println("scaleX = " + scaleX + * " scaleY = "+ scaleY + * " offsetX = "+ offsetX + * " offsetY = "+ offsetY); */ } /** * Transform latitude/longitude/altitude value to XYZ * * @param latlonalt array of latitude, longitude, altitude values * * @return array of display xyz values. * * @throws VisADException can't create the necessary VisAD object */ public double[][] toReference(double[][] latlonalt) throws VisADException { if ((latlonalt == null) || (latlonalt[0].length < 1)) { return latlonalt; } int numpoints = latlonalt[0].length; call1("toReference(d)", numpoints); double[][] t2 = new double[2][]; t2[latIndex] = latlonalt[0]; t2[lonIndex] = latlonalt[1]; /* */ if (adjustLons) { t2[lonIndex] = (use360) ? GeoUtils.normalizeLongitude360(latlonalt[1]) // ? latlonalt[1] : GeoUtils.normalizeLongitude(latlonalt[1]); } t2 = theCoordinateSystem.fromReference(t2); if (t2 == null) { throw new VisADException( "MapProjection.toReference: " + "Can't do (lat,lon) to (x,y) transformation"); } double x, y; double[] t2x = t2[xIndex]; double[] t2y = t2[yIndex]; for (int i = 0; i < numpoints; i++) { if (Double.isNaN(t2x[i]) || Double.isNaN(t2y[i])) { x = Double.NaN; y = Double.NaN; } else { x = (t2x[i] - offsetX) / scaleX; y = (t2y[i] - offsetY) / scaleY; } latlonalt[0][i] = x; latlonalt[1][i] = y; } call2("toReference(d)", numpoints); return latlonalt; } /** * debug * * @param msg debug * @param numpoints debug */ void call1(String msg, int numpoints) { if (numpoints > 10000) { // Misc.printStack(msg,5,null); Trace.call1("MapProjectionDisplay." + msg, " numpoints = " + numpoints); } } /** * debug * * @param msg debug * @param numpoints debug */ void call2(String msg, int numpoints) { if (numpoints > 10000) { Trace.call2("MapProjectionDisplay." + msg); } } /** * Transform latitude/longitude/altitude value to XYZ * * @param latlonalt array of latitude, longitude, altitude values * * @return array of display xyz values. * @throws VisADException can't create the necessary VisAD object */ public float[][] toReference(float[][] latlonalt) throws VisADException { if ((latlonalt == null) || (latlonalt[0].length < 1)) { return latlonalt; } int numpoints = latlonalt[0].length; call1("toReference(f)", numpoints); float[][] t2 = new float[2][]; t2[latIndex] = latlonalt[0]; t2[lonIndex] = latlonalt[1]; /* */ if (adjustLons) { t2[lonIndex] = (use360) ? GeoUtils.normalizeLongitude360(latlonalt[1]) // ? latlonalt[1] : GeoUtils.normalizeLongitude(latlonalt[1]); } // call1("mapProjection.fromReference", numpoints); // Trace.msg("MapProjectionDisplay. class=" + mapProjection.getClass().getName()); t2 = theCoordinateSystem.fromReference(t2); // call2("mapProjection.fromReference", numpoints); if (t2 == null) { throw new VisADException( "MapProjection.toReference: " + "Can't do (lat,lon) to (x,y) transformation"); } float x, y; float[] t2ax = t2[xIndex]; float[] t2ay = t2[yIndex]; for (int i = 0; i < numpoints; i++) { float t2x = t2ax[i]; float t2y = t2ay[i]; if ((t2x != t2x) || (t2y != t2y)) { x = Float.NaN; y = Float.NaN; } else { x = (float) ((t2x - offsetX) / scaleX); y = (float) ((t2y - offsetY) / scaleY); } latlonalt[0][i] = x; latlonalt[1][i] = y; } call2("toReference(f)", numpoints); return latlonalt; } /** * Transform display XYZ values to latitude/longitude/altitude * * @param xyz array of Display.DisplaySpatialCartesianTuple XYZ values * @return array of display lat/lon/alt values. * * @throws VisADException can't create the necessary VisAD object */ public double[][] fromReference(double[][] xyz) throws VisADException { if ((xyz == null) || (xyz[0].length < 1)) { return xyz; } int numpoints = xyz[0].length; call1("fromReference(d)", numpoints); for (int i = 0; i < numpoints; i++) { if (Double.isNaN(xyz[0][i]) || Double.isNaN(xyz[0][i])) { continue; } xyz[0][i] = (xyz[0][i] * scaleX + offsetX); xyz[1][i] = (xyz[1][i] * scaleY + offsetY); } double[][] t2 = new double[][] { xyz[xIndex], xyz[yIndex] }; t2 = theCoordinateSystem.toReference(t2); if (t2 == null) { throw new VisADException( "MapProjection.toReference: " + "Can't do (x,y) to (lat,lon) transformation"); } xyz[0] = t2[latIndex]; xyz[1] = t2[lonIndex]; /* */ if (adjustLons) { xyz[1] = (use360) ? GeoUtils.normalizeLongitude360(t2[lonIndex]) // ? t2[lonIndex] : GeoUtils.normalizeLongitude(t2[lonIndex]); } call2("fromReference(d)", numpoints); return xyz; } /** * Transform display XYZ values to latitude/longitude/altitude * * @param xyz array of Display.DisplaySpatialCartesianTuple XYZ values * @return array of display lat/lon/alt values. * * @throws VisADException can't create the necessary VisAD object */ public float[][] fromReference(float[][] xyz) throws VisADException { if ((xyz == null) || (xyz[0].length < 1)) { return xyz; } int numpoints = xyz[0].length; call1("fromReference(f)", numpoints); for (int i = 0; i < numpoints; i++) { if (Float.isNaN(xyz[0][i]) || Float.isNaN(xyz[0][i])) { continue; } xyz[0][i] = (float) (xyz[0][i] * scaleX + offsetX); xyz[1][i] = (float) (xyz[1][i] * scaleY + offsetY); } float[][] t2 = new float[][] { xyz[xIndex], xyz[yIndex] }; t2 = theCoordinateSystem.toReference(t2); if (t2 == null) { throw new VisADException( "MapProjection.toReference: " + "Can't do (x,y) to (lat,lon) transformation"); } xyz[0] = t2[latIndex]; xyz[1] = t2[lonIndex]; /* */ if (adjustLons) { xyz[1] = (use360) ? GeoUtils.normalizeLongitude360(t2[lonIndex]) // ? t2[lonIndex] : GeoUtils.normalizeLongitude(t2[lonIndex]); } call2("fromReference(f)", numpoints); return xyz; } /** * See if this is equal to the object in question. * * @param obj object in question. * * @return true if they are equal. The two objects are equal if * their MapProjections are equal. */ public boolean equals(Object obj) { if ( !(obj instanceof MapProjection3DAdapter)) { return false; } MapProjection3DAdapter that = (MapProjection3DAdapter) obj; return (that.mapProjection).equals(mapProjection); } /** * Return the MapProjection being used by the CoordinateSystem. * * @return the MapProjection used in this instance */ public MapProjection getMapProjection() { return mapProjection; } /** * Get the scale * * @return the scale (x,y) */ public double[] getScale() { return new double[] { scaleX, scaleY }; } /** * Get the offset * * @return the offset (x_off, y_off) */ public double[] getOffset() { return new double[] { offsetX, offsetY }; } /** * Get the inverted coordinate system * * @return the inverted coordinate system */ public CoordinateSystem getInvertedCoordinateSystem() { return this.mapProjection; } } }
File
MapProjectionDisplay.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration