[Thread Prev][Thread Next][Index]

Re: [las_users] Lat/Lon problems



Hi,

I had problems with time axes with units of minutes some time ago, so I
expanded the LASConfig.java file for our internal LAS server. Now we can
use the unit "min" for axes like:

   <time_axis type="t" units="min">
      <arange start="01-AUG-2006 11:12" step="12" size="1905"
end="17-AUG-2006 08:12"/>
   </time_axis>

If you don't fear editing the Java source code you can use it, but have
to change your LASUI.js (in /WebContent/JavaScript/ui/) in line 1524 from:

this.refs.DW = new
DateWidget(this.state.grid.getLo('t'),this.state.grid.getHi('t'));

to:

var scale = 1;
var tUnits = this.state.grid.getUnits('t');
var tDelta = this.state.grid.getDelta('t');
if (tUnits == "hour") {
  scale = 60;
  if ((24 % tDelta) != 0) {
    tDelta = 1;
  }
}
else {
  if  (tDelta == null || tUnits == "day") {
    tDelta = 1440;
  }
  if (tUnits == "min") {
    if ((1440 % tDelta) != 0) {
      tDelta = 1;
    }
  }
}
this.refs.DW = new
DateWidget(this.state.grid.getLo('t'),this.state.grid.getHi('t'),(tDelta
* scale));

and replace /gov/noaa/pmel/tmap/las/jdom/LASConfig.java by the attached
LASConfig.java.
You should backup all files first!

I hope it works,
Bastian

On 02/24/2010 05:32 PM, Roland Schweitzer wrote:
> Anton,
> 
> Anton Yoel Buenavista wrote:
>> Hello again everyone,
>>
>> For an update on my problem, I was able to display  the variables
>> which only depend on lat and lon using the documentation here:
>> http://ferret.pmel.noaa.gov/LAS/documentation/installer-documentation/adding-curvilinear-data/.
>> Thank you Roland for your guidance on that.
>>
>> However, I am faced with a new problem. The remaining variables depend
>> on lat, lon, and a time dimension as well as you can see in the sample
>> variable definition below:
> [...]
>>           <time-t-id-01ced4ab23 type="t" units="minute">
>>             <arange start="2008-01-23 00:00:00" size="84" step="15" />
>>           </time-t-id-01ced4ab23>
>>         </axes>
>>
>> I have attached a screenshot of the error being returned when the plot
>> is trying to be loaded. Your kind help on the matter is greatly
>> appreciated!
> Unfortunately the LAS user interface time widget does not understand
> time increments of less than one hour.  We'll be working on improving
> that in future versions, but for now you'll need to create your time
> axis by listing each individual time value.  I know it's not practical
> for all cases, but it's the only solution for the immediate future.  For
> example:
> 
>    <time-t-id-01ced4ab23 type="t" units="minute">
>         <v>2008-01-23 00:00:00</v>
>         <v>2008-01-23 00:15:00</v>
>         <v>2008-01-23 00:30:00</v>
>         <v>2008-01-23 00:45:00</v>
>         <v>2008-01-23 01:00:00</v>
> 
> 
>         [...]
> 
>    </time-t-id-01ced4ab23>
> 
> Roland
> 
/**
 * This software is provided by NOAA for full, free and open release.  It is
 * understood by the recipient/user that NOAA assumes no liability for any
 * errors contained in the code.  Although this software is released without
 * conditions or restrictions in its use, it is expected that appropriate
 * credit be given to its author and to the National Oceanic and Atmospheric
 * Administration should the software be included by the recipient as an
 * element in other product development. 
 */
package gov.noaa.pmel.tmap.las.jdom;

import gov.noaa.pmel.tmap.las.client.CategorySerializable;
import gov.noaa.pmel.tmap.las.client.DatasetSerializable;
import gov.noaa.pmel.tmap.las.client.RPCException;
import gov.noaa.pmel.tmap.las.client.VariableSerializable;
import gov.noaa.pmel.tmap.las.exception.LASException;
import gov.noaa.pmel.tmap.las.filter.AttributeFilter;
import gov.noaa.pmel.tmap.las.filter.CategoryFilter;
import gov.noaa.pmel.tmap.las.ui.state.StateNameValueList;
import gov.noaa.pmel.tmap.las.ui.state.TimeSelector;
import gov.noaa.pmel.tmap.las.util.Category;
import gov.noaa.pmel.tmap.las.util.DataConstraint;
import gov.noaa.pmel.tmap.las.util.Dataset;
import gov.noaa.pmel.tmap.las.util.Grid;
import gov.noaa.pmel.tmap.las.util.Institution;
import gov.noaa.pmel.tmap.las.util.NameValuePair;
import gov.noaa.pmel.tmap.las.util.Operation;
import gov.noaa.pmel.tmap.las.util.Option;
import gov.noaa.pmel.tmap.las.util.Region;
import gov.noaa.pmel.tmap.las.util.TimeAxis;
import gov.noaa.pmel.tmap.las.util.Variable;
import gov.noaa.pmel.tmap.las.util.View;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jdom.Attribute;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.xpath.XPath;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Period;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

/**
 * This class is the JDOM instantiation of the "las.xml" file and any entities it references (data stubs and operationsV7.xml).
 * @author Roland Schweitzer
 *
 */
public class LASConfig extends LASDocument {
    private static Logger log = LogManager.getLogger(LASConfig.class.getName());
    private static String time_formats[] = {
            "yyyy-MM-dd HH:mm:ss",
            "yyyy-MM-dd HH:mm",
            "yyyy-MM-dd HH",
            "yyyy-MM-dd",
            "yyy-MM-dd HH:mm:ss",
            "yyy-MM-dd HH:mm",
            "yyy-MM-dd HH",
            "yyy-MM-dd",
            "yy-MM-dd HH:mm:ss",
            "yy-MM-dd HH:mm",
            "yy-MM-dd HH",
            "yy-MM-dd",
            "y-MM-dd HH:mm:ss",
            "y-MM-dd HH:mm",
            "y-MM-dd HH",
            "y-MM-dd",
            "yyyy-M-dd HH:mm:ss",
            "yyyy-M-dd HH:mm",
            "yyyy-M-dd HH",
            "yyyy-M-dd",
            "yyy-M-dd HH:mm:ss",
            "yyy-M-dd HH:mm",
            "yyy-M-dd HH",
            "yyy-M-dd",
            "yy-M-dd HH:mm:ss",
            "yy-M-dd HH:mm",
            "yy-M-dd HH",
            "yy-M-dd",
            "y-M-dd HH:mm:ss",
            "y-M-dd HH:mm",
            "y-M-dd HH",
            "y-M-dd",
            "yyyy-MM-d HH:mm:ss",
            "yyyy-MM-d HH:mm",
            "yyyy-MM-d HH",
            "yyyy-MM-d",
            "yyy-MM-d HH:mm:ss",
            "yyy-MM-d HH:mm",
            "yyy-MM-d HH",
            "yyy-MM-d",
            "yy-MM-d HH:mm:ss",
            "yy-MM-d HH:mm",
            "yy-MM-d HH",
            "yy-MM-d",
            "y-MM-d HH:mm:ss",
            "y-MM-d HH:mm",
            "y-MM-d HH",
            "y-MM-d",
            "yyyy-M-d HH:mm:ss",
            "yyyy-M-d HH:mm",
            "yyyy-M-d HH",
            "yyyy-M-d",
            "yyy-M-d HH:mm:ss",
            "yyy-M-d HH:mm",
            "yyy-M-d HH",
            "yyy-M-d",
            "yy-M-d HH:mm:ss",
            "yy-M-d HH:mm",
            "yy-M-d HH",
            "yy-M-d",
            "y-M-d HH:mm:ss",
            "y-M-d HH:mm",
            "y-M-d HH",
            "y-M-d",
            "dd-MMM-yyyy HH:mm:ss",
            "dd-MMM-yyyy HH:mm",
            "dd-MMM-yyyy HH",
            "dd-MMM-yyyy",
            "d-MMM-yyyy HH:mm:ss",
            "d-MMM-yyyy HH:mm",
            "d-MMM-yyyy HH",
            "d-MMM-yyyy",
            "dd-MMM-yyy HH:mm:ss",
            "dd-MMM-yyy HH:mm",
            "dd-MMM-yyy HH",
            "dd-MMM-yyy",
            "d-MMM-yyy HH:mm:ss",
            "d-MMM-yyy HH:mm",
            "d-MMM-yyy HH",
            "d-MMM-yyy",
            "dd-MMM-yy HH:mm:ss",
            "dd-MMM-yy HH:mm",
            "dd-MMM-yy HH",
            "dd-MMM-yy",
            "d-MMM-yy HH:mm:ss",
            "d-MMM-yy HH:mm",
            "d-MMM-yy HH",
            "d-MMM-yy",
            "dd-MMM-y HH:mm:ss",
            "dd-MMM-y HH:mm",
            "dd-MMM-y HH",
            "dd-MMM-y",
            "d-MMM-y HH:mm:ss",
            "d-MMM-y HH:mm",
            "d-MMM-y HH",
            "d-MMM-y"
    };
    /**.
     * Get all the possible combinations of the characters in a string.  Combo routines based on code by Robert Sedgewick and Kevin Wayne.
     * from their book Introduction to Programming in Java published by Adison Wesley.
     * @param s
     * @return
     */
    private static ArrayList<String> combo(String s) {
        return combo("", s);
    }
    /**
     * Get combinations of the characters in a string.
     * @param prefix A prefix for the combinations
     * @param s the string to scramble
     * @return the combinations
     */
    private static ArrayList<String> combo(String prefix, String s) {
        ArrayList<String> comboList = new ArrayList<String>(); 
        
        if (!prefix.equals("")) {
            comboList.add(prefix);
        }
        
        if ( s.equals("") ) {  
            return comboList;
        } 
        for ( int i = 0; i < s.length(); i++ ) {
            comboList.addAll(combo(prefix + s.charAt(i), s.substring(i+1)));
        }
        return comboList;
    }
    /**
     * Take F-TDS server URL and data directory and build the F-TDS URLs for each variable. 
     * @param fds_base the base URL of the F-TDS server http://server:port/thredds/dodsC
     * @param fds_dir the directory into which the F-TDS journal files will be written
     * @throws LASException
     * @throws JDOMExcption
     * @throws IOFoundException 
     */
    public void addFDS(String fds_base, String fds_dir) throws LASException, JDOMException, IOException {
        File datadir = new File(fds_dir);
        if ( !datadir.exists() ) {
            boolean success = datadir.mkdirs();
            if ( !success ) {
                log.warn("No T-FDS directory.");
            }
        }
        datadir = new File(fds_dir+"dynamic");
        if ( !datadir.exists() ) {
            boolean success = datadir.mkdirs();
            if ( !success ) {
                log.warn("No T-FDS directory for user defined data.");
            }
        }
        List datasetsElements = getRootElement().getChildren("datasets");
        for (Iterator datasetsElementIt = datasetsElements.iterator(); datasetsElementIt.hasNext();) {
            Element datasetsE = (Element) datasetsElementIt.next();
            List datasets = datasetsE.getChildren("dataset");
            
            for (Iterator datasetIt = datasets.iterator(); datasetIt.hasNext();) {
                HashMap<String, String> jnls = new HashMap<String, String>();
                Element dataset = (Element) datasetIt.next();
                String dsID = dataset.getAttributeValue("ID");
                List variablesElements = dataset.getChildren("variables");
                for (Iterator variablesEelementsIt = variablesElements.iterator(); variablesEelementsIt.hasNext();) {
                    Element variablesE = (Element) variablesEelementsIt.next();
                    List variables = variablesE.getChildren("variable");
                    for (Iterator varsIt = variables.iterator(); varsIt.hasNext();) {
                        Element variable = (Element) varsIt.next();
                        String varID = variable.getAttributeValue("ID");
                        String var = getVariableName(dsID,varID);
                        String grid_type = variable.getAttributeValue("grid_type");
                        if ( grid_type.equals("regular") ) {
                            datadir = new File(fds_dir+dsID);
                            if ( !datadir.exists() ) {
                                boolean success = datadir.mkdirs();
                                if ( !success ) {
                                    log.warn("No T-FDS directory for "+dsID);
                                }
                            }
                            String url = getDataObjectURL(variable);
                            String init = getVariablePropertyValue(variable,"ferret","init_script");
                            String key;
                            if ( init != null && !init.equals("") ) {
                                key = url+"_"+init+"_"+var;
                            } else {
                                key = url;
                            }
                            if ( key.startsWith("http://";) ) {
                                key = key.substring(6,key.length());                               
                            } 
                            key = key.replaceAll("/","_");
                            key = key.replaceAll(":","_");
                            
                            if ( key.startsWith("_") ) {
                            	key = key.substring(1,key.length());
                            }
                            
                            if ( !jnls.containsKey(key) ) {
                                StringBuffer jnl = new StringBuffer(); 
                                if ( init != null && !init.equals("") ) {  
                                	jnl.append("DEFINE SYMBOL data_url \\\""+url+"\\\"\n");
                                	jnl.append("DEFINE SYMBOL data_var "+var+"\n");
                                    jnl.append("GO "+init+"\n");
                                    jnls.put(key, jnl.toString());
                                } else {
                                    jnl.append("USE \""+url+"\"\n");
                                    jnls.put(key, jnl.toString());
                                }
                            }
                            variable.setAttribute("ftds_url", fds_base+dsID+"/data_"+key+".jnl");
                        }
                    }
                }

                if ( jnls.size() > 0 ) {
                    int index=0;
                    for (Iterator jnlsIt = jnls.keySet().iterator(); jnlsIt.hasNext();) {
                        String key = (String) jnlsIt.next();
                        File varjnl = new File(fds_dir+dsID+File.separator+"data_"+key+".jnl");
                        PrintWriter data_script = new PrintWriter(new FileWriter(varjnl));
                        data_script.println(jnls.get(key));
                        data_script.close();
                        index++;
                    }
                }
            }
        }
    }
    /**
     * Descends the dataset and variable tree and set the grid_type attribute
     * if it is not already set.
     * @throws LASException 
     * @throws JDOMException 
     */
    public void addGridType() throws LASException, JDOMException {
        Element root = getRootElement();
        String version = root.getAttributeValue("version");
        if ( version != null && !version.contains("7.")) {
            throw new LASException("XML is not version 7.0 or above.  Try convertToSeven() first.");
        }
        List datasetsElements = root.getChildren("datasets");
        for (Iterator dseIt = datasetsElements.iterator(); dseIt.hasNext();) {
            Element datasetsE = (Element) dseIt.next();
            List datasets = datasetsE.getChildren("dataset");
            for (Iterator dsIt = datasets.iterator(); dsIt.hasNext();) {
                Element dataset = (Element) dsIt.next();
                // In theory there's only 1, but loop to be sure.
                List variablesElements = dataset.getChildren("variables");
                for (Iterator varseIt = variablesElements.iterator(); varseIt
                        .hasNext();) {
                    Element variablesE = (Element) varseIt.next();
                    List variables = variablesE.getChildren("variable");
                    for (Iterator varIt = variables.iterator(); varIt.hasNext();) {
                        Element variable = (Element) varIt.next();
                        setGridType(variable);
                    }
                }
            }
        }
    }
    /**
     * Adds attributes to all variables that indicate whether or not the variable
     * has a range or a point in the definition of each axis.  Must be a version
     * 7.0 style document before this method is invoked.
     * @throws LASException 
     * @throws JDOMException 
     */
    public void addIntervalsAndPoints() throws LASException, JDOMException {
        Element root = getRootElement();
        String version = root.getAttributeValue("version");
        if ( version != null && !version.contains("7.")) {
            throw new LASException("XML is not version 7.0 or above.  Try convertToSeven() first.");
        }
        List datasetsElements = root.getChildren("datasets");
        for (Iterator dseIt = datasetsElements.iterator(); dseIt.hasNext();) {
            Element datasetsE = (Element) dseIt.next();
            List datasets = datasetsE.getChildren("dataset");
            for (Iterator dsIt = datasets.iterator(); dsIt.hasNext();) {
                Element dataset = (Element) dsIt.next();
                // In theory there's only 1, but loop to be sure.
                List variablesElements = dataset.getChildren("variables");
                for (Iterator varseIt = variablesElements.iterator(); varseIt
                        .hasNext();) {
                    Element variablesE = (Element) varseIt.next();
                    List variables = variablesE.getChildren("variable");
                    for (Iterator varIt = variables.iterator(); varIt.hasNext();) {
                        Element variable = (Element) varIt.next();
                        // Get the reference to the grid
                        Element grid = variable.getChild("grid");
                        String gridID = grid.getAttributeValue("IDREF");
                        // Replace it with the actual grid element.
                        grid = getElementByXPath("/lasdata/grids/grid[@ID='"+gridID+"']");
                        List axes = grid.getChildren("axis");
                        String[] intervals = {"","","",""};
                        String[] points = {"","","",""};
                        for (Iterator axIt = axes.iterator(); axIt.hasNext();) {
                            // This is a reference to an axis
                            Element axis = (Element) axIt.next();
                            String axisID = axis.getAttributeValue("IDREF");
                            // Replace it with the actual axis
                            axis = getElementByXPath("/lasdata/axes/axis[@ID='"+axisID+"']");
                            String type = axis.getAttributeValue("type");
                            Element arange = axis.getChild("arange");
                            int size;
                            if ( arange != null ) {
                                size = Integer.valueOf(arange.getAttributeValue("size")).intValue();
                            } else {
                                List v = axis.getChildren("v");
                                size = v.size();
                            }
                            // The axis defintions can come in any order but
                            // we want this string orders XYZT
                            if ( size == 1 ) {
                                if ( type.equals("x") ) {
                                    points[0] = type;
                                } else if ( type.equals("y") ) {
                                    points[1] = type;
                                } else if ( type.equals("z") ) {
                                    points[2] = type;
                                } else if ( type.equals("t") ) {
                                    points[3] = type;
                                }
                            } else if ( size > 1 ) {
                                if ( type.equals("x") ) {
                                    points[0] = type;
                                    intervals[0] = type;
                                } else if ( type.equals("y") ) {
                                    points[1] = type;
                                    intervals[1] = type;
                                } else if ( type.equals("z") ) {
                                    points[2] = type;
                                    intervals[2] = type;
                                } else if ( type.equals("t") ) {
                                    points[3] = type;
                                    intervals[3] = type;
                                }
                            }
                        }
                        String existingPoints = variable.getAttributeValue("points");
                        String existingIntervals = variable.getAttributeValue("intervals");
                        // Set them only if they don't already exist in the variable definition.
                        if ( existingPoints == null ) {
                            variable.setAttribute("points", points[0]+points[1]+points[2]+points[3]);
                        }
                        if ( existingIntervals == null ) {
                            variable.setAttribute("intervals", intervals[0]+intervals[1]+intervals[2]+intervals[3]);
                        }
                    }
                }
            }
        }
    }
    /**
     * Create all the extra fancy attributes for a time axis so the DateWidgets can be initialized.
     * @param axis the axis description from this config
     * @throws LASException
     */
    private void addTimeAxisAttributes(Element axis) throws LASException {
        Element arange = axis.getChild("arange");
        if (arange == null) {
                            
            List v = axis.getChildren("v");
            for (Iterator vIt = v.iterator(); vIt.hasNext();) {
                Element vE = (Element) vIt.next();
                String label = vE.getAttributeValue("label");
                if ( label == null ) {
                    String content = vE.getTextTrim();
                    vE.setAttribute("label", content);
                }
                
            }
            Element v0 = (Element) v.get(0);
            String tlo = v0.getTextTrim();
            Element vN = (Element) v.get(v.size()-1);
            String thi = vN.getTextTrim();
            
            axis.setAttribute("lo", tlo);
            axis.setAttribute("hi", thi);
            axis.setAttribute("display_type", "menu");
            
        } else {
            
            axis.setAttribute("display_type", "widget");
            String tlo = arange.getAttributeValue("start");                   
            String units = axis.getAttributeValue("units");
/** by Baschti:
	new attribute end for enddate. so size or end can be used in xml file
*****************************************************************************/
            String thi = arange.getAttributeValue("end");
            double size = 0;
            try {
               size = Double.valueOf(arange.getAttributeValue("size")).doubleValue();
            } catch (Exception e) {}
/** Baschti End **/
            double step = Double.valueOf(arange.getAttributeValue("step")).doubleValue();
            DateTimeFormatter fmt = null;
            DateTime lodt = new DateTime("9000-01-01");
            boolean found = false;
            for (int i = 0; i < time_formats.length; i++) {
                fmt = DateTimeFormat.forPattern(time_formats[i]).withZone(DateTimeZone.UTC);
                try {
                   lodt = fmt.parseDateTime(tlo);
                   found = true;
                } catch ( IllegalArgumentException e ) {
                    found = false;
                }
                if (found) break;
            }
            
            if ( !found ) {
                throw new LASException("Time format for "+tlo+" could not be parsed.");
            }
/** by Baschti:
	do the same test as for lodt for hidt
*****************************************************************************/
            DateTime hidt = new DateTime();
            if ( thi != null) {
               fmt = null;
               hidt = new DateTime("9000-01-01");
               found = false;
               for (int i = 0; i < time_formats.length; i++) {
                   fmt = DateTimeFormat.forPattern(time_formats[i]).withZone(DateTimeZone.UTC);
                   try {
                      hidt = fmt.parseDateTime(thi);
                      found = true;
                   } catch ( IllegalArgumentException e ) {
                       found = false;
                   }
                   if (found) break;
               }
            
               if ( !found ) {
                   throw new LASException("Time format for "+thi+" could not be parsed.");
               }
            }
/** Baschti End **/
            
            DateTimeFormatter longfmt = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").withZone(DateTimeZone.UTC);
            
            axis.setAttribute("lo", lodt.toString(longfmt));
/** by Baschti
	added min for minutes
*****************************************************************************/
            if ( units.contains("min") ) {
                axis.setAttribute("hourNeeded", "true");
                axis.setAttribute("dayNeeded", "true");
                axis.setAttribute("monthNeeded", "true");
                axis.setAttribute("yearNeeded", "true");
                int minutes = 0;
                if (thi == null) {
                   minutes = (int) Math.round((size-1)*step);
                   hidt = lodt.plus(Period.minutes(minutes));
                }
                else {
                   Period delta = new Period(lodt,hidt);
                   minutes = (int) (12 * 8017 * 3);
                }
                int minuteInterval = (int) Math.round(minutes);
                axis.setAttribute("minuteInterval", String.valueOf(minuteInterval) );                       
                axis.setAttribute("hi", hidt.toString(longfmt));
/** Baschti End **/
            } else if ( units.contains("hour") ) {
                axis.setAttribute("hourNeeded", "true");
                axis.setAttribute("dayNeeded", "true");
                axis.setAttribute("monthNeeded", "true");
                axis.setAttribute("yearNeeded", "true");
                int hours = (int) Math.round((size-1)*step);
                int minuteInterval = (int) Math.round(step*60.);
                axis.setAttribute("minuteInterval", String.valueOf(minuteInterval) );                       
                hidt = lodt.plus(Period.hours(hours));
                axis.setAttribute("hi", hidt.toString(longfmt));
            } else if ( units.contains("day") ) {
                axis.setAttribute("hourNeeded", "false");
                axis.setAttribute("dayNeeded", "true");
                axis.setAttribute("monthNeeded", "true");
                axis.setAttribute("yearNeeded", "true");
                int days = (int) Math.round((size-1)*step);
                hidt = lodt.plus(Period.days(days));
                axis.setAttribute("hi", hidt.toString(longfmt));
            } else if ( units.contains("month") ) {
                axis.setAttribute("hourNeeded", "false");
                axis.setAttribute("dayNeeded", "false");
                axis.setAttribute("monthNeeded", "true");
                axis.setAttribute("yearNeeded", "true");
                int months = (int) Math.round((size-1)*step);
                hidt = lodt.plus(Period.months(months));
                axis.setAttribute("hi", hidt.toString(longfmt));
            } else if ( units.contains("year") ) {
                axis.setAttribute("hourNeeded", "false");
                axis.setAttribute("dayNeeded", "false");
                axis.setAttribute("monthNeeded", "false");
                axis.setAttribute("yearNeeded", "true");                        
                double start = Double.valueOf(tlo).doubleValue();
                int years = (int) Math.round(start + (size-1)*step);
                hidt = lodt.plus(Period.years(years));
                axis.setAttribute("hi", hidt.toString(longfmt));
            }
            
            String modulo = axis.getAttributeValue("modulo");
            String climatology = axis.getAttributeValue("climatology");
            if ( modulo != null && modulo.equals("true") ) {
            	axis.setAttribute("yearNeeded", "false");
            }
            if ( climatology != null && climatology.equals("true") ) {
            	axis.setAttribute("yearNeeded", "false");
            }
            
        }
        
    }
    /**
     * @param fdsURL
     * @param analysis_axes
     * @param jsessionid
     * @throws JDOMException 
     * We aren't going to do this here.  We're going to keep a session object with the user defined variables.
    public void addAnalysisVariables(Variable var, ArrayList<String> analysis_axes, String jsessionid) throws JDOMException {
        
        Grid grid = getGrid(var.getDSID(), var.getAttributeValue("orig_var_id"));
        Grid analysis_grid = (Grid) grid.clone();
        
        for (Iterator aaIt = analysis_axes.iterator(); aaIt.hasNext();) {
            String analysis_axis_type = (String) aaIt.next();           
            analysis_grid.removeAxis(analysis_axis_type);
        }  
        
        analysis_grid.setID( analysis_grid.getID()+"_" +var.getName() + "_" + jsessionid );
        
        var.setGridID(analysis_grid.getID());
        
        if ( !containsVariable( var.getDSID(), var.getID() ) ) {        
           getDatasetElement(var.getDSID()).getChild("variables").addContent(var.toElement());
        }
        
        if ( !containsGrid ( analysis_grid.getID() ) ) {
           getRootElement().getChild("grids").addContent(analysis_grid.toElement());
        }
        
        System.out.println(this.toString());
        
        
    } */
    /**
     * See if this config contains a grid with this ID
     * @param id the id to check
     * @return true if found; false if not
     */
    private boolean containsGrid(String grid_id) {
        
        List gridsElements = getRootElement().getChildren("grids");
        for (Iterator gridsIt = gridsElements.iterator(); gridsIt.hasNext();) {
            Element gridsE = (Element) gridsIt.next();
            List grids = gridsE.getChildren("grid");
            for (Iterator gridIt = grids.iterator(); gridIt.hasNext();) {
                Element gridE = (Element) gridIt.next();
                String ID = gridE.getAttributeValue("ID");
                if ( ID.equals(grid_id) ) {
                    return true;
                }
            }
        }

        return false;
    }
    /**
     * Does this config contain this data set and variable?
     * @param dsid the data set
     * @param id the variable
     * @return
     * @throws JDOMException 
     */
    private boolean containsVariable(String dsid, String var_id) throws JDOMException {
    	// Could be done with getElementByXPath, no?
        Element dataset = getDatasetElement(dsid);
        List variables = dataset.getChild("variables").getChildren("variable");
        for (Iterator varIt = variables.iterator(); varIt.hasNext();) {
            Element varE = (Element) varIt.next();
            String ID = varE.getAttributeValue("ID");
            if ( ID.equals(var_id) ) {
                return true;
            }
        }
        return false;
    }
    /**
     * Converts to XML that can be validated against a schema, or returns if it detects that XML is already "Version 7".
     *
     */
    public void convertToSeven() {
        Element root = getRootElement();
        String version = root.getAttributeValue("version");
        if ( version != null && version.contains("7.")) {
            return;
        }
        root.setAttribute("version", "7.0");
        List children = root.getChildren();
        for (Iterator childIt = children.iterator(); childIt.hasNext();) {
            Element child = (Element) childIt.next();
            if (child.getName().equalsIgnoreCase("datasets"))  {
                List datasets = child.getChildren();
                for (Iterator dsIt = datasets.iterator(); dsIt.hasNext();) {
                    Element dataset = (Element) dsIt.next();
                    /*
                     * Rather than look for the element we want we have to ignore the elements
                     * we don't want (since the "dataset" element names are random.
                     * If there are other names that must be ignored, the must be
                     * added here and below...
                     */
                    
                    if ( !dataset.getName().equals("properties") &&
                    	 !dataset.getName().equals("documentation") &&
                    	 !dataset.getName().equals("contributor")) {
                        String ID = dataset.getName();
                        dataset.setName("dataset");
                        dataset.setAttribute("ID", ID);
                        // Technically, I think there's only one of these per dataset,
                        // but you can't be sure so loop over all you can find.
                        List variablesParents = dataset.getChildren();
                        for (Iterator varsIt = variablesParents.iterator(); varsIt.hasNext();) {
                            Element variablesElement = (Element) varsIt.next();
                            if ( !variablesElement.getName().equals("properties") &&
                            	 !variablesElement.getName().equals("documentation") &&
                            	 !variablesElement.getName().equals("contributor") ) {
                                List variables = variablesElement.getChildren();
                                for (Iterator varIt = variables.iterator(); varIt.hasNext();) {
                                    Element var = (Element) varIt.next();
                                    if ( !var.getName().equals("properties") &&
                                    	 !var.getName().equals("documentation") &&
                                    	 !var.getName().equals("contributor")) {
                                        String VID = var.getName();
                                        var.setName("variable");
                                        var.setAttribute("ID", VID);
                                        Element grid = var.getChild("link");
                                        String GID = grid.getAttributeValue("match");
                                        String[] parts = GID.split("/");
                                        GID = parts[3];
                                        grid.setName("grid");
                                        grid.removeAttribute("match");
                                        grid.setAttribute("IDREF", GID);
                                        // Convert any proproperties.
                                        Element vprops = var.getChild("properties");
                                        if (vprops != null) {
                                            vprops.setContent(LASDocument.convertProperties(vprops));
                                        }
                                    } else if ( var.getName().equals("properties") ){
                                        var.setContent(LASDocument.convertProperties(var));
                                    }
                                }
                            } else if ( variablesElement.getName().equals("properties") ) {
                                variablesElement.setContent(LASDocument.convertProperties(variablesElement));
                            }
                        }
                    } else if ( dataset.getName().equals("properties") ) {
                        dataset.setContent(LASDocument.convertProperties(dataset));
                    }
                }
            } else if ( child.getName().equalsIgnoreCase("grids")) {
                List grids = child.getChildren();
                for (Iterator gridsIt = grids.iterator(); gridsIt.hasNext();) {
                    Element grid = (Element) gridsIt.next();
                    String GID = grid.getName();
                    grid.setName("grid");
                    grid.setAttribute("ID", GID);
                    List axesrefs = grid.getChildren();
                    for (Iterator axisIt = axesrefs.iterator(); axisIt.hasNext();) {
                        Element axis = (Element) axisIt.next();
                        String AID = axis.getAttributeValue("match");
                        axis.setName("axis");
                        String[] parts = AID.split("/");
                        AID = parts[3];
                        axis.setAttribute("IDREF", AID);
                        axis.removeAttribute("match");
                    }
                }               
            } else if (child.getName().equalsIgnoreCase("axes")) {
                List axes = child.getChildren();        
                for (Iterator axesIt = axes.iterator(); axesIt.hasNext();) {                    
                	Element axis = (Element) axesIt.next();
                	if ( !axis.getName().equals("properties")) {
                		String AID = axis.getName();
                		axis.setName("axis");
                		axis.setAttribute("ID", AID);
                		List v = axis.getChildren("v");
                		boolean warn = false;
                		if ( v != null && axis.getAttributeValue("type").equals("t") ) {
                			for (Iterator vIt = v.iterator(); vIt.hasNext();) {
                				Element ve = (Element) vIt.next();
                				String value = ve.getTextTrim();
                				if ( value.equalsIgnoreCase("jan") || value.equalsIgnoreCase("feb") || 
                						value.equalsIgnoreCase("mar") || value.equalsIgnoreCase("apr") ||
                						value.equalsIgnoreCase("may") || value.equalsIgnoreCase("jun") ||
                						value.equalsIgnoreCase("jul") || value.equalsIgnoreCase("aug") ||
                						value.equalsIgnoreCase("sep") || value.equalsIgnoreCase("oct") ||
                						value.equalsIgnoreCase("nov") || value.equalsIgnoreCase("dec") ) {

                					ve.setAttribute("label", value);
                					ve.setText("15-"+value);
                					warn = true;
                				}
                			}
                			if ( warn ) {
                				log.warn("Converted <v>Jan</v> syntax to <v label=\"Jan\">15-Jan</v> syntax for axis "+AID);
                			}
                		}
                	} else if ( axis.getName().equals("properties")) {
                        axis.setContent(LASDocument.convertProperties(axis));
                    }
                }
            } else if ( child.getName().equalsIgnoreCase("properties")) {
                child.setContent(LASDocument.convertProperties(child));
            } else if ( child.getName().equalsIgnoreCase("las_categories") ) {
                List categories = child.getChildren("category"); 
                setID(categories);                     
            }

        }
    }
    /**
     * Helper method to recursively extract the options.
     * @throws JDOMException 
     */
    public ArrayList<Option> extractOptions (String optionID) throws JDOMException {
        ArrayList<Option> options = new ArrayList<Option>();
        Element optiondef = getElementByXPath("/lasdata/lasui/options/optiondef[@name='"+optionID+"']");
        List definedOptions = optiondef.getChildren("option");
        // Collect the options that are defined inside this optiondef.
        Option opB = null;
        for (Iterator doIt = definedOptions.iterator(); doIt.hasNext();) {
            Element opt = (Element) doIt.next();
            opB = new Option((Element)opt.clone());
            options.add(opB);
        }
        // These options are defined by inheritence from other optiondef elements.
        String inherit = optiondef.getAttributeValue("inherit");
        if ( inherit != null ) {
            if ( inherit.contains(",")) {
                String[] inheritedOptionsIDs = optiondef.getAttributeValue("inherit").split(",");
                for (int i = 0; i < inheritedOptionsIDs.length; i++) {
                    options.addAll(extractOptions(inheritedOptionsIDs[i].substring(1)));
                }
            } else {
                options.addAll(extractOptions(inherit.substring(1)));
            }
        }
        return options;
    }
    /**
     * A filter to find a category by its ID
     * @param catid the ID to find
     * @return the category element
     * @throws JDOMException
     */
    private Element findCategory(String catid) throws JDOMException {      
        CategoryFilter filter = new CategoryFilter(catid);
        Iterator catIt= getRootElement().getDescendants(filter);
        return (Element) catIt.next();
    }
    /**
     * Get the base url of this LAS server (the server host, port and context path).
     * @return base url of the LAS server family.
     * @throws JDOMException
     */
    
    public String getBaseServerURL() throws JDOMException {
        Element ops = getElementByXPath("/lasdata/operations");
        String server = ops.getAttributeValue("url");
        if ( server != null && server.contains("ProductServer.do") ) {
            return server.substring(0, server.lastIndexOf("/"));
        } else {
            throw new JDOMException("No server URL found in the las.xml operations element.");
        }
    }
    /**
     * Get any applicable data constraints for a particular data set and variable.
     * @param dsID
     * @param varID
     * @return the list of constraints
     * @throws JDOMException
     */
    public ArrayList<DataConstraint> getConstraints(String dsID, String varID) throws JDOMException {
    	String ui_default = getUIDefaultName(dsID, varID);
    	ui_default = ui_default.substring(ui_default.indexOf("#")+1);
    	if ( ui_default != null && !ui_default.equals("") ) {
    		return getConstraints(ui_default, dsID, varID);
    	} else {
    		return new ArrayList<DataConstraint>();
    	}
    } 
    /**
     * Get any constraints from the named UI default.
     * 
     * @param ui_default
     * @return the constraints
     * @throws JDOMException
     */
    public ArrayList<DataConstraint> getConstraints(String ui_default, String dsID, String varID) throws JDOMException {
    	ArrayList<DataConstraint> constraints = new ArrayList<DataConstraint>();
    	Element def = getUIDefault(ui_default);
    	Element op = getUIMap(def, "ops");
    	List cons = op.getChildren("constraint");
    	for (Iterator consIt = cons.iterator(); consIt.hasNext();) {
    		// Get the reference to the constraint...
			Element constraint = (Element) consIt.next();
			
		    String type = constraint.getAttributeValue("type");
		    String name = constraint.getAttributeValue("name");
		    String ID = constraint.getAttributeValue("ID");
		    if ( type.equals("variable") ) {
		    	DataConstraint vc = getVariableConstraint(dsID, varID);
		    	
		    	if ( name != null ) {
		    	    vc.setName(name);
		    	} else {
		    		vc.setName("variable");
		    	}
		    	
		    	if ( ID != null ) {
		    		vc.setID(ID);
		    	}
		    	constraints.add(vc);
		    } else {
		    	// Build the constraint...
		    	Element full_constraint = new Element("constraint");
		    	if ( name != null ) {
		    	    full_constraint.setAttribute("name", name);
		    	}
		    	if ( ID != null ) {
		    		full_constraint.setAttribute("ID", ID);
		    	}
		    	full_constraint.setAttribute("type", "menu");
		    	// First copy the attributes...
		    	List attrs = constraint.getAttributes();
		    	for (Iterator attIt = attrs.iterator(); attIt.hasNext();) {
					Attribute attr = (Attribute) attIt.next();
					full_constraint.setAttribute(attr.getName(), attr.getValue());
				}
		    	// Then follow the references to get the three parts, left-hand side menu, operations, right-hand side menu
		    	List menus = constraint.getChildren("menu");
		    	int it = 0;
		    	for (Iterator menuIt = menus.iterator(); menuIt.hasNext();) {
					Element menu_ref = (Element) menuIt.next();
					String href = menu_ref.getAttributeValue("href");
					href = href.substring(1, href.length());
					Element menu = getUIMenu(href);
					Element menu_clone = (Element) menu.clone();
					if ( it == 0 ) {
						menu_clone.setAttribute("position", "lhs");
					} else if ( it == 1 ) {
						menu_clone.setAttribute("position", "ops");
					} else if ( it == 2 ) {
						menu_clone.setAttribute("position", "rhs");
					}
					full_constraint.addContent(menu_clone);
					it++;
				}
		    	constraints.add(new DataConstraint(full_constraint));
		    }
		}
    	return constraints;
    }
    /**
     * Build a variable constraint from a particular variable
     * @param dsID the data set ID of the variable
     * @param varID the variable id
     * @return the data constraint for this variable
     * @throws JDOMException
     */
    public DataConstraint getVariableConstraint(String dsID, String varID) throws JDOMException {
    	Element constraint = new Element("constraint");
    	constraint.setAttribute("type", "variable");
    	ArrayList<Variable> vars = getVariables(dsID);
    	Element menu = new Element("menu");
    	menu.setAttribute("position", "lhs");
    	menu.setAttribute("type", "constraint");
		menu.setAttribute("name","variable_"+dsID);
    	for (Iterator varIt = vars.iterator(); varIt.hasNext();) {
			Variable var = (Variable) varIt.next();			
			Element item = new Element("item");
			item.setAttribute("values", var.getID());
			item.setText(var.getName());
			menu.addContent(item);
		}
    	constraint.addContent(menu);
    	menu = new Element("menu");
    	menu.setAttribute("position", "ops");
    	menu.setAttribute("type", "constraint");
		menu.setAttribute("name","variable_"+dsID);
    	
		Element item = new Element("item");
    	item.setAttribute("values",">=");
    	item.setText(">=");
    	menu.addContent(item);
    	
    	item = new Element("item");
    	item.setAttribute("values",">");
    	item.setText(">");
    	menu.addContent(item);
		
		item = new Element("item");
    	item.setAttribute("values","=");
    	item.setText("=");
    	menu.addContent(item);
    	
    	item = new Element("item");
    	item.setAttribute("values","!=");
    	item.setText("!=");
    	menu.addContent(item);
    	
    	item = new Element("item");
    	item.setAttribute("values","<");
    	item.setText("<");
    	menu.addContent(item);
    	
    	item = new Element("item");
    	item.setAttribute("values","<=");
    	item.setText("<=");
    	menu.addContent(item);
    	
    	constraint.addContent(menu);
    	
    	return new DataConstraint(constraint);
    }
    public CategorySerializable[] getCategorySerializable(ArrayList<Category> categories) throws LASException, JDOMException {
    	CategorySerializable[] cats = new CategorySerializable[categories.size()];
    	int i=0;
    	for (Iterator catIt = categories.iterator(); catIt.hasNext();) {
    		Category cat = (Category) catIt.next();
    		CategorySerializable wireCat = cat.getCategorySerializable();
    		if ( wireCat.hasMultipleDatasets() ) {
    			DatasetSerializable[] ds = wireCat.getDatasetSerializableArray();
    			for (int j = 0; j < ds.length; j++) {
					VariableSerializable[] wireVars = ds[j].getVariablesSerializable();
					for (int k = 0; k < wireVars.length; k++) {
    					VariableSerializable var = wireVars[k];
    					Grid grid = getGrid(var.getDSID(), var.getID());
    					var.setGrid(grid.getGridSerializable());
    				}
				}

    		} else {
    			if ( wireCat.isVariableChildren() ) {
    				VariableSerializable[] wireVars = wireCat.getDatasetSerializable().getVariablesSerializable();
    				for (int j = 0; j < wireVars.length; j++) {
    					VariableSerializable var = wireVars[j];
    					Grid grid = getGrid(var.getDSID(), var.getID());
    					var.setGrid(grid.getGridSerializable());
    				}
    			}
    		}
    		cats[i] = wireCat;

    		i++;
    	}
    	return cats;
    }
    /**
     * Get the categories directly below this id.  If the id is null get the top.
     * @param catid
     * @return
     * @throws JDOMException
     */
    public ArrayList<Category> getCategories(String catid) throws JDOMException {
        ArrayList<Category> categories = new ArrayList<Category>();
        List tops = getRootElement().getChildren("las_categories");
        if ( catid == null ) {
            // Get the top level categories...

            if ( tops != null && tops.size() > 0 ) {
                for (Iterator topIt = tops.iterator(); topIt.hasNext();) {
                    Element las_categoryE = (Element)((Element) topIt.next()).clone();
                    List cats = (List) las_categoryE.getChildren("category");
                    for (Iterator catIt = cats.iterator(); catIt.hasNext();) {
                        Element category = (Element) ((Element) catIt.next()).clone();
                        Element category_container = new Element("category");
                        List attributes = category.getAttributes();
                        for (Iterator attrIt = attributes.iterator(); attrIt.hasNext();) {
                            Attribute attr = (Attribute) attrIt.next();
                            category_container.setAttribute(attr.getName(), attr.getValue());
                        }
                        // Set the "chidren" attribute to identify what kind of kids it has.
                        if ( category.getChild("filter") != null ) {
                        	Element filter = category.getChild("filter");
                        	Dataset dataset = getDataset(filter);
                        	if ( dataset != null ) {
                        	    category_container.setAttribute("children_dsid", dataset.getAttributeValue("ID"));
                        	}
                            category_container.setAttribute("children", "variables");
                        } else {
                            category_container.setAttribute("children", "categories");
                        }
                        categories.add(new Category(category_container));
                    }
                }
            } else {
                categories = getDatasets();
            }
        } else {
            // There are categories in the config, use them...
            if ( tops != null && tops.size() > 0 ) {
                // Either the category has other categories as children...

                // When a category has categories as children, we need to only include
                // the next level down.  Therefore we copy the attributes and add
                // the child categories without the grandchildren.
                Element category = findCategory(catid);
                Element category_container = new Element("category");
                List attributes = category.getAttributes();
                for (Iterator attrIt = attributes.iterator(); attrIt.hasNext();) {
                    Attribute attr = (Attribute) attrIt.next();
                    category_container.setAttribute(attr.getName(), attr.getValue());
                }
                List cats = (List) category.getChildren("category");
                if ( cats.size() > 0 ) {
                    for (Iterator catsIt = cats.iterator(); catsIt.hasNext();) {
                        Element cat = (Element) catsIt.next();
                        Element cat_nokids = new Element("category");
                        List cat_attr = cat.getAttributes();
                        for (Iterator attrIt = cat_attr.iterator(); attrIt.hasNext();) {
                            Attribute attr = (Attribute) attrIt.next();
                            cat_nokids.setAttribute(attr.getName(), attr.getValue());
                        }
                        List filters = cat.getChildren("filter");
                        if ( filters.size() > 0 ) {  
                        	Element filter = (Element)filters.get(0);
                        	Dataset dataset = getDataset(filter);
                        	if ( dataset != null ) {
                        		// Add only if the filter returns a dataset.  It's possible to create
                        		// a filter that returns an empty list.
                        	    cat_nokids.setAttribute("children_dsid", dataset.getAttributeValue("ID"));
                        	}
                            cat_nokids.setAttribute("children", "variables");
                        } else {
                            cat_nokids.setAttribute("children", "categories");
                        }
                        categories.add(new Category(cat_nokids));
                    }
                }

                // or it has variables...
                if ( category.getChild("filter") != null ) {
                    List filters = category.getChildren("filter");
                    for (Iterator filterIt = filters.iterator(); filterIt.hasNext();) {
                        Element filter = (Element) filterIt.next();
                        /*
                         * The rules say that all of the variables caught by a filter
                         * must come from the same dataset.  I think the best way to
                         * represent this collection of information is via a dataset
                         * element the filtered list of variables.  Therefore this
                         * is a getDataset call...
                         */
                        Dataset dataset = getDataset(filter);
                        if ( dataset != null ) {
                            category_container.addContent(dataset.getElement());
                        }
                    }
                    categories.add(new Category(category_container));
                }               
            } else {
                // This config has no "categories", just datasets and variables.  Use them.
                Element dataset = getDatasetElement(catid);
                Element container_dataset;
                if ( dataset != null ) {
                    container_dataset = (Element) dataset.clone();
                } else {
                    // send an empty one
                    container_dataset = new Element("dataset");
                }
                Element category = new Element("category");
                category.addContent(container_dataset);
                categories.add(new Category(category));
            }
        }           
        return categories;
    }
    /**
     * Get data access URL.  If fds is set to true, then the FDS URL will be
     * returned in every case.  If the fds boolean is set to false, then the 
     * actual OPeNDAP URL of the remote data set will be returned where available.
     * If no remote URL is available, the the FDS URL will be returned.
     * @param xpath the XPath of the variable
     * @param fds true if FDS URL is required
     * @throws JDOMException 
     * @throws LASException 
     */
    public String getDataAccessURL(String xpath, boolean fds) throws LASException, JDOMException {
        String url = "";      
        String dataObjectURL = getDataObjectURL(xpath);
        if (dataObjectURL == null || dataObjectURL.equals("")) {
            return url;
        }
        // If this is a local data set then fds must be set to true.
        if ( !dataObjectURL.startsWith("http:")) {
            fds = true;
        }
        if (fds) {
            url = getFTDSURL(xpath);
        } else {
            url = dataObjectURL;
        }
        return url;
    }
    /**
     * Get data access URL.  If fds is set to true, then the FDS URL will be
     * returned in every case.  If the fds boolean is set to false, then the 
     * actual OPeNDAP URL of the remote data set will be returned where available.
     * If no remote URL is available, the the FDS URL will be returned.
     * @param dsID the Dataset ID
     * @param varID the Variable ID
     * @param fds true if FDS URL is required
     * @throws JDOMException 
     * @throws LASException 
     */
    public String getDataAccessURL(String dsID, String varID, boolean fds) throws LASException, JDOMException {
        return getDataAccessURL("/lasdata/datasets/dataset[@ID='"+dsID+"']/variables/variable[@ID='"+varID+"']", fds);
    }
    /**
     * Returns the data access URL for a particular variable via the XPath of the variable (strips off the #var)
     * @param xpathValue The XPath of the variable
     * @return url The access ready URL OPeNDAP URL or filename.
     * @throws JDOMException
     * @throws LASException
     */
    public String getDataObjectURL(Element variable) throws LASException, JDOMException {
        String url = getFullDataObjectURL(variable);
        if ( url.contains("#")) {
            url = url.substring(0,url.indexOf("#"));
        }
        return url;
    }
    /**
     * Returns the OPeNDAP ready data access URL for a particular variable via the XPath of the variable (strips off the #var)
     * @param xpathValue The XPath of the variable 
     * @return url The access ready URL OPeNDAP URL or filename.
     * @throws JDOMException 
     * @throws LASException 
     */
    public String getDataObjectURL(String xpathValue) throws LASException, JDOMException {
        String url = getFullDataObjectURL(xpathValue);
        if ( url.contains("#")) {
            url = url.substring(0,url.indexOf("#"));
        }
        return url;
    }
    /**
	 * Return a list of attributes on the given element
	 * @param xpathValue The XPath to the element
	 * @return attribute The list of JDOM attributes on this element.
	 * @throws JDOMException
	 */
	public List getDataOjectAttributes(String xpathValue) throws JDOMException {
	    /*
	     * We know this is a variable XPath.  If it's "old style" fix it.
	     */
	    if (!xpathValue.contains("@ID")) {
	        String[] parts = xpathValue.split("/");
	        // Throw away index 0 since the string has a leading "/".
	        xpathValue = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
	    }
	    Element variable = getElementByXPath(xpathValue);
	    return variable.getAttributes();
	}
    /**
     * Extract data sets based on a &lt;filter&gt; element from the config. 
     * @param filter A category filter element to be used to select variables from the configuration.
     * @return the data set that matches the filter
     */
    private Dataset getDataset(Element filter) {
        Dataset container_dataset = null;
        String action = filter.getAttributeValue("action");

        String name_contains = filter.getAttributeValue("contains");
        String name_equals = filter.getAttributeValue("equals");
        String tag_contains = filter.getAttributeValue("contains-tag");
        String tag_equals = filter.getAttributeValue("equals-tag");
        
        if ( action.equals("apply-dataset") ) {

            List datasets = getRootElement().getChildren("datasets");
            /**
             * In this case, we're going to take the first dataset that matches
             * and ignore all the rest...
             */
            for (Iterator datasetsIt = datasets.iterator(); datasetsIt.hasNext();) {
                Element datasetsE = (Element) datasetsIt.next();
                List memberDatasets = datasetsE.getChildren("dataset");
                // If we found a match quit. I don't like this so much, but it might be a bit more efficient.
                if ( container_dataset != null ) {
                    break;
                }
                for (Iterator memberDSIt = memberDatasets.iterator(); memberDSIt.hasNext();) {
                    Element dataset = (Element) memberDSIt.next();
                    Element container_dataset_element = (Element) dataset.clone();
                    String name = dataset.getAttributeValue("name");
                    String ID = dataset.getAttributeValue("ID");
                    if ( (name_contains != null && name.contains(name_contains)) ||
                         (name_equals != null && name.equals(name_equals)) ||
                         (tag_contains != null && ID.contains(tag_contains)) ||
                         (tag_equals != null && ID.equals(tag_equals)) ) {
                        // There is nothing to do except create the dataset container and break out.
                        container_dataset = new Dataset(container_dataset_element);
                        break; // I don't like this so much, but it might be a bit more efficient.
                    }
                }
            }
        } else if ( action.equals("apply-variable") ) {
            /*
             * In this case we need to find the data set that contains the first match,
             * filter its member variables to include only the matches.
             */
            List datasets = getRootElement().getChildren("datasets");
            for (Iterator datasetsIt = datasets.iterator(); datasetsIt.hasNext();) {
                if ( container_dataset != null ) {
                    break; // Ugly loop breaking in the interest of efficiency.
                }
                Element datasetsE = (Element) datasetsIt.next();
                List memberDatasets = datasetsE.getChildren("dataset");
                for (Iterator memberDSIt = memberDatasets.iterator(); memberDSIt.hasNext();) {
                    if ( container_dataset != null ) {
                        break;  // Ugly loop breaking in the interest of efficiency.
                    }
                    Element dataset = (Element) memberDSIt.next();    
                    Element container_dataset_element = (Element) dataset.clone();
                    container_dataset_element.removeChildren("variables");
                        List memberVariables = dataset.getChild("variables").getChildren("variable");
                        for (Iterator varIt = memberVariables.iterator(); varIt.hasNext();) {
                            Element variable = (Element) varIt.next();
                            Element container_variable = (Element) variable.clone();
                            String name = variable.getAttributeValue("name");
                            String ID = variable.getAttributeValue("ID");
                            if ( (name_contains != null && name.contains(name_contains)) ||
                                 (name_equals != null && name.equals(name_equals)) ||
                                 (tag_contains != null && ID.contains(tag_contains)) ||
                                 (tag_equals != null && ID.equals(tag_equals)) ) { 
                                 container_dataset_element.addContent(container_variable);
                        }
                    }
                    container_dataset = new Dataset(container_dataset_element);
                }
            }
        }
        return container_dataset;
    }
    
    /**
	 * Get all of the attributes from the parent data set element.
	 * @param varXPath the variable whose parent data set will be used
	 * @return the attributes
	 * @throws JDOMException 
	 */
	public HashMap <String, String> getDatasetAttributes(String varXPath) throws JDOMException {
	    HashMap<String, String> attrs = new HashMap<String, String>();
	    if (!varXPath.contains("@ID")) {
	        String[] parts = varXPath.split("/");
	        // Throw away index 0 since the string has a leading "/".
	        varXPath = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
	    }
	    
	    Element variable = getElementByXPath(varXPath);
	    Element dataset = variable.getParentElement().getParentElement();
	    List attributes = dataset.getAttributes();
	    for (Iterator iter = attributes.iterator(); iter.hasNext();) {
	        Attribute attr = (Attribute) iter.next();
	        String name = attr.getName();
	        String value = attr.getValue();
	        if ( name != null && value != null && !name.equals("") && !value.equals("") ) {
	           attrs.put(name, value );
	        }
	    }
	    return attrs;
	}
    /**
     * Get the &lt;dataset&gt; element from this config that matches this ID
     * @param dsid
     * @return
     * @throws JDOMException 
     */
    private Element getDatasetElement(String dsid) throws JDOMException {
        String xPath = "/lasdata/datasets/dataset[@ID='"+dsid+"']";
        return getElementByXPath(xPath);
    }
    
    /**
     * Returns all the datasets as gov.noaa.pmel.tmap.las.util.Dataset objects.
     * @return ArrayList of dataset objects
     */
    public ArrayList<Category> getDatasets() {
        ArrayList<Category> datasets = new ArrayList<Category>();
        Element datasetsE = getDatasetsAsElement();
        List datasetElements = datasetsE.getChildren("dataset");
        for (Iterator dsIt = datasetElements.iterator(); dsIt.hasNext();) {
            Element dataset = (Element) dsIt.next();
            Element ds_novars = (Element)dataset.clone();
            ds_novars.setName("category");
            /*
             * Since we don't want all the children (the variables, composites, etc.)
             * we'll remove every things that's not properties.  However, we will
             * keep the contributor and documentation elements.
             */
            List children = ds_novars.getChildren();
            ArrayList<String> remove = new ArrayList<String>();
            for (Iterator childIt = children.iterator(); childIt.hasNext();) {
                Element child = (Element) childIt.next();
                if (!child.getName().equals("properties") && 
                    !child.getName().equals("documentation") && 
                    !child.getName().equals("contributor") ) {
                    remove.add(child.getName());                  
                }
            }     
            for (int i=0; i < remove.size(); i++) {
               ds_novars.removeChild(remove.get(i));
            }
            ds_novars.setAttribute("children", "variables");
            ds_novars.setAttribute("children_dsid", ds_novars.getAttributeValue("ID"));
            Category ds = new Category(ds_novars);                
            datasets.add(ds);
        }
        return datasets;
    }
    /**
     * Return all datasets as a single "datasets" element.
     * 
     */
    public Element getDatasetsAsElement() {
        Element datasets = new Element("datasets");
        List datasetsElements = getRootElement().getChildren("datasets");
        for (Iterator dseIt = datasetsElements.iterator(); dseIt.hasNext();) {
            Element dsets = (Element) dseIt.next();
            List datasetElements = dsets.getChildren("dataset");
            for (Iterator dsIt = datasetElements.iterator(); dsIt.hasNext();) {
                Element dataset = (Element) dsIt.next();
                datasets.addContent((Element)dataset.clone());
            }
        }
        return datasets;
    }
    /**
     * Get a &lt;dataset&gt; element base on the data set id
     * @param dsID the id of the dataset 
     * @return the data set element
     * @throws JDOMException
     */
    public Dataset getDataset(String dsID) throws JDOMException {
    	String xpathValue = "/lasdata/datasets/dataset[@ID='"+dsID+"']";
    	Element ds = getElementByXPath(xpathValue);
    	if ( ds != null ) {
    		return new Dataset(ds);
    	} else {
    		return null;
    	}
    }
    /**
     * !!!! returns datasets list This ignores categories for now.  Have to fix this.
     * Don't need this anymore
     * @deprecated
     */
    //TODO decide what to do about categories...
    public ArrayList<NameValuePair> getDatasetsAsNameValueBeans() {
        ArrayList <NameValuePair> datasets = new ArrayList<NameValuePair>();
        List datasetsElements = getRootElement().getChildren("datasets");
        for (Iterator dseIt = datasetsElements.iterator(); dseIt.hasNext();) {
            Element dsets = (Element) dseIt.next();
            List datasetElements = dsets.getChildren("dataset");
            for (Iterator dsIt = datasetElements.iterator(); dsIt.hasNext();) {
                Element dataset = (Element) dsIt.next();
                String name = dataset.getAttributeValue("name");
                String value = dataset.getAttributeValue("ID");
                datasets.add(new NameValuePair(name, value));
            }
        }
        return datasets;
    }
    /**
     * Get first variable from LASConfig.
     * @return The XPath of the first variable in the LASConfig (the default)
     */
    public String getFirstVariable() {
        Element dataset = getRootElement().getChild("datasets").getChild("dataset");
        String datasetID = dataset.getAttributeValue("ID");
        Element variable = dataset.getChild("variables").getChild("variable");
        String variableID = variable.getAttributeValue("ID");
        return "/lasdata/datasets/dataset[@ID='"+datasetID+"']/variables/variable[@ID='"+variableID+"']";
    }
    /**
     * Returns the full data URL for a particular variable (as identified by the variable element) including the #var (netCDF variable name convention used by LAS)
     * @param variable The variable element of the variable whose data access URL is desired
     * @return url The access URL (either a /path/path/filename#var or http://opendap_url#var).
     * @throws JDOMException
     * @throws LASException
     */
    public String getFullDataObjectURL(Element variable) throws LASException, JDOMException {
        if ( variable == null ) {
            throw new LASException("Variable not found.");
        }
        Element dataset = variable.getParentElement().getParentElement();
        String varURL = variable.getAttributeValue("url");
        String dsURL = dataset.getAttributeValue("url");
        String url = "";
        if ( varURL != null && dsURL != null) {
            if ( varURL.startsWith("http://";) || varURL.startsWith("file:///") ) {
                url = varURL;
            } else {
                url = dsURL + varURL;
            }
        } else if ( varURL != null && dsURL == null ) {
            url = varURL;
        } else if ( varURL == null && dsURL != null ) {
            url = dsURL;
        } else if ( varURL == null && dsURL == null ) {
            url = null;
        }

        if (url.startsWith("file://")) {
            url = url.substring(6, url.length());
        }
        if (url.startsWith("file:/")) {
            url = url.substring(5, url.length());
        }
        if (url.startsWith("file:")) {
            url = url.substring(5, url.length());
        }
         return url;
    }
    /**
     * Returns the full data URL for a particular variable (as identified by its XPath) including the #var (netCDF variable name convention used by LAS)
     * @param xpathValue The XPath of the variable whose data access URL is desired
     * @return url The access URL (either a /path/path/filename#var or http://opendap_url#var).
     * @throws JDOMException 
     * @throws LASException
     */
    public String getFullDataObjectURL(String xpathValue) throws LASException, JDOMException {
        /*
         * We know this is a variable XPath.  If it's "old style" fix it.
         */
        if (!xpathValue.contains("@ID")) {
            String[] parts = xpathValue.split("/");
            // Throw away index 0 since the string has a leading "/".
            xpathValue = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
        }
        Element variable = getElementByXPath(xpathValue);
        if ( variable == null ) {
            throw new LASException("Variable: "+xpathValue+" not found.");
        }
        Element dataset = variable.getParentElement().getParentElement();
        String varURL = variable.getAttributeValue("url");
        String dsURL = dataset.getAttributeValue("url");
        String url = "";
        if ( varURL != null && dsURL != null) {
            if ( varURL.startsWith("http://";) || varURL.startsWith("file:///") ) {
                url = varURL;
            } else {
                url = dsURL + varURL;
            }         
        } else if ( varURL != null && dsURL == null ) {
            url = varURL;
        } else if ( varURL == null && dsURL != null ) {
            url = dsURL;
        } else if ( varURL == null && dsURL == null ) {
            url = null;
        }
        
        if ( url == null ) {
        	log.warn("URL for "+xpathValue+" is empty.");
        	url = "none";
        }
        
        if (url.startsWith("file://")) {
            url = url.substring(6, url.length());
        }
        if (url.startsWith("file:/")) {
            url = url.substring(5, url.length());
        }
        if (url.startsWith("file:")) {
            url = url.substring(5, url.length());
        }
         return url;
    }
    /**
     * Get the value of a "global" property (a property in the config that is not in any particular data set or variable).
     * @param group the name of the property group
     * @param name the name of the property
     * @return the value of specified property
     */
    public String getGlobalPropertyValue(String group, String name) {
        String value = "";
        Element properties = getRootElement().getChild("properties");
        if (properties != null) {
            List groups = properties.getChildren("property_group");
            for (Iterator groupsIt = groups.iterator(); groupsIt.hasNext();) {
                Element property_group = (Element) groupsIt.next();
                if ( property_group.getAttributeValue("type").equals(group)) {
                    List props = property_group.getChildren("property");
                    for (Iterator propsIt = props.iterator(); propsIt.hasNext();) {
                        Element property = (Element) propsIt.next();
                        String pname = property.getChildTextNormalize("name");
                        String pvalue = property.getChildTextNormalize("value");
                        if ( pname.equals(name) ) {
                            return pvalue;
                        }
                    }
                }
            }
        }
        return value;
    }
    /**
     * Get the grid of a variable from its XPath
     * @param varXPath
     * @return
     * @throws JDOMException
     * @throws LASException
     */
    public Grid getGrid(String varXPath) throws JDOMException, LASException {
    	if (!varXPath.contains("@ID")) {
            String[] parts = varXPath.split("/");
            // Throw away index 0 since the string has a leading "/".
            varXPath = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
        }
    	Element variable = getElementByXPath(varXPath);
        ArrayList<Element> axes_list = new ArrayList<Element>();
        Element gridE = null;
        if (variable != null) {
            String ID = variable.getChild("grid").getAttributeValue("IDREF");          
            gridE = (Element) getElementByXPath("/lasdata/grids/grid[@ID='"+ID+"']").clone();          
            List axes = gridE.getChildren("axis");           
            for (Iterator axisIt = axes.iterator(); axisIt.hasNext();) {
                Element axis_ref = (Element) axisIt.next();                
                String axisID = axis_ref.getAttributeValue("IDREF");
                Element axisE = (Element) getElementByXPath("/lasdata/axes/axis[@ID='"+axisID+"']").clone();
                String type = axisE.getAttributeValue("type");
                if ( type.equals("x") || type.equals("y") || type.equals("z") ) {
                    axes_list.add(axisE);
                } else if (type.equals("t") ) {
                    addTimeAxisAttributes(axisE);
                    axes_list.add(axisE);
                } else if ( type.equals("d") ) {
                	axes_list.add(axisE);
                }
            }
        }
        // Replace the references with the actual axis definition.
        if ( gridE != null ) {
            gridE.setContent(axes_list);
            return new Grid(gridE);
        } else {
        	throw new LASException("The grid was empty.");
        }
    }
    /**
     * Get grid for a particular dataset and variable.
     * 
     * @param dsid the id of the desired data set
     * @param varid the id if the desired variable
     * @return grid the Grid object with up to for Axes
     * @throws JDOMException 
     */
    public Grid getGrid(String dsID, String varID) throws JDOMException, LASException {
        return getGrid("/lasdata/datasets/dataset[@ID='"+dsID+"']/variables/variable[@ID='"+varID+"']");
    }
    /**
	 * Get the grid_type for the variable (regular, scattered, ...)
	 * @param dsID
	 * @param varID
	 * @throws JDOMException 
	 */
	public String getGridType(String dsID, String varID) throws JDOMException {
	    String grid_type="";
	    String varXPath = "/lasdata/datasets/dataset[@ID='"+dsID+"']/variables/variable[@ID='"+varID+"']";
	    Element var = getElementByXPath(varXPath);
	    if ( var != null ) {
	        grid_type = var.getAttributeValue("grid_type");
	        if ( grid_type == null ) {
	            grid_type = "";
	        }
	    }
	    return grid_type;
	}
    /**
     * Get hi value for a particular axis type for the specified variable
     * @param varpath XPath to the variable
     * @param type which axis x,y,z or t
     * 
     */
    public String getHi(String type, String varpath) throws JDOMException {
        String hi = null;
        /*
         * We know this is a variable XPath.  If it's "old style" fix it.
         */
        if (!varpath.contains("@ID")) {
            String[] parts = varpath.split("/");
            // Throw away index 0 since the string has a leading "/".
            varpath = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
        }
        Element variable = getElementByXPath(varpath);
        if (variable == null) {
            return hi;
        }
        String gridID = variable.getChild("grid").getAttributeValue("IDREF");
        Element grid = getElementByXPath("/lasdata/grids/grid[@ID='"+gridID+"']");
        if (grid == null) {
            return hi;
        }
        List axes = grid.getChildren("axis");
        for (Iterator axIt = axes.iterator(); axIt.hasNext();) {
            Element axis = (Element) axIt.next();
            String axisID = axis.getAttributeValue("IDREF");
            axis = getElementByXPath("/lasdata/axes/axis[@ID='"+axisID+"']");
            String t = axis.getAttributeValue("type");
            if ( type.equals(t) ) {
                Element arange = axis.getChild("arange");
                if (arange == null) {
                    List v = axis.getChildren("v");
                    Element vN = (Element) v.get(v.size()-1);
                    hi = vN.getTextTrim();
                } else {
                    String st = arange.getAttributeValue("start");
                    double start = Double.valueOf(st).doubleValue();
                    double step = Double.valueOf(arange.getAttributeValue("step")).doubleValue();
                    double size = Double.valueOf(arange.getAttributeValue("size")).doubleValue();
                    double end = start+(size-1)*step;
                    hi = String.valueOf(end);
                }
            }
        }
        return hi;
    }
    /**
     * Get the container with all the information about the institution that installed this LAS.
     * @return Institution the information about the place that installed this LAS.
     */
    public Institution getInstitution() throws JDOMException {
        Element institution = getElementByXPath("/lasdata/institution");
        return new Institution(institution);
    }
	/**
     * Get lo value for a particular axis type for the specified variable.  If it's 
     * the T axis you get a string with the low value, but you might want a TimeSelector instead.
     * @param varpath XPath to the variable
     * @param type which axis x,y,z or t 
     * 
     */
    public String getLo(String type, String varpath) throws JDOMException {
        String lo = null;
        /*
         * We know this is a variable XPath.  If it's "old style" fix it.
         */
        if (!varpath.contains("@ID")) {
            String[] parts = varpath.split("/");
            // Throw away index 0 since the string has a leading "/".
            varpath = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
        }
        Element variable = getElementByXPath(varpath);
        if (variable == null) {
            return lo;
        }
        String gridID = variable.getChild("grid").getAttributeValue("IDREF");
        Element grid = getElementByXPath("/lasdata/grids/grid[@ID='"+gridID+"']");
        if (grid == null) {
            return lo;
        }
        List axes = grid.getChildren("axis");
        for (Iterator axIt = axes.iterator(); axIt.hasNext();) {
            Element axis = (Element) axIt.next();
            String axisID = axis.getAttributeValue("IDREF");
            axis = getElementByXPath("/lasdata/axes/axis[@ID='"+axisID+"']");
            String t = axis.getAttributeValue("type");
            // do xyz
            if ( type.equals(t) ) {
                Element arange = axis.getChild("arange");
                if (arange == null) {
                    List v = axis.getChildren("v");
                    Element v0 = (Element) v.get(0);
                    lo = v0.getTextTrim();
                } else {
                    lo = arange.getAttributeValue("start");
                }
            }
        }
        return lo;
    }
    /**
	 * Given a dataset element merge the properties (probably should be private)
	 * All properties should have been converted to "V7.0" style properties
	 * before this code is called.
	 * @param dsE The dataset element to merge
	 * @return datasetElement The same element with all of the new properites folded in to each variable.
	 */
	public Element getMergedProperties(Element dsE) {
	    
	    // The colleciton is a HashMap of HashMaps.
	    // TODO these should be lists and loop through the list and combine any
	    // stray property groups...
	    Element variablePropsE = dsE.getChild("properties");
	    // The parent of the variable is <variables> the parent of <variables>
	    // is the particular <[datasetname]> tag.
	    Element datasetPropsE = dsE.getParentElement().getParentElement().getChild("properties");
	    // Same as above, plus the parent of the particular dataset tag is <datasets> and
	    // the parent of that is <lasdata>
	    Element globalPropsE = dsE.getParentElement().getParentElement().getParentElement().getParentElement().getChild("properties");
	    
	    
	    HashMap<String, HashMap<String, String>> propertyGroups = new HashMap<String, HashMap<String, String>>();
	    if (globalPropsE != null) {               
	        // All children should be elements of the form <property_group type="name">
	        List propGroups = globalPropsE.getChildren();                
	        for (Iterator pgIt = propGroups.iterator(); pgIt.hasNext();) {
	            Element propGroupE = (Element) pgIt.next();
	            // All children should be elements of the form <property><name>thename</name><value>thevalue</value></property>
	            List props = propGroupE.getChildren();
	            HashMap<String, String> property = new HashMap<String, String>();
	            for (Iterator propIt = props.iterator(); propIt.hasNext();) {
	                Element prop = (Element) propIt.next();
	                String propName = prop.getChildTextNormalize("name");
	                String propValue = prop.getChildTextNormalize("value");
	                property.put(propName, propValue);
	            }
	            propertyGroups.put(propGroupE.getAttributeValue("type"), property);
	        }
	    }
	    // If the group already exists any properties from this element
	    // replace the properites from the "global" properties.
	    if (datasetPropsE != null) {
	        List propGroups = datasetPropsE.getChildren();
	        for (Iterator pgIt = propGroups.iterator(); pgIt.hasNext();) {
	            Element propGroupE = (Element) pgIt.next();
	            List props = propGroupE.getChildren();
	            HashMap<String, String> group = propertyGroups.get(propGroupE.getAttributeValue("type"));
	            if (group == null) {
	                group = new HashMap<String, String>();
	            }
	            for (Iterator propIt = props.iterator(); propIt.hasNext();) {
	                Element prop = (Element) propIt.next();
	                String propName = prop.getChildTextNormalize("name");
	                String propValue = prop.getChildTextNormalize("value");
	                String currentValue = group.get(propName);
	                if (currentValue == null) {
	                    group.put(propName, propValue);
	                } else if (currentValue.equals("default")) {
	                    group.put(propName, propValue);
	                }
	            }
	            
	            propertyGroups.put(propGroupE.getAttributeValue("type"), group);
	        }
	    }
	    
	    if (variablePropsE != null) {
	        List propGroups = variablePropsE.getChildren();
	        for (Iterator pgIt = propGroups.iterator(); pgIt.hasNext();) {
	            Element propGroupE = (Element) pgIt.next();
	            List props = propGroupE.getChildren();
	            HashMap<String, String> group = propertyGroups.get(propGroupE.getAttributeValue("type"));
	            if (group == null) {
	                group = new HashMap<String, String>();
	            }
	            for (Iterator propIt = props.iterator(); propIt.hasNext();) {
	                Element prop = (Element) propIt.next();
	                String propName = prop.getChildTextNormalize("name");
	                String propValue = prop.getChildTextNormalize("value");
	                String currentValue = group.get(propName);
	                if (currentValue == null) {
	                    group.put(propName, propValue);
	                } else if (currentValue.equals("default")) {
	                    group.put(propName, propValue);
	                }                    
	            }
	            
	            propertyGroups.put(propGroupE.getAttributeValue("type"), group);
	        }
	    }
	    
	    Element properties = new Element("properties");
	    Element dsGroupE = null;
	    for (Iterator propertyGroupKeyIterator = propertyGroups.keySet()
	            .iterator(); propertyGroupKeyIterator.hasNext();) {
	        String propertyGroupKey = (String) propertyGroupKeyIterator
	        .next();
	        dsGroupE = new Element("property_group");
	        dsGroupE.setAttribute("type", propertyGroupKey);
	        HashMap propertyGroup = (HashMap) propertyGroups.get(propertyGroupKey);
	        for (Iterator keyIt = propertyGroup.keySet().iterator(); keyIt
	        .hasNext();) {
	            String key = (String) keyIt.next();
	            Element property = new Element("property");
	            Element property_name = new Element("name");
	            Element property_value = new Element("value");
	            property_name.addContent(key);
	            property_value.addContent((String) propertyGroup.get(key));
	            property.addContent(property_name);
	            property.addContent(property_value);
	            dsGroupE.addContent(property);
	        }
	        properties.addContent(dsGroupE);
	    }
	    
	    return properties;
	}
    /**
     * !! temporarily busted for V7 XML -- Returns the merged properties for a particular (maybe this should be private)
     * This /lasdata/datasets/DatasetTagName/variables/VariableTagName path and it
     * should be /lasdata/datasets/dataset[@ID='DatasetID']/variables/variable[@ID='VariableID'].
     * @deprecated
     * @param xpathValue
     * @return Element The varaible element with the properties merged into it.
     * @throws JDOMException
     */
 
        public Element getMergedProperties(String xpathValue) throws JDOMException {
            /*
             * We know this is a variable XPath.  If it's "old style" fix it.
             */
            if (!xpathValue.contains("@ID")) {
                String[] parts = xpathValue.split("/");
                // Throw away index 0 since the string has a leading "/".
                xpathValue = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
            }
            return getMergedProperties(getElementByXPath(xpathValue));
        }
    /**
	 * Get operations for a data set and variable, either by the associated default or by the interval.
	 * @throws JDOMException 
	 */
	public ArrayList<Operation> getOperations(String view, String dsID, String varID) throws JDOMException {
		ArrayList<Operation> operations = new ArrayList<Operation>();

		String grid_type = getGridType(dsID, varID);

		String ui_default = getUIDefaultName(dsID, varID);

		if ( ui_default != null && !ui_default.equals("") ) {
			ui_default = ui_default.substring(ui_default.indexOf("#")+1, ui_default.length());
			operations = getOperationsByDefault(view, ui_default);
		} else {
			operations = getOperationsByIntervalAndGridType(view, grid_type);
		}
		return operations;
	}

	/**
	 * Get the operations that are include with a particular UI default
	 * @param ui_default
	 * @return operations JSONObject with the operations that are defined for this "default".
	 * @throws JDOMException 
	 */
	public ArrayList<Operation> getOperationsByDefault(String view, String ui_default) throws JDOMException {
		ArrayList<Operation> operations = new ArrayList<Operation>();
		Element def = getUIDefault(ui_default);
		if ( def != null ) {
			Element map = getUIMap(def, "ops");               
			if (map != null) {
				List ifmenusElements = map.getChildren("ifmenu");
				for (Iterator ifmenusIt = ifmenusElements.iterator(); ifmenusIt
				.hasNext();) {
					Element ifmenu = (Element) ifmenusIt.next();
					// An ifmenu is not always controlled by a view.
					// Could be the mode as well !!
					// TODO handle distinctions for comparison mode

					String ifmenu_view = ifmenu.getAttributeValue("view");
					if ( ifmenu_view != null ) {
						if (ifmenu_view.equals(view)) {
							String ops_menu_ref = ifmenu
							.getAttributeValue("href");

							Element menu = getElementByXPath("/lasdata/lasui/menus/menu[@name='"
									+ ops_menu_ref.substring(1) + "']");
							List ops = menu.getChildren("item");
							for (Iterator opsIt = ops.iterator(); opsIt
							.hasNext();) {
								Element item = (Element) opsIt.next();
								String value = item.getAttributeValue("values");
								/* This is pulling out information that was designed to be the values
								 * of an HTML menu.  It's not such a great way to store informaiton that
								 * is intended to be used to extract further information from the XML.
								 * Therefore there's a lot of splitting and spitting to get the job done.
								 * TODO we should re-think the XML at some point...  Soon?
								 */
								String opID = value.substring(0, value
										.indexOf(","));
								Element opE = getElementByXPath("/lasdata/operations/operation[@ID='"
										+ opID + "']");
								if ( opE != null ) {
									Operation op = new Operation(opE);
									operations.add(op);
								} else {
									log.warn("Operation "+opID+" from default "+ui_default+" not found in configuration.");
								}
							}
						}
					}
				}
			}                
		}
		return operations;
	}
    /**
     * Get the operations based on the axes defined on an interval and the grid type.
	 * @param view  The view to be matched to the interval specifications in the operation config
	 * @param grid_type the desired grid type
	 * @return the list of operations
	 */
	public ArrayList<Operation> getOperationsByIntervalAndGridType(String view, String grid_type) throws JDOMException {

	    ArrayList<Operation> matchingOperations = new ArrayList<Operation>();
	    List operationsElements = getRootElement().getChildren("operations");
	    for (Iterator opsElementsIt = operationsElements.iterator(); opsElementsIt.hasNext();) {               
	        Element opsElement = (Element) opsElementsIt.next();
	        List opElements = opsElement.getChildren("operation");
	        for (Iterator opIt = opElements.iterator(); opIt.hasNext(); ) {
	            boolean grid_type_match = false;
	            boolean intervals_match = false;
	            boolean degenerate_match = false;
	            Element operation = (Element) opIt.next();
	            Element region = operation.getChild("region");
	            Element grid_types = operation.getChild("grid_types");
	            if (region != null && grid_types != null) {
	                List types = grid_types.getChildren("grid_type");
	                for (Iterator typeIt = types.iterator(); typeIt.hasNext();) {
	                    Element type = (Element) typeIt.next();
	                    if (type.getAttributeValue("name").equals(
	                            grid_type)) {
	                        grid_type_match = true;
	                    }
	                }
	                List intervals = region.getChildren("intervals");
	                for (Iterator intvIt = intervals.iterator(); intvIt.hasNext();) {
	                    Element intv = (Element) intvIt.next();
	                    if (intv.getAttributeValue("name").equals(view)) {
	                        intervals_match = true;
	                    }
	                }
	                Element degenerate = region.getChild("degenerate");
	                if ( degenerate != null && view.equals("d") ) {
	                	degenerate_match = true;
	                }
	                boolean private_op = false;
	                String private_attr = operation.getAttributeValue("private");
	                if ( private_attr != null && private_attr.equals("true") ) {
	                    private_op = true;
	                }
	                if (grid_type_match && (intervals_match||degenerate_match) && !private_op) {
	                    Operation op = new Operation(operation);
	                    matchingOperations.add(op);
	                }
	            }
	        }                
	    }
	    return matchingOperations;
	}
    /**
     * Selects operations that use interval inputs on the axes listed in the view
     * 
     * @param view a string containing an ordered subset of xyzt.
     * @return operations a list of operation elements that use this view
     * @throws JDOMException 
     * @deprecated
     */
    public ArrayList<NameValuePair> getOperationsByView(String view) throws JDOMException {
        ArrayList<NameValuePair> ops = new ArrayList<NameValuePair>();
        String path = "/lasdata/operations/operation[@intervals='"+view+"']";
        XPath xpath = XPath.newInstance(path);
        for (Iterator nodes = xpath.selectNodes(this).iterator(); nodes.hasNext(); ) {
            Element op = (Element)nodes.next();
            ops.add(new NameValuePair(op.getAttributeValue("name"), op.getAttributeValue("ID")));
        }
        return ops;
    }
    

    /**
     * Returns the options associated with this option ID
     * @param optionID option ID
     * @return options the options for this id.
     * @throws JDOMException 
     */
    public ArrayList<Option> getOptions(String optionID) throws JDOMException {
        ArrayList<Option> options = new ArrayList<Option>();
        // Collect the options from this ID (which is really the name, but nevermind).
        options.addAll(extractOptions(optionID));
        return options;
    }
    /**
     * Returns the options associated with this operation ID
     * @param operationID operation ID
     * @return options the options for this operation.
     * @throws JDOMException 
     */
    public ArrayList<Option> getOptionsByOperationID(String operationID) throws JDOMException {
        ArrayList<Option> options = new ArrayList<Option>();
        
        // Be sure to make items null for textarea.
        
        Element op = getElementByXPath("/lasdata/operations/operation[@ID='"+operationID+"']");
        if ( op != null ) {
            Element option = op.getChild("optiondef");
            if ( option != null ) {
                String optionID = option.getAttributeValue("IDREF");
                if ( optionID != null ) {
                    // Collect the options from this ID (which is really the name, but nevermind).
                    options.addAll(extractOptions(optionID));
                }
            }
        }
        return options;
    }
    /**
	 * Get the name of the output directory for this LAS
	 * @return outputdir The path to the output directory.
	 */
	public String getOutputDir() {
	    return this.getRootElement().getChildText("output_dir");
	}
    /**
     * Get xy region for a particular variable
     * @param varpath XPath to variable
     * @return region ArrayList of NameValueBeans with x_lo, x_hi, y_lo and y_hi
     * @throws JDOMException 
     */
    public ArrayList<NameValuePair> getRangeForXY(String varpath) throws JDOMException {
        ArrayList<NameValuePair> region = new ArrayList<NameValuePair>();
        /*
         * We know this is a variable XPath.  If it's "old style" fix it.
         */
        if (!varpath.contains("@ID")) {
            String[] parts = varpath.split("/");
            // Throw away index 0 since the string has a leading "/".
            varpath = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
        }
        Element variable = getElementByXPath(varpath);
        if (variable == null) {
            return region;
        }
        String gridID = variable.getChild("grid").getAttributeValue("IDREF");
        Element grid = getElementByXPath("/lasdata/grids/grid[@ID='"+gridID+"']");
        if (grid == null) {
            return region;
        }
        List axes = grid.getChildren("axis");
        for (Iterator axIt = axes.iterator(); axIt.hasNext();) {
            Element axis = (Element) axIt.next();
            String axisID = axis.getAttributeValue("IDREF");
            axis = getElementByXPath("/lasdata/axes/axis[@ID='"+axisID+"']");
            String type = axis.getAttributeValue("type");
            if ( type.equals("x") || type.equals("y") ) {
                NameValuePair lo=null;
                NameValuePair hi=null;
                if ( type.equals("x") ) {
                    lo = new NameValuePair("x_lo", "");
                    hi = new NameValuePair("x_hi", "");
                } else if ( type.equals("y") ) {
                    lo = new NameValuePair("y_lo", "");
                    hi = new NameValuePair("y_hi", "");
                }
                Element arange = axis.getChild("arange");
                if (arange == null) {
                    List v = axis.getChildren("v");
                    Element v0 = (Element) v.get(0);
                    Element vN = (Element) v.get(v.size()-1);
                    lo.setValue(v0.getTextTrim());
                    hi.setValue(vN.getTextTrim());
                } else {
                    String st = arange.getAttributeValue("start");
                    lo.setValue(st);
                    double start = Double.valueOf(st).doubleValue();
                    long step = Long.valueOf(arange.getAttributeValue("step")).longValue();
                    double size = Double.valueOf(arange.getAttributeValue("size")).doubleValue();
                    double end = start+(size-1)*step;
                    hi.setValue(String.valueOf(end));
                }
                region.add(lo);
                region.add(hi);
            }
        }
        return region;
    }
    
    /**
     * Get the pre-defined regions, by UI default based on the data set and variable.
     * @param dsID
     * @param varID
     * @return
     * @throws JDOMException 
     */
    public ArrayList<Region> getRegions(String dsID, String varID) throws JDOMException {
    	ArrayList<Region> regions = new ArrayList<Region>();
    	Element ui_default = getUIDefault(dsID, varID);
    	Element livemap = getUIMap(ui_default, "livemap");
    	if ( livemap != null ) {
    		Element menu = livemap.getChild("menu");
    		String href = menu.getAttributeValue("href");
    		menu = getElementByXPath("/lasdata/lasui/menus/menu[@name='"+href.substring(1)+"']");
    		List items = menu.getChildren("item");
    		for (Iterator itemsIt = items.iterator(); itemsIt.hasNext();) {
    			Element item = (Element) itemsIt.next();
    			Element region = new Element("region");
    			String values = item.getAttributeValue("values");
    			String name = item.getTextTrim();
    			region.setAttribute("name", name);
    			region.setAttribute("ID", name);
    			region.setAttribute("values", values);
    			String[] corners = values.split(",");
    			region.setAttribute("xhi", corners[0]);
    			region.setAttribute("xlo", corners[1]);
    			region.setAttribute("yhi", corners[2]);
    			region.setAttribute("ylo", corners[3]);
    			Region reg = new Region(region);
    			regions.add(reg);
    		}
    	}
    	return regions;
    }
    /**
     * Get all variables in a data set that are defined on a regular grid
     * @param dsID the data set
     * @return the list of variables
     * @throws JDOMException
     */
    public ArrayList<NameValuePair> getRegularVariables(String dsID) throws JDOMException {
        ArrayList<NameValuePair> variables = new ArrayList<NameValuePair>();
        Element dataset = getElementByXPath("/lasdata/datasets/dataset[@ID='"+dsID+"']");
        List variablesElements = dataset.getChildren("variables");
        for (Iterator vseIt = variablesElements.iterator(); vseIt.hasNext();) {
            Element variablesElement = (Element) vseIt.next();
            List vars = variablesElement.getChildren("variable");
            for (Iterator varIt = vars.iterator(); varIt.hasNext();) {             
                Element variable = (Element) varIt.next();
                String grid_type = variable.getAttributeValue("grid_type");
                if (grid_type.equals("regular")) {
                    String name = variable.getAttributeValue("name");
                    String value = variable.getAttributeValue("ID");
                    variables.add(new NameValuePair(name, value));
                }
            }
        }
        return variables;
    }
    /**
     * Get the URL of the product server.  This URL should be correct even if the
     * server is being proxied through Apache.
     * @throws JDOMException 
     */
    
    public String getServerURL() throws JDOMException {
        String server = "";
        Element ops = getElementByXPath("/lasdata/operations");
        server = ops.getAttributeValue("url");
        return server;
    }
    
    /**
     * Get the name of the service based on the operation ID.
     * @param opID the ID of the operation
     * @return service the name of the service associated with this operation
     * @throws JDOMException 
     */
    public String getService(String opID) throws JDOMException {
        String service = null;
        Element op = getElementByXPath("/lasdata/operations/operation[@ID='"+opID+"']");
        if ( op != null ) {
            Element serviceE = op.getChild("service");
            if ( serviceE != null ) {
                return serviceE.getTextNormalize();
            }
        }
        return service;
    }
       
    /**
     * Get time selector object the specified variable
     * @param varpath XPath to the variable 
     * @throws JDOMException 
     * 
     */
    public TimeSelector getT(String varpath) throws JDOMException {
        TimeSelector t = new TimeSelector();
        /*
         * We know this is a variable XPath.  If it's "old style" fix it.
         */
        if (!varpath.contains("@ID")) {
            String[] parts = varpath.split("/");
            // Throw away index 0 since the string has a leading "/".
            varpath = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
        }
        Element variable = getElementByXPath(varpath);
        if (variable == null) {
            return t;
        }
        String gridID = variable.getChild("grid").getAttributeValue("IDREF");
        Element grid = getElementByXPath("/lasdata/grids/grid[@ID='"+gridID+"']");
        if (grid == null) {
            return t;
        }
        List axes = grid.getChildren("axis");
        for (Iterator axIt = axes.iterator(); axIt.hasNext();) {
            Element axis = (Element) axIt.next();
            String axisID = axis.getAttributeValue("IDREF");
            axis = getElementByXPath("/lasdata/axes/axis[@ID='"+axisID+"']");
            String type = axis.getAttributeValue("type");
            if ( type.equals("t") ) {
                String default_display = axis.getAttributeValue("default");
                Element arange = axis.getChild("arange");
                if (arange == null) {
                    t.setType("menu");
                    List v = axis.getChildren("v");
                    Element v0 = (Element) v.get(0);
                    String tlo = v0.getTextTrim();
                    Element vN = (Element) v.get(v.size()-1);
                    String thi = vN.getTextTrim();
                    t.setLo(tlo);
                    t.setCurrent_lo(tlo);                   
                    if ( default_display != null && default_display.equals("last") ) {
                        t.setCurrent_hi(thi);
                    } else {
                        t.setCurrent_hi(tlo);
                    }                    
                    StateNameValueList vs = new StateNameValueList();
                    for (Iterator vIt = v.iterator(); vIt.hasNext();) {
                        Element vElement = (Element) vIt.next();
                        vs.add(new NameValuePair(vElement.getTextNormalize(), vElement.getTextNormalize()));
                    }
                    t.setLo_items(vs);
                    t.setHi_items(vs);
                    if (default_display != null && default_display.equals("last") ) {
                        t.getHi_items().setCurrent(thi);
                    }
                } else {
                    t.setType("widget");
                    String tlo = arange.getAttributeValue("start");                   
                    String units = axis.getAttributeValue("units");
                    double size = Double.valueOf(arange.getAttributeValue("size")).doubleValue();
                    double step = Double.valueOf(arange.getAttributeValue("step")).doubleValue();
                    DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd HH").withZone(DateTimeZone.UTC);
                    DateTimeFormatter zfmt = DateTimeFormat.forPattern("yyyy-MM-dd").withZone(DateTimeZone.UTC);
                    DateTimeFormatter longfmt = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").withZone(DateTimeZone.UTC);
                    DateTime lodt;
                    if ( tlo.length() == 10 ) {
                        lodt = zfmt.parseDateTime(tlo);
                    } else {
                        lodt = fmt.parseDateTime(tlo);
                    }
                    t.setLo(lodt.toString(longfmt));
                    t.setCurrent_lo(t.getLo());
                    DateTime hidt = new DateTime();
                    if ( units.contains("hour") ) {
                        t.setHourNeeded(true);
                        t.setDayNeeded(true);
                        t.setMonthNeeded(true);
                        t.setYearNeeded(true);
                        int hours = (int) Math.round((size-1)*step);
                        int minuteInterval = (int) Math.round(step*60.);
                        t.setMinuteInterval(minuteInterval);                       
                        hidt = lodt.plus(Period.hours(hours));
                        t.setHi(hidt.toString(longfmt));
                    } else if ( units.contains("day") ) {
                        t.setHourNeeded(false);
                        t.setDayNeeded(true);
                        t.setMonthNeeded(true);
                        t.setYearNeeded(true);
                        int days = (int) Math.round((size-1)*step);
                        hidt = lodt.plus(Period.days(days));
                        t.setHi(hidt.toString(longfmt));
                    } else if ( units.contains("month") ) {
                        t.setHourNeeded(false);
                        t.setDayNeeded(false);
                        t.setMonthNeeded(true);
                        t.setYearNeeded(true);
                        int months = (int) Math.round((size-1)*step);
                        hidt = lodt.plus(Period.months(months));
                        t.setHi(hidt.toString(longfmt));
                    } else if ( units.contains("year") ) {
                        t.setHourNeeded(false);
                        t.setDayNeeded(false);
                        t.setMonthNeeded(false);
                        t.setYearNeeded(true);                        
                        double start = Double.valueOf(tlo).doubleValue();
                        int years = (int) Math.round(start + (size-1)*step);
                        hidt = lodt.plus(Period.years(years));
                        t.setHi(hidt.toString(longfmt));
                    }
                    if ( default_display != null && default_display.equals("last") ) {
                        t.setCurrent_hi(hidt.toString(longfmt));
                    } else {
                        t.setCurrent_hi(lodt.toString(longfmt));
                    } 
                }
            }
        }
        return t;
    }
        /**
         * Given the XPath to an operation return the output template that should be processed for this product
         * @param XPath The path to the operation element
         * @return output_template The name of the template (which will be resolve using the class loader and the backend config information)
         * @throws JDOMException
         */
        public String getTemplateByXPath(String XPath) throws JDOMException {
            Element opE = getElementByXPath(XPath);
            if (opE != null) {
                return opE.getAttributeValue("output_template");
            } else {
                //TODO configurable default output_template.
                return "output";
            }
        }
        
        /**
         * Get the F-TDS URL for this variable
		 * @param variable the variable element
		 * @return the F-TDS URL
		 * @throws JDOMException
		 */
		public String getFTDSURL(Element variable) {
		    String ftds_url = variable.getAttributeValue("ftds_url");
		    if ( ftds_url != null ) {
		        return ftds_url;
		    } else {
		        return "";
		    }
		}
        
        /**
         * Get the F-TDS URL for the variable specified by the XPath
		 * @param varXPath the XPath of the variable
		 * @return the F-TDS URL
		 * @throws JDOMException
		 */
		public String getFTDSURL(String varXPath) throws JDOMException {
		    if (!varXPath.contains("@ID")) {
		        String[] parts = varXPath.split("/");
		        // Throw away index 0 since the string has a leading "/".
		        varXPath = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
		    }
		    Element variable = getElementByXPath(varXPath);
		    return getFTDSURL(variable);
		}
		/**
         * Get the F-TDS URL for the variable specified by the dsID and varID
		 * @param dsID of the variable
		 * @param varID of the variable
		 * @return the F-TDS URL
		 * @throws JDOMException
		 */
		public String getFTDSURL(String dsID, String varID) throws JDOMException {
			String varXPath = "/lasdata/datasets/dataset[@ID='"+dsID+"']/"+"variables/variable[@ID='"+varID+"']";
		    Element variable = getElementByXPath(varXPath);
		    return getFTDSURL(variable);
		}
        /**
         * Create a time axis with all the info needed for the DateWidgets
         * @param variable the variable whose time axis will be built
         * @return the time axis specification
         * @throws JDOMException
         * @throws LASException
         */
        public TimeAxis getTime(Element variable) throws JDOMException, LASException {
		    TimeAxis t = null;
		    if (variable == null) {
		        return t;
		    }
		    String gridID = variable.getChild("grid").getAttributeValue("IDREF");
		    Element grid = getElementByXPath("/lasdata/grids/grid[@ID='"+gridID+"']");
		    if (grid == null) {
		        return t;
		    }
		    List axes = grid.getChildren("axis");
		    
		    for (Iterator axIt = axes.iterator(); axIt.hasNext();) {
		        Element axis = (Element) axIt.next();
		        String axisID = axis.getAttributeValue("IDREF");
		        axis = getElementByXPath("/lasdata/axes/axis[@ID='"+axisID+"']");
		        String type = axis.getAttributeValue("type");
		        if ( type.equals("t") ) {
		            
		            Element arange = axis.getChild("arange");
		            if (arange == null) {
		                                
		                List v = axis.getChildren("v");
		                Element v0 = (Element) v.get(0);
		                String tlo = v0.getTextTrim();
		                Element vN = (Element) v.get(v.size()-1);
		                String thi = vN.getTextTrim();
		                
		                ArrayList<NameValuePair> vs = new ArrayList<NameValuePair>();
		                for (Iterator vIt = v.iterator(); vIt.hasNext();) {
		                    Element vElement = (Element) vIt.next();
		                    String label = vElement.getAttributeValue("label");
		                    if ( label == null ) {
		                        label = vElement.getTextNormalize();
		                    }
		                    vs.add(new NameValuePair(label, vElement.getTextNormalize()));
		                }
		                t = new TimeAxis(axis);
		                t.setLo(tlo);
		                t.setHi(thi);
		                t.setDisplay_type("menu");
		                
		            } else {
		                t = new TimeAxis(axis);
		                t.setDisplay_type("widget");
		                String tlo = arange.getAttributeValue("start");                   
		                String units = axis.getAttributeValue("units");
		                double size = Double.valueOf(arange.getAttributeValue("size")).doubleValue();
		                double step = Double.valueOf(arange.getAttributeValue("step")).doubleValue();
		                DateTimeFormatter fmt = null;
		                DateTime lodt = new DateTime("9000-01-01");
		                boolean found = false;
		                for (int i = 0; i < time_formats.length; i++) {
		                    fmt = DateTimeFormat.forPattern(time_formats[i]).withZone(DateTimeZone.UTC);
		                    try {
		                       lodt = fmt.parseDateTime(tlo);
		                       found = true;
		                    } catch ( IllegalArgumentException e ) {
		                        found = false;
		                    }
		                    if (found) break;
		                }
		                
		                if ( !found ) {
		                    throw new LASException("Time format for "+tlo+" could not be parsed.");
		                }
		                
		                t.setLo(lodt.toString(fmt));
		                DateTime hidt = new DateTime();
		                if ( units.contains("hour") ) {
		                    t.setHourNeeded(true);
		                    t.setDayNeeded(true);
		                    t.setMonthNeeded(true);
		                    t.setYearNeeded(true);
		                    int hours = (int) Math.round((size-1)*step);
		                    int minuteInterval = (int) Math.round(step*60.);
		                    t.setMinuteInterval(minuteInterval);                       
		                    hidt = lodt.plus(Period.hours(hours));
		                    t.setHi(hidt.toString(fmt));
		                } else if ( units.contains("day") ) {
		                    t.setHourNeeded(false);
		                    t.setDayNeeded(true);
		                    t.setMonthNeeded(true);
		                    t.setYearNeeded(true);
		                    int days = (int) Math.round((size-1)*step);
		                    hidt = lodt.plus(Period.days(days));
		                    t.setHi(hidt.toString(fmt));
		                } else if ( units.contains("month") ) {
		                    t.setHourNeeded(false);
		                    t.setDayNeeded(false);
		                    t.setMonthNeeded(true);
		                    t.setYearNeeded(true);
		                    int months = (int) Math.round((size-1)*step);
		                    hidt = lodt.plus(Period.months(months));
		                    t.setHi(hidt.toString(fmt));
		                } else if ( units.contains("year") ) {
		                    t.setHourNeeded(false);
		                    t.setDayNeeded(false);
		                    t.setMonthNeeded(false);
		                    t.setYearNeeded(true);                        
		                    double start = Double.valueOf(tlo).doubleValue();
		                    int years = (int) Math.round(start + (size-1)*step);
		                    hidt = lodt.plus(Period.years(years));
		                    t.setHi(hidt.toString(fmt));
		                }
		            }
		        }
		    }
		    return t;
		}
        /**
		 * Get time selector object the specified variable
		 * @param varpath XPath to the variable 
		 * @throws LASException 
		 * 
		 */
		public TimeAxis getTime(String varpath) throws JDOMException, LASException {
		    
		    /*
		     * We know this is a variable XPath.  If it's "old style" fix it.
		     */
		    if (!varpath.contains("@ID")) {
		        String[] parts = varpath.split("/");
		        // Throw away index 0 since the string has a leading "/".
		        varpath = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
		    }
		    
		    Element variable = getElementByXPath(varpath);
		    return getTime(variable);
		    
		}
        /**
		 * Extracts the LAS title from the configuration
		 * @return title The title of this LAS
		 * @throws JDOMException
		 */
		public String getTitle() throws JDOMException {
		    String title = "";
		    Element ui = getElementByXPath("/lasdata/lasui");
		    if ( ui != null ) {
		        title = ui.getAttributeValue("title");
		        if ( title == null ) {
		            title="";
		        }
		    }
		    return title;
		}
		/**
		 * Get the name of the UI Default associated with this variable.
		 * @param dsID the data set
		 * @param varID the variable
		 * @return the UI Default name; can be null
		 * @throws JDOMException
		 */
	    public String getUIDefaultName(String dsID, String varID) throws JDOMException {
			return getVariablePropertyValue("/lasdata/datasets/dataset[@ID='"+dsID+"']/variables/variable[@ID='"+varID+"']","ui", "default");
		}
	    /**
	     * Get the actual UI Default element
	     * @param dsID the data set
	     * @param varID the variable
	     * @return the default element (can be null)
	     * @throws JDOMException
	     */
	    public Element getUIDefault(String dsID, String varID) throws JDOMException {
	    	return getUIDefault(getUIDefaultName(dsID, varID));
	    }
	    /**
	     * Get the default element based on its name
	     * @param ui_default the name of the desired default
	     * @return the default element (can be null)
	     */
        public Element getUIDefault(String ui_default) {
            List defaultsElements = getRootElement().getChild("lasui").getChildren("defaults");
            for (Iterator defsIt = defaultsElements.iterator(); defsIt.hasNext();) {
                Element defaultsE = (Element) defsIt.next();
                List defaults = defaultsE.getChildren("default");
                for (Iterator defIt = defaults.iterator(); defIt.hasNext();) {
                    Element def = (Element) defIt.next();
                    String name = def.getAttributeValue("name");
                    // The default default has no name...
                    if ( name != null ) {
                        if ( name.equals(ui_default)  ) {
                            return def;
                        }
                    } else {
                        // The default "default" has no name so the input is null and the match is null.
                        if ( ui_default == null || ui_default.equals("") ) {
                            return def;
                        }
                    }
                }
            }
            return null;
        }
        public Element getUIMap(Element def, String intype) throws JDOMException {
            List maps = def.getChildren("map");
            for (Iterator mapIt = maps.iterator(); mapIt.hasNext();) {
                Element map_ref = (Element) mapIt.next();
                String ref = map_ref.getAttributeValue("href");
                Element map = getElementByXPath("/lasdata/lasui/maps/map[@name='"+ref.substring(1)+"']");
                String type = map.getAttributeValue("type");
                if ( type.equals(intype)) {
                   return map;
                }
            }
            return null;
        }
        public Element getUIMenu (String href) throws JDOMException {
        	return getElementByXPath("/lasdata/lasui/menus/menu[@name='"+href+"']");
        }

        /**
		 * Extracts of list of known LAS UI Clients for this product server.
		 * @return ui_url_list a list of URLs of known LAS UI Clients for this server
		 * @throws JDOMException
		 */
		public ArrayList<String> getUIs() {
		    ArrayList<String> uis = new ArrayList<String>();
		    List uiElements = getRootElement().getChild("lasui").getChildren("ui");
		    for (Iterator uiIt = uiElements.iterator(); uiIt.hasNext();) {
		        Element ui = (Element) uiIt.next();
		        uis.add(ui.getAttributeValue("url"));
		    }
		    return uis;
		}

        public Variable getVariableByXPath(String xpath) throws JDOMException {
		    String dsid;
		    if (!xpath.contains("@ID")) {
		        String[] parts = xpath.split("/");
		        // Throw away index 0 since the string has a leading "/".
		        xpath = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
		        dsid = parts[3];
		    } else {
		        dsid = xpath.substring(xpath.indexOf("dataset[@ID='")+13,xpath.indexOf("']"));
		    }
		    Element variable = getElementByXPath(xpath);
		    if ( variable != null ) {
		        return new Variable(variable, dsid);
		    } else {
		        return null;
		    }
		}
        public String getVariableIntervals(String xpath) throws JDOMException {
		    Element variable = getElementByXPath(xpath);
		    return variable.getAttributeValue("intervals");
		}
        /**
		 * !!! does not work with V7 XML...Returns the netCDF variable name from the variable's XPath (the #var or the variable ID)
		 * @param xpathValue The XPath of the variable
		 * @return var The netCDF variable name
		 * @throws JDOMException
		 */
		public String getVariableName(String xpathValue) throws LASException, JDOMException {
		    String url = getFullDataObjectURL(xpathValue);
		    if ( url.contains("#")) {
		        return url.substring(url.indexOf("#")+1, url.length());
		    } else {
		        /*
		         * We know this is a variable XPath.  If it's "old style" fix it.
		         */
		        if (!xpathValue.contains("@ID")) {
		            String[] parts = xpathValue.split("/");
		            // Throw away index 0 since the string has a leading "/".
		            xpathValue = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
		        }
		        Element var = getElementByXPath(xpathValue);
		        if ( var.getAttributeValue("ID") != null ) {
		            return var.getAttributeValue("ID");
		        } else {
		            return var.getName();
		        }
		    }
		}
        public String getVariableName(String dsID, String varID) throws JDOMException, LASException {
			return getVariableName("/lasdata/datasets/dataset[@ID='"+dsID+"']/variables/variable[@ID='"+varID+"']");
		}
        public String getVariablePoints(String xpath) throws JDOMException {
		    Element variable = getElementByXPath(xpath);
		    return variable.getAttributeValue("points");
		}
        /**
         * Extract the properties group from a variable given its JDOM Element
         * container as an Array List of NameValueBeans
         * @param variable Element an XML variable element
         * @param group the name of the property group to extract
         * @return properties The an array list of properties.
         *
         */
        public HashMap getVariableProperties(Element variable, String group) throws JDOMException {
            HashMap<String, String> propMap = new HashMap<String, String>();
            List propGroups = variable.getChild("properties").getChildren("property_group");
            for (Iterator pgIt = propGroups.iterator(); pgIt.hasNext();) {
                Element propGroupE = (Element) pgIt.next();
                if ( propGroupE.getAttributeValue("type").equals(group)) {
                    List props = propGroupE.getChildren("property");
                    for (Iterator pIt = props.iterator(); pIt.hasNext();) {
                        Element prop  = (Element) pIt.next();
                        String name = prop.getChildText("name");
                        String value = prop.getChildText("value");
                        propMap.put(name, value);
                    }
                }
            }
            return propMap;
        }
        /**
         * Extract the properties element from a variable given its XPath
         * @param xpathValue The XPath of the variable to find.
         * @return properties The properties element.
         *
         */
        public Element getVariableProperties(String xpathValue) throws JDOMException {
            /*
             * We know this is a variable XPath.  If it's "old style" fix it.
             */
            if (!xpathValue.contains("@ID")) {
                String[] parts = xpathValue.split("/");
                // Throw away index 0 since the string has a leading "/".
                xpathValue = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
            }
            Element variable = getElementByXPath(xpathValue);
            return variable.getChild("properties");
        }
        /**
         * Extract the properties group from a variable given its XPath as an
         * Array List of NameValueBeans
         * @param xpathValue The XPath of the variable to find.
         * @return properties The an array list of properties.
         *
         */
        public HashMap getVariableProperties(String xpathValue, String group) throws JDOMException {
            /*
             * We know this is a variable XPath.  If it's "old style" fix it.
             */
            HashMap<String, String> theProps = new HashMap<String, String>();
            if (!xpathValue.contains("@ID")) {
                String[] parts = xpathValue.split("/");
                // Throw away index 0 since the string has a leading "/".
                xpathValue = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
            }
            Element variable = getElementByXPath(xpathValue);
            List propGroups = variable.getChild("properties").getChildren("property_group");
            for (Iterator pgIt = propGroups.iterator(); pgIt.hasNext();) {
                Element propGroupE = (Element) pgIt.next();
                List props = propGroupE.getChildren("property");
                for (Iterator pIt = props.iterator(); pIt.hasNext();) {
                    Element prop  = (Element) pIt.next();
                    String name = prop.getChildText("name");
                    String value = prop.getChildText("value");
                    theProps.put(name, value);
                }
            }
            return theProps;
        }

    /**
	 * Extract a property value from a variable element
	 * Array List of NameValueBeans
	 * @param xpathValue The XPath of the variable to find.
	 * @return value the property value
	 *
	 */
	public String getVariablePropertyValue(Element variable, String group, String property) throws JDOMException {
	    if ( variable != null ) {
	        List propGroups = variable.getChild("properties").getChildren("property_group");
	        for (Iterator pgIt = propGroups.iterator(); pgIt.hasNext();) {
	            Element propGroupE = (Element) pgIt.next();
	            if ( propGroupE.getAttributeValue("type").equals(group )) {
	                List props = propGroupE.getChildren("property");
	                for (Iterator pIt = props.iterator(); pIt.hasNext();) {
	                    Element prop  = (Element) pIt.next();
	                    String name = prop.getChildText("name");
	                    String value = prop.getChildText("value");
	                    if (name.equals(property)) {
	                        return value;
	                    }
	                }
	            }
	        }
	    }
	    return "";
	}
    /**
	 * Extract a property value from a variable given its XPath as an
	 * Array List of NameValueBeans
	 * @param xpathValue The XPath of the variable to find.
	 * @return value the property value
	 *
	 */
	public String getVariablePropertyValue(String xpathValue, String group, String property) throws JDOMException {
	    /*
	     * We know this is a variable XPath.  If it's "old style" fix it.
	     */

	    if (!xpathValue.contains("@ID")) {
	        String[] parts = xpathValue.split("/");
	        // Throw away index 0 since the string has a leading "/".
	        xpathValue = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
	    }
	    Element variable = getElementByXPath(xpathValue);
	    if ( variable != null ) {
	        List propGroups = variable.getChild("properties").getChildren("property_group");
	        for (Iterator pgIt = propGroups.iterator(); pgIt.hasNext();) {
	            Element propGroupE = (Element) pgIt.next();
	            if ( propGroupE.getAttributeValue("type").equals(group )) {
	                List props = propGroupE.getChildren("property");
	                for (Iterator pIt = props.iterator(); pIt.hasNext();) {
	                    Element prop  = (Element) pIt.next();
	                    String name = prop.getChildText("name");
	                    String value = prop.getChildText("value");
	                    if (name.equals(property)) {
	                        return value;
	                    }
	                }
	            }
	        }
	    }
	    return "";
	}
    /**
     * Returns a Variable object from a LAS XML variable Element.
     * @param variable - the varible element
     * @return var - the variable object
     * 
     * Probably won't need this method!!
     * 
     * 
     *
    public Variable getVariable(Element variable) {
        List attributes = variable.getAttributes();
        Variable var = new Variable();
        String dsid = variable.getParentElement().getParentElement().getAttributeValue("ID");
        var.setDSID(dsid);
        ArrayList<NameValuePair> exattrs = new ArrayList<NameValuePair>();
        for (Iterator atIt = attributes.iterator(); atIt.hasNext();) {
            Attribute attr = (Attribute) atIt.next();
            if ( attr.getName().equals("name") ) {
                var.setName(attr.getValue());
            } else if ( attr.getName().equals("ID")) {
                var.setID(attr.getValue());
            } else {
                exattrs.add(new NameValuePair(attr.getName(), attr.getValue()));
            }
        }               
        var.setAttributes(exattrs);
        Element propertiesE = variable.getChild("properties");               
        if ( propertiesE != null ) {
            List groups = propertiesE.getChildren("property_group");
            HashMap<String, ArrayList<NameValuePair>> props = new HashMap<String, ArrayList<NameValuePair>>();
            for (Iterator grpsIt = groups.iterator(); grpsIt.hasNext();) {
                Element group = (Element) grpsIt.next();
                String group_name = group.getAttributeValue("type");
                List properties = group.getChildren("property");
                ArrayList<NameValuePair> group_props = new ArrayList<NameValuePair>();
                for (Iterator propIt = properties.iterator(); propIt
                        .hasNext();) {
                    Element property = (Element) propIt.next();
                    NameValuePair prop = new NameValuePair(property
                            .getChildText("name"), property
                            .getChildText("value"));
                    group_props.add(prop);
                }
                props.put(group_name, group_props);
            }
            var.setProperties(props);
        }     
        Element grid = variable.getChild("grid");
        var.setGridID(grid.getAttributeValue("IDREF"));
        return var;
    }
    */
    /**
     * Returns list of variables in given a dataset as pmel.tmap.las.util.Dataset objects.
     * @param dsID ID of the dataset for which variables should be listed.
     * @return variables Array list of variables
     */
    public ArrayList<Variable> getVariables(String dsID) throws JDOMException {
        ArrayList<Variable> variables = new ArrayList<Variable>();
        Element dataset = getElementByXPath("/lasdata/datasets/dataset[@ID='"+dsID+"']");
        if ( dataset != null ) {
            List variablesElements = dataset.getChildren("variables");
            for (Iterator vseIt = variablesElements.iterator(); vseIt.hasNext();) {
                Element variablesElement = (Element) vseIt.next();
                List vars = variablesElement.getChildren("variable");
                for (Iterator varIt = vars.iterator(); varIt.hasNext();) {
                    Element variable = (Element) varIt.next();
                    Variable var = new Variable(variable, dsID);
                    variables.add(var);
                }
            }
        }
        return variables;
    }
    /**
     * Returns a single variable from a given a dataset as a pmel.tmap.las.util.Variable object.
     * @param dsID ID of the dataset that contains the variable
     * @param varID ID of the variable
     * @return variable Variable object
     */
    public Variable getVariable(String dsID, String varID) throws JDOMException {
        String variableXPath = "/lasdata/datasets/dataset[@ID='"+dsID+"']/variables/variable[@ID='"+varID+"']";
        return getVariableByXPath(variableXPath);
    }
    /**
     * Returns list of variables give a dataset
     * @param dsID ID of the dataset for which variables should be listed.
     * @return variables Array list of variables
     */
    public ArrayList<NameValuePair> getVariablesAsNameValueBeans(String dsID) throws JDOMException {
        ArrayList<NameValuePair> variables = new ArrayList<NameValuePair>();
        Element dataset = getElementByXPath("/lasdata/datasets/dataset[@ID='"+dsID+"']");
        List variablesElements = dataset.getChildren("variables");
        for (Iterator vseIt = variablesElements.iterator(); vseIt.hasNext();) {
            Element variablesElement = (Element) vseIt.next();
            List vars = variablesElement.getChildren("variable");
            for (Iterator varIt = vars.iterator(); varIt.hasNext();) {
                Element variable = (Element) varIt.next();
                String name = variable.getAttributeValue("name");
                String value = variable.getAttributeValue("ID");
                variables.add(new NameValuePair(name, value));
            }
        }
        return variables;
    }
    /**
     * Extract the supposedly human interesting title of a variable
     * @param xpathValue The XPath of the variable
     * @return title The title of the variable (Not to quibble but, it's actually the name attibute)
     * @throws JDOMException
     */
    public String getVariableTitle(String xpathValue) throws JDOMException {
        /*
         * We know this is a variable XPath.  If it's "old style" fix it.
         */
        if (!xpathValue.contains("@ID")) {
            String[] parts = xpathValue.split("/");
            // Throw away index 0 since the string has a leading "/".
            xpathValue = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
        }
        return getElementByXPath(xpathValue).getAttributeValue("name");
    }
    public ArrayList<View> getViewsByDatasetAndVariable(String dsID, String varID) throws JDOMException {
        String variableXPath = "/lasdata/datasets/dataset[@ID='"+dsID+"']/variables/variable[@ID='"+varID+"']";
        String ui_default = "";
        
        ui_default = getVariablePropertyValue(variableXPath,"ui", "default");

        ArrayList<View> views = new ArrayList<View>();

        if ( ui_default != null && !ui_default.equals("") ) {
            // This will be a list of views determined by the installer.  This list
            // will not be modified.  The installer is responsible for picking sensible
            // views for the data.
            ui_default = ui_default.substring(ui_default.indexOf("#")+1, ui_default.length());
            views = getViewsByDefault(ui_default); 
        } else {
            // This returns all possible views.
            ui_default = null;
            ArrayList<View> allViews = getViewsByDefault(ui_default);
            // Now filter them according to the places where the data variable
            // is defined on an interval.
            String intervals = getVariableIntervals(variableXPath);
            
            // Create an ArrayList with all combinations
            ArrayList<String> combos = combo(intervals);
            
            for (Iterator viewIt = allViews.iterator(); viewIt.hasNext();) {
                View view = (View) viewIt.next();
                String view_value = view.getValue();
                
                // If the combination of interval axes matches one of the
                // views add it to the collection of acceptable views.
                
                for (Iterator comboIt = combos.iterator(); comboIt.hasNext();) {
                    String combo = (String) comboIt.next();
                    if ( combo.equals(view_value) ) {
                        views.add(view);
                    }
                }
                
            }
        }
        return views;
    }
    /**
     * @param ui_default
     * @return
     */
    public ArrayList<View> getViewsByDefault(String ui_default) throws JDOMException {
        ArrayList <View> views = new ArrayList<View>(); 
        Element def = getUIDefault(ui_default);
        if ( def != null ) {
            Element map = getUIMap(def, "views");
            if ( map != null ) {
                Element menu = map.getChild("menu");
                String href = menu.getAttributeValue("href");
                menu = getElementByXPath("/lasdata/lasui/menus/menu[@name='"+href.substring(1)+"']");
                List ifmenusElements = menu.getChildren("ifitem");
                for (Iterator ifItemIt = ifmenusElements.iterator(); ifItemIt.hasNext();) {
                    Element ifitem = (Element) ifItemIt.next();
                    Element view = new Element("view");
                    view.setAttribute("name", ifitem.getTextNormalize());
                    view.setAttribute("value", ifitem.getAttributeValue("view"));
                    List attributes = ifitem.getAttributes();
                    for (Iterator attrIt = attributes.iterator(); attrIt.hasNext();) {
                        Attribute attr = (Attribute) attrIt.next();
                        // TODO this really implies we need to rework the XML so that these are expressed with XML that matches
                        // what we want it to look like in the end.
                        if (!attr.getName().equals("value") && !attr.getName().equals("values") && !attr.getName().equals("view") ) {
                            view.setAttribute((Attribute) attr.clone());
                        }
                    }
                    views.add(new View(view));
                }
            }
        }
        return views;
    }
    /** 
     * Returns true if a T axis is defined for this variable.
     * @param varpath XPath of the variable to check
     * @return hasZ boolean  
     */
    public boolean hasT(String varpath) throws JDOMException {
        /*
         * We know this is a variable XPath.  If it's "old style" fix it.
         */
        if (!varpath.contains("@ID")) {
            String[] parts = varpath.split("/");
            // Throw away index 0 since the string has a leading "/".
            varpath = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
        }
        Element variable = getElementByXPath(varpath);
        if (variable == null) {
            return false;
        }
        String gridID = variable.getChild("grid").getAttributeValue("IDREF");
        Element grid = getElementByXPath("/lasdata/grids/grid[@ID='"+gridID+"']");
        if (grid == null) {
            return false;
        }
        List axes = grid.getChildren("axis");
        for (Iterator axIt = axes.iterator(); axIt.hasNext();) {
            Element axis = (Element) axIt.next();
            String axisID = axis.getAttributeValue("IDREF");
            axis = getElementByXPath("/lasdata/axes/axis[@ID='"+axisID+"']");
            String type = axis.getAttributeValue("type");
            if (type.equals("t")) {
                return true;
            }
        }
        return false;
    }
    /** 
     * Returns true if a T axis is defined for this variable.
     * @param dsID ID of the dataset to check
     * @param varID ID of the variable to check
     * @return hasZ boolean  
     */
    public boolean hasT(String dsID, String varID) throws JDOMException {
        String varpath = "/lasdata/datasets/dataset[@ID='"+dsID+"']/variables/variable[@ID='"+varID+"']";
        return hasT(varpath);
    }
    /** 
     * Returns true if a Z axis is defined for this variable.
     * @param varpath XPath of the variable to check
     * @return hasZ boolean  
     * @throws JDOMException 
     */
    public boolean hasZ(String varpath) throws JDOMException {
        /*
         * We know this is a variable XPath.  If it's "old style" fix it.
         */
        if (!varpath.contains("@ID")) {
            String[] parts = varpath.split("/");
            // Throw away index 0 since the string has a leading "/".
            varpath = "/"+parts[1]+"/"+parts[2]+"/dataset[@ID='"+parts[3]+"']/"+parts[4]+"/variable[@ID='"+parts[5]+"']";
        }
        Element variable = getElementByXPath(varpath);
        if (variable == null) {
            return false;
        }
        String gridID = variable.getChild("grid").getAttributeValue("IDREF");
        Element grid = getElementByXPath("/lasdata/grids/grid[@ID='"+gridID+"']");
        if (grid == null) {
            return false;
        }
        List axes = grid.getChildren("axis");
        for (Iterator axIt = axes.iterator(); axIt.hasNext();) {
            Element axis = (Element) axIt.next();
            String axisID = axis.getAttributeValue("IDREF");
            axis = getElementByXPath("/lasdata/axes/axis[@ID='"+axisID+"']");
            String type = axis.getAttributeValue("type");
            if (type.equals("z")) {
                return true;
            }
        }
        return false;
    }
    /** 
     * Returns true if a Z axis is defined for this variable.
     * @param dsID ID of the dataset to check
     * @param varID ID of the variable to check
     * @return hasZ boolean  
     */
    public boolean hasZ(String dsID, String varID) throws JDOMException {
        String varpath = "/lasdata/datasets/dataset[@ID='"+dsID+"']/variables/variable[@ID='"+varID+"']";
        return hasZ(varpath);
    }
    
    public boolean isRegular(String dsID, String varID) throws JDOMException {
        boolean reg = false;
        Element variable = getElementByXPath("/lasdata/datasets/dataset[@ID='"+dsID+"']"+"/variables/variable"+"[@ID='"+varID+"']");
        String grid_type = variable.getAttributeValue("grid_type");
        if ( grid_type.equals("regular")) {
            reg = true;
        }
        return reg;
    }
    /**
     * Merge all of the variable, dataset and global properties for each variable.  Variable properties override dataset
     * properties which in turn override "global" properties (those defined under the /lasdata element).
     *
     */
    public void mergeProperites() {
        List dsTags = this.getRootElement().getChildren("datasets");
        for (Iterator dsTagIt = dsTags.iterator(); dsTagIt.hasNext();) {
            Element dsTag = (Element) dsTagIt.next();
            List datasets = dsTag.getChildren();
            for (Iterator dsIt = datasets.iterator(); dsIt.hasNext();) {
                Element dataset = (Element) dsIt.next();
                List varTags = dataset.getChildren("variables");
                for (Iterator varTagsIt = varTags.iterator(); varTagsIt.hasNext();) {
                    Element varTag = (Element)varTagsIt.next();
                    List variables = varTag.getChildren();
                    for (Iterator varsIt = variables.iterator(); varsIt.hasNext();) {
                        Element var = (Element) varsIt.next();    
                        Element properties = var.getChild("properties");
                        if ( properties != null ) {
                            var.setContent(var.indexOf(properties), getMergedProperties(var));
                        } else {
                            var.addContent(getMergedProperties(var));
                        }
                    }
                }
            }
        }
    }
    /**
     * Descends the dataset and variable tree and set the grid_type attribute
     * if it is not already set.
     * @throws JDOMException 
     */
    public void setGridType(Element variable) throws JDOMException {
        String grid_type = variable.getAttributeValue("grid_type");
        if ( grid_type == null || grid_type.equals("") ) {
            HashMap<String, String> propMap = getVariableProperties(variable, "database_access");
            if ( propMap.size() > 0 ) {
                variable.setAttribute("grid_type","scattered");
            } else {
                variable.setAttribute("grid_type", "regular");
            }
        }
    }
    private void setID(List categories) {
        for (Iterator catIt = categories.iterator(); catIt.hasNext();) {
            Element category = (Element) catIt.next();
            String ID = category.getAttributeValue("ID");
            if ( ID == null ) {
                String name = category.getAttributeValue("name");
                try {
                    ID = JDOMUtils.MD5Encode(name+String.valueOf(Math.random()));
                } catch (UnsupportedEncodingException e) {
                    ID = String.valueOf(Math.random());
                }
                category.setAttribute("ID", ID);
            }   
            List subcategories = category.getChildren("category");
            if ( subcategories.size() > 0 ) {
                setID(subcategories);
            }
        }
    }
    /**
	 * Helper method to set the output directory if need be
	 * @param dir The path to the directory
	 */
	public void setOutputDir(String dir) {
	   Element output_dir = this.getRootElement().getChild("output_dir");
	   if (output_dir == null) {
	       output_dir = new Element("output_dir");
	       this.getRootElement().addContent(output_dir);
	   }
	   output_dir.setText(dir);
	}
	
	public ArrayList<Category> getTimeSeriesDatasets() throws LASException {
		ArrayList<Category> time_series = new ArrayList<Category>();
		if (hasCategories()) {
			AttributeFilter attribute_filter = new AttributeFilter("category", "group_type", "time_series");
			Iterator catIt = getRootElement().getDescendants(attribute_filter);
			while( catIt.hasNext() ) {
				Element category = (Element) ((Element)catIt.next()).clone();
				Element category_container = new Element("category");
                List attributes = category.getAttributes();
                for (Iterator attrIt = attributes.iterator(); attrIt.hasNext();) {
                    Attribute attr = (Attribute) attrIt.next();
                    category_container.setAttribute(attr.getName(), attr.getValue());
                }
                List filters = category.getChildren("filter");
                for (Iterator filterIt = filters.iterator(); filterIt.hasNext();) {
					Element filter = (Element) filterIt.next();
					List<Dataset> group_list = getDatasets(filter);
					for (Iterator groupIt = group_list.iterator(); groupIt
							.hasNext();) {
						Dataset dataset = (Dataset) groupIt.next();
						category_container.addContent(dataset.getElement());
					}
				}
                time_series.add(new Category(category_container));
			}
		} else {
			AttributeFilter filter = new AttributeFilter("dataset", "group_type", "time_series");
			Iterator dsIt= getRootElement().getDescendants(filter);
			HashMap<String, ArrayList<Dataset>> time_series_groups = new HashMap<String, ArrayList<Dataset>>();
			while (dsIt.hasNext()) {
				Element dataset = (Element)((Element) dsIt.next()).clone();
				String ts_id = dataset.getAttributeValue("group_id");
				ArrayList<Dataset> members = time_series_groups.get(ts_id);
				if ( members == null) {
					members = new ArrayList<Dataset>();
					time_series_groups.put(ts_id, members);
				}
				members.add(new Dataset(dataset));
			}
			for (Iterator gIt = time_series_groups.keySet().iterator(); gIt.hasNext();) {
				String group = (String) gIt.next();
				ArrayList<Dataset> members = time_series_groups.get(group);
				Element category = new Element("category");
				category.setAttribute("id", group);
				for (Iterator memIt = members.iterator(); memIt.hasNext();) {
					Dataset member = (Dataset) memIt.next();
					category.addContent(member.getElement());
					category.setAttribute("name", member.getAttributeValue("group_name"));
					category.setAttribute("ID", member.getAttributeValue("group_id"));
				}
				time_series.add(new Category(category));
			}
		}
		return time_series;
	}
	/**
     * Extract data sets based on a &lt;filter&gt; element from the config, used for group_type="time_series" and group_type="ensemble" categories. 
     * @param filter A category filter element to be used to select variables from the configuration.
     * @return the data set that matches the filter
	 * @throws LASException 
     */
    private List<Dataset> getDatasets(Element filter) throws LASException {
        List<Dataset> container_datasets = new ArrayList<Dataset>();
        String action = filter.getAttributeValue("action");

        String name_contains = filter.getAttributeValue("contains");
        String name_equals = filter.getAttributeValue("equals");
        String tag_contains = filter.getAttributeValue("contains-tag");
        String tag_equals = filter.getAttributeValue("equals-tag");
        
        if ( action.equals("apply-dataset") ) {

            List datasets = getRootElement().getChildren("datasets");
            
            for (Iterator datasetsIt = datasets.iterator(); datasetsIt.hasNext();) {
                Element datasetsE = (Element) datasetsIt.next();
                List memberDatasets = datasetsE.getChildren("dataset");
                for (Iterator memberDSIt = memberDatasets.iterator(); memberDSIt.hasNext();) {
                    Element dataset = (Element) memberDSIt.next();
                    Element container_dataset_element = (Element) dataset.clone();
                    String name = dataset.getAttributeValue("name");
                    String ID = dataset.getAttributeValue("ID");
                    if ( (name_contains != null && name.contains(name_contains)) ||
                         (name_equals != null && name.equals(name_equals)) ||
                         (tag_contains != null && ID.contains(tag_contains)) ||
                         (tag_equals != null && ID.equals(tag_equals)) ) {
                        container_datasets.add(new Dataset(container_dataset_element));
                    }
                }
            }
        } else if ( action.equals("apply-variable") ) {
        	throw new LASException("Should not try to filter groups using an 'apply-variable' filter.");
        }
        return container_datasets;
    }
	public boolean hasCategories() {
		List tops = getRootElement().getChildren("las_categories");
		if ( tops != null && tops.size() > 0 ) {
			return true;
		} else {
			return false;
		}
	}
}
begin:vcard
fn:Bastian Kern
n:Kern;Bastian
org:Max Planck Institute for Chemistry;Atmospheric Chemistry Department
adr:;;Johann-Joachim Becher Weg 27;Mainz;;55128;Germany
email;internet:bastian.kern@xxxxxxx
title:Dipl.-Met.
tel;work:+49 6131 305452
tel;fax:+49 6131 305436
url:www.atmosphere.mpg.de
version:2.1
end:vcard


[Thread Prev][Thread Next][Index]


Contact Us
Dept of Commerce / NOAA / OAR / PMEL / TMAP

Privacy Policy | Disclaimer | Accessibility Statement