All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ucar.nc2.dataset.CoordSysBuilder Maven / Gradle / Ivy

Go to download

The NetCDF-Java Library is a Java interface to NetCDF files, as well as to many other types of scientific data formats.

The newest version!
/*
 * Copyright 1998-2014 University Corporation for Atmospheric Research/Unidata
 *
 *   Portions of this software were developed by the Unidata Program at the
 *   University Corporation for Atmospheric Research.
 *
 *   Access and use of this software shall impose the following obligations
 *   and understandings on the user. The user is granted the right, without
 *   any fee or cost, to use, copy, modify, alter, enhance and distribute
 *   this software, and any derivative works thereof, and its supporting
 *   documentation for any purpose whatsoever, provided that this entire
 *   notice appears in all copies of the software, derivative works and
 *   supporting documentation.  Further, UCAR requests that the user credit
 *   UCAR/Unidata in any publications that result from the use of this
 *   software or in any product that includes this software. The names UCAR
 *   and/or Unidata, however, may not be used in any advertising or publicity
 *   to endorse or promote any products or commercial entity unless specific
 *   written permission is obtained from UCAR/Unidata. The user also
 *   understands that UCAR/Unidata is not obligated to provide the user with
 *   any support, consulting, training or assistance of any kind with regard
 *   to the use, operation and performance of this software nor to provide
 *   the user with any updates, revisions, new versions or "bug fixes."
 *
 *   THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR
 *   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *   DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
 *   INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 *   FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 *   NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 *   WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
 */
package ucar.nc2.dataset;

import ucar.nc2.*;
import ucar.nc2.constants.CDM;
import ucar.nc2.constants.CF;
import ucar.nc2.constants._Coordinate;
import ucar.nc2.constants.AxisType;
import ucar.nc2.util.CancelTask;
import ucar.nc2.ncml.NcMLReader;
import ucar.nc2.dataset.conv.*;
import ucar.ma2.DataType;

import java.lang.reflect.Method;
import java.io.IOException;
import java.util.*;

/**
 * Abstract class for implementing Convention-specific parsing of netCDF files.
 * 

* You can use an NcML file alone (use registerNcML()) if file uses a convention attribute. * If not, you must implement a class that implements isMine() to identify your files, and * call wrapNcML in the augmentDataset method (see eg ATDRadarConvention class). *

*

* Subclasses Info: *

 * // identify which variables are coordinate axes
 * // default: 1) coordinate variables 2) variables with _coordinateAxisType attribute 3) variables listed
 * // in a coordinates attribute on another variable.
 * findCoordinateAxes( ncDataset);
 * 

* // identify which variables are used to describe coordinate system * findCoordinateSystems( ncDataset); * // identify which variables are used to describe coordinate transforms * findCoordinateTransforms( ncDataset); * // turn Variables into CoordinateAxis objects * makeCoordinateAxes( ncDataset); * // make Coordinate Systems for all Coordinate Systems Variables * makeCoordinateSystems( ncDataset); *

* // Assign explicit CoordinateSystem objects to variables * assignExplicitCoordinateSystems( ncDataset); * makeCoordinateSystemsImplicit( ncDataset); * if (useMaximalCoordSys) * makeCoordinateSystemsMaximal( ncDataset); *

* makeCoordinateTransforms( ncDataset); * assignCoordinateTransforms( ncDataset); *

* * @author caron */ /* Implementation notes: Generally, subclasses should add the _Coordinate conventions, see http://www.unidata.ucar.edu/software/netcdf-java/CoordinateAttributes.html Then let this class do the rest of the work. How to add Coordinate Transforms: A. 1) create a dummy Variable called the Coordinate Transform Variable. This Coordinate Transform variable always has a name that identifies the transform, and any attributes needed for the transformation. 2) explicitly point to it by adding a _CoordinateTransform attribute to a Coordinate System Variable _CoordinateTransforms = "LambertProjection HybridSigmaVerticalTransform" B. You could explicitly add it by overriding assignCoordinateTransforms() */ public class CoordSysBuilder implements CoordSysBuilderIF { static public final String resourcesDir = "resources/nj22/coords/"; // resource path static protected org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CoordSysBuilder.class); static private List conventionList = new ArrayList<>(); static private Map ncmlHash = new HashMap<>(); static private boolean useMaximalCoordSys = true; static private boolean userMode = false; /** * Allow plug-ins to determine if it owns a file based on the file's Convention attribute. */ static public interface ConventionNameOk { /** * Do you own this file? * * @param convName name in the file * @param wantName name passed into registry * @return true if you own this file */ boolean isMatch(String convName, String wantName); } // search in the order added static { // wont get loaded unless explicitly called // ours registerConvention(_Coordinate.Convention, CoordSysBuilder.class, null); registerConvention("CF-1.", CF1Convention.class, new ConventionNameOk() { public boolean isMatch(String convName, String wantName) { if (convName.startsWith(wantName)) return true; List names = breakupConventionNames(convName); for (String name : names) if (name.startsWith(wantName)) return true; return false; } }); registerConvention("CDM-Extended-CF", CF1Convention.class); registerConvention("COARDS", COARDSConvention.class, null); registerConvention("NCAR-CSM", CSMConvention.class, null); registerConvention("Unidata Observation Dataset v1.0", UnidataObsConvention.class, null); registerConvention("GDV", GDVConvention.class, null); registerConvention("ATDRadar", ATDRadarConvention.class, null); registerConvention("CEDRICRadar", CEDRICRadarConvention.class, null); registerConvention("Zebra", ZebraConvention.class, null); registerConvention("GIEF/GIEF-F", GIEFConvention.class, null); registerConvention("IRIDL", IridlConvention.class, null); // the uglies registerConvention("NUWG", NUWGConvention.class, null); registerConvention("AWIPS", AWIPSConvention.class, null); registerConvention("AWIPS-Sat", AWIPSsatConvention.class, null); registerConvention("WRF", WRFConvention.class, null); //registerConvention("M3IOVGGrid", M3IOVGGridConvention.class, null); registerConvention("M3IO", M3IOConvention.class, null); registerConvention("IFPS", IFPSConvention.class, null); registerConvention("ARPS/ADAS", ADASConvention.class, null); // point data registerConvention("MADIS surface observations, v1.0", MADISStation.class, null); registerConvention("epic-insitu-1.0", EpicInsitu.class, null); registerConvention("NCAR-RAF/nimbus", Nimbus.class, null); registerConvention("Cosmic1Convention", Cosmic1Convention.class, null); registerConvention("Jason2Convention", Jason2Convention.class, null); registerConvention("Suomi", Suomi.class, null); // new registerConvention("NSSL National Reflectivity Mosaic", NsslRadarMosaicConvention.class, null); registerConvention("FslWindProfiler", FslWindProfiler.class, null); registerConvention("ModisSatellite", ModisSatellite.class, null); registerConvention("AvhrrSatellite", AvhrrConvention.class, null); registerConvention("NPP/NPOESS", NppConvention.class, null); registerConvention("HDF5-EOS-OMI", HdfEosOmiConvention.class, null); registerConvention("HDF4-EOS-MODIS", HdfEosModisConvention.class, null); // further calls to registerConvention are by the user userMode = true; } /** * Register an NcML file that implements a Convention by wrappping the dataset in the NcML. * It is then processed by CoordSysBuilder, using the _Coordinate attributes. * * @param conventionName name of Convention, must be in the "Conventions" global attribute. * @param ncmlLocation location of NcML file, may be local file or URL. * @see ucar.nc2.ncml.NcMLReader#wrapNcML */ static public void registerNcML(String conventionName, String ncmlLocation) { ncmlHash.put(conventionName, ncmlLocation); } /** * Register a class that implements a Convention. Will match (ignoring case) the COnvention name. * * @param conventionName name of Convention. * This name will be used to look in the "Conventions" global attribute. * Otherwise, you must implement the isMine() static method. * @param c implementation of CoordSysBuilderIF that parses those kinds of netcdf files. */ static public void registerConvention(String conventionName, Class c) { registerConvention(conventionName, c, null); } /** * Register a class that implements a Convention. * * @param conventionName name of Convention. * This name will be used to look in the "Conventions" global attribute. * Otherwise, you must implement the isMine() static method. * @param match pass in your own matcher. if null, equalsIgnoreCase() will be used. * @param c implementation of CoordSysBuilderIF that parses those kinds of netcdf files. */ static public void registerConvention(String conventionName, Class c, ConventionNameOk match) { if (!(CoordSysBuilderIF.class.isAssignableFrom(c))) throw new IllegalArgumentException("CoordSysBuilderIF Class " + c.getName() + " must implement CoordSysBuilderIF"); // fail fast - check newInstance works try { c.newInstance(); } catch (InstantiationException e) { throw new IllegalArgumentException("CoordSysBuilderIF Class " + c.getName() + " cannot instantiate, probably need default Constructor"); } catch (IllegalAccessException e) { throw new IllegalArgumentException("CoordSysBuilderIF Class " + c.getName() + " is not accessible"); } // user stuff gets put at top if (userMode) conventionList.add(0, new Convention(conventionName, c, match)); else conventionList.add(new Convention(conventionName, c, match)); // user stuff will override here // conventionHash.put(conventionName, c); } static private Class matchConvention(String convName) { for (Convention c : conventionList) { if ((c.match == null) && c.convName.equalsIgnoreCase(convName)) return c.convClass; if ((c.match != null) && c.match.isMatch(convName, c.convName)) return c.convClass; } return null; } /** * Register a class that implements a Convention. * * @param conventionName name of Convention. * This name will be used to look in the "Conventions" global attribute. * Otherwise, you must implement the isMine() static method. * @param className name of class that implements CoordSysBuilderIF. * @throws ClassNotFoundException if class could not be loaded */ static public void registerConvention(String conventionName, String className) throws ClassNotFoundException { Class c = Class.forName(className); registerConvention(conventionName, c, null); } /** * If true, assign implicit CoordinateSystem objects to variables that dont have one yet. * Default value is false. * * @param b true if if you want to guess at Coordinate Systems * @see #makeCoordinateSystemsMaximal */ static public void setUseMaximalCoordSys(boolean b) { useMaximalCoordSys = b; } /** * Get whether to make records into Structures. * * @return whether to make records into Structures. */ static public boolean getUseMaximalCoordSys() { return useMaximalCoordSys; } /** * Breakup list of Convention names in teh COnvention attribute in CF compliant way. * @param convAttValue original value of COnvention attribute * @return list of Convention names */ static public List breakupConventionNames(String convAttValue) { List names = new ArrayList<>(); if ((convAttValue.indexOf(',') > 0) || (convAttValue.indexOf(';') > 0)) { StringTokenizer stoke = new StringTokenizer(convAttValue, ",;"); while (stoke.hasMoreTokens()) { String name = stoke.nextToken(); names.add(name.trim()); } } else if ((convAttValue.indexOf('/') > 0)) { StringTokenizer stoke = new StringTokenizer(convAttValue, "/"); while (stoke.hasMoreTokens()) { String name = stoke.nextToken(); names.add(name.trim()); } } else { StringTokenizer stoke = new StringTokenizer(convAttValue, " "); while (stoke.hasMoreTokens()) { String name = stoke.nextToken(); names.add(name.trim()); } } return names; } /** * Get a CoordSysBuilder whose job it is to add Coordinate information to a NetcdfDataset. * * @param ds the NetcdfDataset to modify * @param cancelTask allow user to bail out. * @return the builder used * @throws java.io.IOException on io error * @see ucar.nc2.dataset.NetcdfDataset#enhance */ static public CoordSysBuilderIF factory(NetcdfDataset ds, CancelTask cancelTask) throws IOException { // look for the Conventions attribute String convName = ds.findAttValueIgnoreCase(null, CDM.CONVENTIONS, null); if (convName == null) convName = ds.findAttValueIgnoreCase(null, "Convention", null); // common mistake Convention instead of Conventions if (convName != null) convName = convName.trim(); // look for ncml first if (convName != null) { String convNcML = ncmlHash.get(convName); if (convNcML != null) { CoordSysBuilder csb = new CoordSysBuilder(); NcMLReader.wrapNcML(ds, convNcML, cancelTask); return csb; } } // look for registered conventions using convention name Class convClass = null; if (convName != null) { convClass = matchConvention(convName); // now look for comma or semicolon or / delimited list if (convClass == null) { List names = breakupConventionNames(convName); if (names.size() > 0) { // search the registered conventions, in order for (Convention conv : conventionList) { for (String name : names) { if (name.equalsIgnoreCase(conv.convName)) { convClass = conv.convClass; convName = name; } } if (convClass != null) break; } } } } // look for ones that dont use Convention attribute, in order added. // call static isMine() using reflection. if (convClass == null) { for (Convention conv : conventionList) { Class c = conv.convClass; Method m; try { m = c.getMethod("isMine", new Class[]{NetcdfFile.class}); // LOOK cant we test if method exists ? } catch (NoSuchMethodException ex) { continue; } try { Boolean result = (Boolean) m.invoke(null, ds); if (result) { convClass = c; break; } } catch (Exception ex) { log.error("ERROR: Class " + c.getName() + " Exception invoking isMine method\n" + ex); } } } // use service loader mechanism // call static isMine() using reflection. CoordSysBuilderIF builder = null; if (convClass == null) { for (CoordSysBuilderIF csb : ServiceLoader.load(CoordSysBuilderIF.class)) { Class c = csb.getClass(); Method m; try { m = c.getMethod("isMine", new Class[]{NetcdfFile.class}); } catch (NoSuchMethodException ex) { continue; } try { Boolean result = (Boolean) m.invoke(null, new Object[]{ds}); if (result) { builder = csb; convClass = c; break; } } catch (Exception ex) { log.error("ERROR: Class " + c.getName() + " Exception invoking isMine method%n" + ex); } } } // if no convention class found, use the default if (convClass == null) convClass = DefaultConvention.class; if (builder == null) { // get an instance of the class try { builder = (CoordSysBuilderIF) convClass.newInstance(); } catch (Exception e) { log.error("failed on CoordSysBuilderIF for " + convClass.getName(), e); return null; } } if (convName == null) builder.addUserAdvice("No 'Conventions' global attribute."); else if (convClass == DefaultConvention.class) builder.addUserAdvice("No CoordSysBuilder is defined for Conventions= '"+convName+"'\n"); else builder.setConventionUsed(convClass.getName()); ds.addAttribute(null, new Attribute(_Coordinate._CoordSysBuilder, convClass.getName())); return builder; } static private class Convention { String convName; Class convClass; ConventionNameOk match; Convention(String convName, Class convClass, ConventionNameOk match) { this.convName = convName; this.convClass = convClass; this.match = match; } } //////////////////////////////////////////////////////////////////////////////////////// protected String conventionName = _Coordinate.Convention; // default name of Convention, override in subclass protected List varList = new ArrayList<>(); // varProcess objects protected Map> coordVarMap = new HashMap<>(); protected Formatter parseInfo = new Formatter(); protected Formatter userAdvice = new Formatter(); protected boolean debug = false; @Override public void setConventionUsed(String convName) { this.conventionName = convName; } @Override public String getConventionUsed() { return conventionName; } @Override public void addUserAdvice(String advice) { userAdvice.format("%s", advice); } @Override public String getParseInfo() { return parseInfo.toString(); } @Override public String getUserAdvice() { return userAdvice.toString(); } //////////////////////////////////////////////////////////////////////////////////////////////////// // subclasses can override any of these routines @Override public void augmentDataset(NetcdfDataset ncDataset, CancelTask cancelTask) throws IOException { } /** * Identify what kind of AxisType the named variable is. * Only called for variables already identified as Coordinate Axes. * Default null - subclasses can override. * * @param ncDataset for this dataset * @param v a variable alreaddy identified as a Coodinate Axis * @return AxisType or null if unknown. */ protected AxisType getAxisType(NetcdfDataset ncDataset, VariableEnhanced v) { return null; } /** * Heres where the work is to identify coordinate axes and coordinate systems. * * @param ncDataset modify this dataset */ @Override public void buildCoordinateSystems(NetcdfDataset ncDataset) { // put status info into parseInfo that can be shown to someone trying to debug this process parseInfo.format("Parsing with Convention = %s%n", conventionName); // Bookkeeping info for each variable is kept in the VarProcess inner class addVariables(ncDataset, ncDataset.getVariables(), varList); // identify which variables are coordinate axes findCoordinateAxes(ncDataset); // identify which variables are used to describe coordinate system findCoordinateSystems(ncDataset); // identify which variables are used to describe coordinate transforms findCoordinateTransforms(ncDataset); // turn Variables into CoordinateAxis objects makeCoordinateAxes(ncDataset); // make Coordinate Systems for all Coordinate Systems Variables makeCoordinateSystems(ncDataset); // assign explicit CoordinateSystem objects to variables assignCoordinateSystemsExplicit(ncDataset); // assign implicit CoordinateSystem objects to variables makeCoordinateSystemsImplicit(ncDataset); // optionally assign implicit CoordinateSystem objects to variables that dont have one yet if (useMaximalCoordSys) makeCoordinateSystemsMaximal(ncDataset); // make Coordinate Transforms makeCoordinateTransforms(ncDataset); // assign Coordinate Transforms assignCoordinateTransforms(ncDataset); if (debug) System.out.println("parseInfo = \n" + parseInfo.toString()); } private void addVariables(NetcdfDataset ncDataset, List varList, List varProcessList) { for (Variable v : varList) { varProcessList.add(new VarProcess(ncDataset, v)); if (v instanceof Structure) { List nested = ((Structure) v).getVariables(); addVariables(ncDataset, nested, varProcessList); } } } /** * Identify coordinate axes, set VarProcess.isCoordinateAxis = true. * Default is to look for those referenced by _CoordinateAxes attribute. * Note coordinate variables are already identified. * * @param ncDataset why */ protected void findCoordinateAxes(NetcdfDataset ncDataset) { for (VarProcess vp : varList) { if (vp.coordAxes != null) findCoordinateAxes(vp, vp.coordAxes); if (vp.coordinates != null) findCoordinateAxes(vp, vp.coordinates); } } private void findCoordinateAxes(VarProcess vp, String coordinates) { StringTokenizer stoker = new StringTokenizer(coordinates); while (stoker.hasMoreTokens()) { String vname = stoker.nextToken(); VarProcess ap = findVarProcess(vname, vp); if (ap == null) { Group g = vp.v.getParentGroup(); Variable v = g.findVariableOrInParent(vname); if (v != null) ap = findVarProcess(v.getFullName(), vp); else { parseInfo.format("***Cant find coordAxis %s referenced from var= %s%n", vname, vp.v.getFullName()); userAdvice.format("***Cant find coordAxis %s referenced from var= %s%n", vname, vp.v.getFullName()); } } if (ap != null) { if (!ap.isCoordinateAxis) parseInfo.format(" CoordinateAxis = %s added; referenced from var= %s%n", vname, vp.v.getFullName()); ap.isCoordinateAxis = true; } else { parseInfo.format("***Cant find coordAxis %s referenced from var= %s%n", vname, vp.v.getFullName()); userAdvice.format("***Cant find coordAxis %s referenced from var= %s%n", vname, vp.v.getFullName()); } } } /** * Identify coordinate systems, set VarProcess.isCoordinateSystem = true. * Default is to look for those referenced by _CoordinateSystems attribute. * * @param ncDataset why */ protected void findCoordinateSystems(NetcdfDataset ncDataset) { for (VarProcess vp : varList) { if (vp.coordSys != null) { StringTokenizer stoker = new StringTokenizer(vp.coordSys); while (stoker.hasMoreTokens()) { String vname = stoker.nextToken(); VarProcess ap = findVarProcess(vname, vp); if (ap != null) { if (!ap.isCoordinateSystem) parseInfo.format(" CoordinateSystem = %s added; referenced from var= %s%n", vname, vp.v.getFullName()); ap.isCoordinateSystem = true; } else { parseInfo.format("***Cant find coordSystem %s referenced from var= %s%n", vname, vp.v.getFullName()); userAdvice.format("***Cant find coordSystem %s referenced from var= %s%n", vname, vp.v.getFullName()); } } } } } /** * Identify coordinate transforms, set VarProcess.isCoordinateTransform = true. * Default is to look for those referenced by _CoordinateTransforms attribute * ( or has a _CoordinateTransformType attribute, done in VarProcess constructor) * * @param ncDataset why */ protected void findCoordinateTransforms(NetcdfDataset ncDataset) { for (VarProcess vp : varList) { if (vp.coordTransforms != null) { StringTokenizer stoker = new StringTokenizer(vp.coordTransforms); while (stoker.hasMoreTokens()) { String vname = stoker.nextToken(); VarProcess ap = findVarProcess(vname, vp); if (ap != null) { if (!ap.isCoordinateTransform) parseInfo.format(" CoordinateTransform = %s added; referenced from var= %s%n", vname, vp.v.getFullName()); ap.isCoordinateTransform = true; } else { parseInfo.format("***Cant find CoordinateTransform %s referenced from var= %s%n", vname, vp.v.getFullName()); userAdvice.format("***Cant find CoordinateTransform %s referenced from var= %s%n", vname, vp.v.getFullName()); } } } } } /** * Take previously identified Coordinate Axis and Coordinate Variables and make them into a * CoordinateAxis. Uses the getAxisType() method to figure out the type, if not already set. * * @param ncDataset containing dataset */ protected void makeCoordinateAxes(NetcdfDataset ncDataset) { for (VarProcess vp : varList) { if (vp.isCoordinateAxis || vp.isCoordinateVariable) { if (vp.axisType == null) vp.axisType = getAxisType(ncDataset, (VariableEnhanced) vp.v); if (vp.axisType == null) { userAdvice.format("Coordinate Axis %s does not have an assigned AxisType%n", vp.v.getFullName()); } vp.makeIntoCoordinateAxis(); } } } /** * Take all previously identified Coordinate Systems and create a * CoordinateSystem object. * * @param ncDataset why */ protected void makeCoordinateSystems(NetcdfDataset ncDataset) { for (VarProcess vp : varList) { if (vp.isCoordinateSystem) { vp.makeCoordinateSystem(); } } } /** * Assign explicit CoordinateSystem objects to variables. * * @param ncDataset why */ protected void assignCoordinateSystemsExplicit(NetcdfDataset ncDataset) { // look for explicit references to coord sys variables for (VarProcess vp : varList) { if (vp.coordSys != null && !vp.isCoordinateTransform) { StringTokenizer stoker = new StringTokenizer(vp.coordSys); while (stoker.hasMoreTokens()) { String vname = stoker.nextToken(); VarProcess ap = findVarProcess(vname, vp); if (ap == null) { parseInfo.format("***Cant find Coordinate System variable %s referenced from var= %s%n", vname, vp.v.getFullName()); userAdvice.format("***Cant find Coordinate System variable %s referenced from var= %s%n", vname, vp.v.getFullName()); continue; } if (ap.cs == null) { parseInfo.format("***Not a Coordinate System variable %s referenced from var= %s%n", vname, vp.v.getFullName()); userAdvice.format("***Not a Coordinate System variable %s referenced from var= %s%n", vname, vp.v.getFullName()); continue; } VariableEnhanced ve = (VariableEnhanced) vp.v; ve.addCoordinateSystem(ap.cs); } } } // look for explicit references from coord sys variables to data variables for (VarProcess csVar : varList) { if (!csVar.isCoordinateSystem || (csVar.coordSysFor == null)) continue; // get list of dimensions from '_CoordinateSystemFor' attribute List dimList = new ArrayList<>(6); StringTokenizer stoker = new StringTokenizer(csVar.coordSysFor); while (stoker.hasMoreTokens()) { String dname = stoker.nextToken(); Dimension dim = ncDataset.getRootGroup().findDimension(dname); if (dim == null) { parseInfo.format("***Cant find Dimension %s referenced from CoordSys var= %s%n", dname, csVar.v.getFullName()); userAdvice.format("***Cant find Dimension %s referenced from CoordSys var= %s%n", dname, csVar.v.getFullName()); } else dimList.add(dim); } // look for vars with those dimensions for (VarProcess vp : varList) { if (!vp.hasCoordinateSystem() && vp.isData() && (csVar.cs != null)) { VariableEnhanced ve = (VariableEnhanced) vp.v; if (CoordinateSystem.isSubset(dimList, vp.v.getDimensionsAll()) && CoordinateSystem.isSubset(vp.v.getDimensionsAll(), dimList)) ve.addCoordinateSystem(csVar.cs); } } } // look for explicit listings of coordinate axes for (VarProcess vp : varList) { VariableEnhanced ve = (VariableEnhanced) vp.v; if (!vp.hasCoordinateSystem() && (vp.coordAxes != null) && vp.isData()) { List dataAxesList = getAxes(vp, vp.coordAxes, vp.v.getFullName()); if (dataAxesList.size() > 1) { String coordSysName = CoordinateSystem.makeName(dataAxesList); CoordinateSystem cs = ncDataset.findCoordinateSystem(coordSysName); if (cs != null) { ve.addCoordinateSystem(cs); parseInfo.format(" assigned explicit CoordSystem '%s' for var= %s%n", cs.getName(), vp.v.getFullName()); } else { CoordinateSystem csnew = new CoordinateSystem(ncDataset, dataAxesList, null); ve.addCoordinateSystem(csnew); ncDataset.addCoordinateSystem(csnew); parseInfo.format(" created explicit CoordSystem '%s' for var= %s%n", csnew.getName(), vp.v.getFullName()); } } } } } private List getAxes(VarProcess vp, String names, String varName) { List axesList = new ArrayList<>(); StringTokenizer stoker = new StringTokenizer(names); while (stoker.hasMoreTokens()) { String vname = stoker.nextToken(); VarProcess ap = findVarProcess(vname, vp); if (ap != null) { CoordinateAxis axis = ap.makeIntoCoordinateAxis(); if (!axesList.contains(axis)) axesList.add(axis); } else { parseInfo.format("***Cant find Coordinate Axis %s referenced from var= %s%n", vname, varName); userAdvice.format("***Cant find Coordinate Axis %s referenced from var= %s%n", vname, varName); } } return axesList; } /** * Make implicit CoordinateSystem objects for variables that dont already have one, by using the * variables' list of coordinate axes, and any coordinateVariables for it. Must be at least 2 axes. * * @param ncDataset why */ protected void makeCoordinateSystemsImplicit(NetcdfDataset ncDataset) { // do largest rank first //List varsSorted = new ArrayList(varList); //Collections.sort(varsSorted, new VarProcessSorter()); for (VarProcess vp : varList) { if (!vp.hasCoordinateSystem() && vp.maybeData()) { List dataAxesList = vp.findCoordinateAxes(true); if (dataAxesList.size() < 2) continue; VariableEnhanced ve = (VariableEnhanced) vp.v; String csName = CoordinateSystem.makeName(dataAxesList); CoordinateSystem cs = ncDataset.findCoordinateSystem(csName); if ((cs != null) && cs.isComplete(vp.v)) { // DANGER WILL ROGERS! ve.addCoordinateSystem(cs); parseInfo.format(" assigned implicit CoordSystem '%s' for var= %s%n", cs.getName(), vp.v.getFullName()); } else { CoordinateSystem csnew = new CoordinateSystem(ncDataset, dataAxesList, null); csnew.setImplicit(true); if (csnew.isComplete(vp.v)) { ve.addCoordinateSystem(csnew); ncDataset.addCoordinateSystem(csnew); parseInfo.format(" created implicit CoordSystem '%s' for var= %s%n", csnew.getName(), vp.v.getFullName()); } } } } } /** * If a variable still doesnt have a coordinate system, use hueristics to try to find one that was probably * forgotten. * Look through all existing CS. create a subset of axes that fits the variable. Choose the one with highest rank. * It must have X,Y or lat,lon. If so, add it. * * @param ncDataset why */ protected void makeCoordinateSystemsMaximal(NetcdfDataset ncDataset) { for (VarProcess vp : varList) { VariableEnhanced ve = (VariableEnhanced) vp.v; CoordinateSystem implicit = null; if (vp.hasCoordinateSystem() || !vp.isData()) continue; CoordinateSystem existing = null; if (ve.getCoordinateSystems().size() == 1) { existing = ve.getCoordinateSystems().get(0); if (!existing.isImplicit()) continue; // cant overrrride explicit if (existing.getRankRange() >= ve.getRank()) continue; // looks ok implicit = existing; } // look through all axes that fit List axisList = new ArrayList<>(); List axes = ncDataset.getCoordinateAxes(); for (CoordinateAxis axis : axes) { if (isCoordinateAxisForVariable(axis, ve)) axisList.add(axis); } if ((existing != null) && (axisList.size() <= existing.getRankRange())) continue; if (axisList.size() < 2) continue; /* ArrayList bestAxisList = null; List csys = ncDataset.getCoordinateSystems(); for (int j = 0; j < csys.size(); j++) { CoordinateSystem cs = (CoordinateSystem) csys.get(j); List axes = cs.getCoordinateAxes(); ArrayList axisList = new ArrayList(); for (int k = 0; k < axes.size(); k++) { CoordinateAxis axis = (CoordinateAxis) axes.get(k); if ( isCoordinateAxisForVariable( axis, ve)) axisList.add( axis); } if (hasXY( axisList)) { if ((bestAxisList == null) || (axisList.size() > bestAxisList.size())) bestAxisList = axisList; } } // make or get a coord sys for it if (bestAxisList != null) { */ String csName = CoordinateSystem.makeName(axisList); CoordinateSystem cs = ncDataset.findCoordinateSystem(csName); if (cs != null) { if (null != implicit) ve.removeCoordinateSystem(implicit); ve.addCoordinateSystem(cs); parseInfo.format(" assigned maximal CoordSystem '%s' for var= %s%n", cs.getName(), ve.getFullName()); } else { CoordinateSystem csnew = new CoordinateSystem(ncDataset, axisList, null); csnew.setImplicit(true); if (null != implicit) ve.removeCoordinateSystem(implicit); ve.addCoordinateSystem(csnew); ncDataset.addCoordinateSystem(csnew); parseInfo.format(" created maximal CoordSystem '%s' for var= %s%n", csnew.getName(), ve.getFullName()); } } } /** * Does this axis "fit" this variable. * True if all of the dimensions in the axis also appear in the variable. * If char variable, last dimension is left out. * * @param axis check if this axis is ok for the given variable * @param v the given variable * @return true if all of the dimensions in the axis also appear in the variable. */ protected boolean isCoordinateAxisForVariable(Variable axis, VariableEnhanced v) { List varDims = v.getDimensionsAll(); /* for (Dimension d : varDims) { if (!d.isShared()) return false; // anon cant have coordinates } */ // LOOK // a CHAR variable must really be a STRING, so leave out the last (string length) dimension int checkDims = axis.getRank(); if (axis.getDataType() == DataType.CHAR) checkDims--; for (int i = 0; i < checkDims; i++) { Dimension axisDim = axis.getDimension(i); if (!varDims.contains(axisDim)) { return false; } } return true; } protected boolean hasXY(List coordAxes) { boolean hasX = false, hasY = false, hasLat = false, hasLon = false; for (CoordinateAxis axis : coordAxes) { AxisType axisType = axis.getAxisType(); if (axisType == AxisType.GeoX) hasX = true; if (axisType == AxisType.GeoY) hasY = true; if (axisType == AxisType.Lat) hasLat = true; if (axisType == AxisType.Lon) hasLon = true; } return (hasLat && hasLon) || (hasX && hasY); } /** * Take all previously identified Coordinate Transforms and create a * CoordinateTransform object by calling CoordTransBuilder.makeCoordinateTransform(). * * @param ncDataset why */ protected void makeCoordinateTransforms(NetcdfDataset ncDataset) { for (VarProcess vp : varList) { if (vp.isCoordinateTransform && vp.ct == null) { vp.ct = CoordTransBuilder.makeCoordinateTransform(vp.ds, vp.v, parseInfo, userAdvice); } } } protected CoordinateTransform makeCoordinateTransform(NetcdfDataset ds, Variable ctv) { return CoordTransBuilder.makeCoordinateTransform(ds, ctv, parseInfo, userAdvice); } /** * Assign CoordinateTransform objects to Coordinate Systems. * * @param ncDataset why */ protected void assignCoordinateTransforms(NetcdfDataset ncDataset) { // look for explicit transform assignments on the coordinate systems for (VarProcess vp : varList) { if (vp.isCoordinateSystem && vp.coordTransforms != null) { StringTokenizer stoker = new StringTokenizer(vp.coordTransforms); while (stoker.hasMoreTokens()) { String vname = stoker.nextToken(); VarProcess ap = findVarProcess(vname, vp); if (ap != null) { if (ap.ct != null) { vp.addCoordinateTransform(ap.ct); parseInfo.format(" assign explicit coordTransform %s to CoordSys= %s%n", ap.ct, vp.cs); } else { parseInfo.format("***Cant find coordTransform in %s referenced from var= %s%n", vname, vp.v.getFullName()); userAdvice.format("***Cant find coordTransform in %s referenced from var= %s%n", vname, vp.v.getFullName()); } } else { parseInfo.format("***Cant find coordTransform variable= %s referenced from var= %s%n", vname, vp.v.getFullName()); userAdvice.format("***Cant find coordTransform variable= %s referenced from var= %s%n", vname, vp.v.getFullName()); } } } } // look for explicit coordSys assignments on the coordinate transforms for (VarProcess vp : varList) { if (vp.isCoordinateTransform && (vp.ct != null) && (vp.coordSys != null)) { StringTokenizer stoker = new StringTokenizer(vp.coordSys); while (stoker.hasMoreTokens()) { String vname = stoker.nextToken(); VarProcess vcs = findVarProcess(vname, vp); if (vcs == null) { parseInfo.format("***Cant find coordSystem variable= %s referenced from var= %s%n", vname, vp.v.getFullName()); userAdvice.format("***Cant find coordSystem variable= %s referenced from var= %s%n", vname, vp.v.getFullName()); } else { vcs.addCoordinateTransform(vp.ct); parseInfo.format("***assign explicit coordTransform %s to CoordSys= %s%n", vp.ct, vp.cs); } } } } // look for coordAxes assignments on the coordinate transforms for (VarProcess vp : varList) { if (vp.isCoordinateTransform && (vp.ct != null) && (vp.coordAxes != null)) { List dataAxesList = vp.findCoordinateAxes(false); if (dataAxesList.size() > 0) { for (CoordinateSystem cs : ncDataset.getCoordinateSystems()) { if (cs.containsAxes(dataAxesList)) { cs.addCoordinateTransform(vp.ct); parseInfo.format("***assign (implicit coordAxes) coordTransform %s to CoordSys= %s%n", vp.ct, cs); } } } } } // look for coordAxisType assignments on the coordinate transforms for (VarProcess vp : varList) { if (vp.isCoordinateTransform && (vp.ct != null) && (vp.coordAxisTypes != null)) { List axisTypesList = new ArrayList<>(); StringTokenizer stoker = new StringTokenizer(vp.coordAxisTypes); while (stoker.hasMoreTokens()) { String name = stoker.nextToken(); AxisType atype; if (null != (atype = AxisType.getType(name))) axisTypesList.add(atype); } if (axisTypesList.size() > 0) { for (CoordinateSystem cs : ncDataset.getCoordinateSystems()) { if (cs.containsAxisTypes(axisTypesList)) { cs.addCoordinateTransform(vp.ct); parseInfo.format("***assign (implicit coordAxisType) coordTransform %s to CoordSys= %s%n", vp.ct, cs); } } } } } } protected VarProcess findVarProcess(String name, VarProcess from) { if (name == null) return null; // compare full name for (VarProcess vp : varList) { if (name.equals(vp.v.getFullName())) return vp; } // prefer ones in the same group if (from != null) { for (VarProcess vp : varList) { if (name.equals(vp.v.getShortName()) && vp.v.getGroup().equals(from.v.getGroup())) return vp; } } // WAEF, use short name for (VarProcess vp : varList) { if (name.equals(vp.v.getShortName())) return vp; } return null; } protected VarProcess findCoordinateAxis(String name) { if (name == null) return null; for (VarProcess vp : varList) { if (name.equals(vp.v.getFullName()) && (vp.isCoordinateVariable || vp.isCoordinateAxis)) return vp; } return null; } // track coordinate variables protected void addCoordinateVariable(Dimension dim, VarProcess vp) { List list = coordVarMap.get(dim); if (list == null) { list = new ArrayList<>(); coordVarMap.put(dim, list); } if (!list.contains(vp)) list.add(vp); } /** * Wrap each variable in the dataset with a VarProcess object. */ public class VarProcess { public NetcdfDataset ds; public Variable v; // coord axes public boolean isCoordinateVariable; public boolean isCoordinateAxis; public AxisType axisType; public String coordAxes, coordSys, coordSysFor, coordVarAlias, positive, coordAxisTypes; public String coordinates;// CF : partial coordAxes public CoordinateAxis axis; // if its made into a Coordinate Axis, this is not null // coord systems public boolean isCoordinateSystem; public String coordTransforms; public CoordinateSystem cs; // coord transform public boolean isCoordinateTransform; public String coordTransformType; public CoordinateTransform ct; /** * Wrap the given variable. * Identify Coordinate Variables. * Process all _Coordinate attributes. * * @param ds dataset container * @param v wrap this Variable */ private VarProcess(NetcdfDataset ds, Variable v) { this.ds = ds; this.v = v; VariableEnhanced ve = (VariableEnhanced) v; isCoordinateVariable = v.isCoordinateVariable(); if (isCoordinateVariable) { v.isCoordinateVariable(); // DEBUG addCoordinateVariable(v.getDimension(0), this); parseInfo.format(" Coordinate Variable added = %s for dimension %s%n", v.getFullName(), v.getDimension(0)); } Attribute att = v.findAttributeIgnoreCase(_Coordinate.AxisType); if (att != null) { String axisName = att.getStringValue(); axisType = AxisType.getType(axisName); isCoordinateAxis = true; parseInfo.format(" Coordinate Axis added = %s type= %s%n", v.getFullName(), axisName); } coordVarAlias = ds.findAttValueIgnoreCase(v, _Coordinate.AliasForDimension, null); if (coordVarAlias != null) { coordVarAlias = coordVarAlias.trim(); if (v.getRank() != 1) { parseInfo.format("**ERROR Coordinate Variable Alias %s has rank %d%n", v.getFullName(), v.getRank()); userAdvice.format("**ERROR Coordinate Variable Alias %s has rank %d%n", v.getFullName(), v.getRank()); } else { Dimension coordDim = v.getGroup().findDimension(coordVarAlias); Dimension vDim = v.getDimension(0); if (!coordDim.equals(vDim)) { parseInfo.format("**ERROR Coordinate Variable Alias %s names wrong dimension %s%n", v.getFullName(), coordVarAlias); userAdvice.format("**ERROR Coordinate Variable Alias %s names wrong dimension %s%n", v.getFullName(), coordVarAlias); } else { isCoordinateAxis = true; addCoordinateVariable(coordDim, this); parseInfo.format(" Coordinate Variable Alias added = %s for dimension= %s%n", v.getFullName(), coordVarAlias); } } } positive = ds.findAttValueIgnoreCase(v, _Coordinate.ZisPositive, null); if (positive == null) positive = ds.findAttValueIgnoreCase(v, CF.POSITIVE, null); else { isCoordinateAxis = true; positive = positive.trim(); parseInfo.format(" Coordinate Axis added(from positive attribute ) = %s for dimension= %s%n", v.getFullName(), coordVarAlias); } coordAxes = ds.findAttValueIgnoreCase(v, _Coordinate.Axes, null); coordSys = ds.findAttValueIgnoreCase(v, _Coordinate.Systems, null); coordSysFor = ds.findAttValueIgnoreCase(v, _Coordinate.SystemFor, null); coordTransforms = ds.findAttValueIgnoreCase(v, _Coordinate.Transforms, null); isCoordinateSystem = (coordTransforms != null) || (coordSysFor != null); coordAxisTypes = ds.findAttValueIgnoreCase(v, _Coordinate.AxisTypes, null); coordTransformType = ds.findAttValueIgnoreCase(v, _Coordinate.TransformType, null); isCoordinateTransform = (coordTransformType != null) || (coordAxisTypes != null); /* WTF? JC 7/15/2010 /* Old GRIB code basically has a bug in it for lat/lon coordinates String latLonCoordSys; :_CoordinateAxes = "time lat lon"; this is a Coordinate System Variable getting misidentified as a data variable, because its doesnt qualify as a CoordinateSystem see http://www.unidata.ucar.edu/software/netcdf-java/reference/CoordinateAttributes.html but it must be a CoordinateSystem because the _CoordinateAxes dont fit the variable. Problem: For some data, some axes are being constructed with anon dimensions, so that isCoordinateAxisForVariable is failing and so the data value is being tagged as a CoordinateSystem. Could insist that all variable fail ?? */ // this is the case of a Coordinate System with no references or coordinate transforms // see /testdata2/grid/grib/grib1/data/NOGAPS-Temp-Regional.grib if (!isCoordinateSystem && !isCoordinateTransform && !isCoordinateAxis && coordAxes != null) { // figure out if data or coordSys Variable StringTokenizer stoker = new StringTokenizer(coordAxes); while (stoker.hasMoreTokens()) { String vname = stoker.nextToken(); Variable axis = ds.findVariable(vname); if ((axis != null) && !isCoordinateAxisForVariable(axis, ve)) isCoordinateSystem = true; } } // */ } // fakeroo public VarProcess(NetcdfDataset ds) { this.ds = ds; } public boolean isData() { return !isCoordinateVariable && !isCoordinateAxis && !isCoordinateSystem && !isCoordinateTransform; } public boolean maybeData() { return !isCoordinateVariable && !isCoordinateSystem && !isCoordinateTransform; } public boolean hasCoordinateSystem() { return ((VariableEnhanced) v).getCoordinateSystems().size() > 0; } public String toString() { return v.getShortName(); } /** * Turn the variable into a coordinate axis, if not already. Add to the dataset, replacing variable if needed. * * @return variable as a coordinate axis */ public CoordinateAxis makeIntoCoordinateAxis() { if (axis != null) return axis; // if not a CoordinateAxis, will turn into one v = axis = ds.addCoordinateAxis((VariableDS) v); if (axisType != null) { axis.setAxisType(axisType); axis.addAttribute(new Attribute(_Coordinate.AxisType, axisType.toString())); if (((axisType == AxisType.Height) || (axisType == AxisType.Pressure) || (axisType == AxisType.GeoZ)) && (positive != null)) { axis.setPositive(positive); axis.addAttribute(new Attribute(_Coordinate.ZisPositive, positive)); } } return axis; } /** * Create a Coordinate System object, using the list of coordinate axis names in the * (required) axes field. */ public void makeCoordinateSystem() { // find referenced coordinate axes List axesList = new ArrayList<>(); if (coordAxes != null) { StringTokenizer stoker = new StringTokenizer(coordAxes); // _CoordinateAxes attribute while (stoker.hasMoreTokens()) { String vname = stoker.nextToken(); VarProcess ap = findVarProcess(vname, this); if (ap != null) { CoordinateAxis axis = ap.makeIntoCoordinateAxis(); if (!axesList.contains(axis)) axesList.add(axis); } else { parseInfo.format(" Cant find axes %s for Coordinate System %s%n", vname, v.getFullName()); userAdvice.format(" Cant find axes %s for Coordinate System %s%n", vname, v.getFullName()); } } } if (axesList.size() == 0) { parseInfo.format(" No axes found for Coordinate System %s%n", v.getFullName()); userAdvice.format(" No axes found for Coordinate System %s%n", v.getFullName()); return; } // create the coordinate system cs = new CoordinateSystem(ds, axesList, null); ds.addCoordinateSystem(cs); parseInfo.format(" Made Coordinate System %s for ", cs.getName()); v.getNameAndDimensions(parseInfo, true, false); parseInfo.format(" from %s%n", coordAxes); } /** * Create a list of coordinate axes for this data variable. * Use the list of names in axes or coordinates field. * * @param addCoordVariables if true, add any coordinate variables that are missing. * @return list of coordinate axes for this data variable. */ public List findCoordinateAxes(boolean addCoordVariables) { List axesList = new ArrayList<>(); if (coordAxes != null) { // explicit axes StringTokenizer stoker = new StringTokenizer(coordAxes); while (stoker.hasMoreTokens()) { String vname = stoker.nextToken(); VarProcess ap = findVarProcess(vname, this); if (ap != null) { CoordinateAxis axis = ap.makeIntoCoordinateAxis(); if (!axesList.contains(axis)) axesList.add(axis); } } } else if (coordinates != null) { // CF partial listing of axes StringTokenizer stoker = new StringTokenizer(coordinates); while (stoker.hasMoreTokens()) { String vname = stoker.nextToken(); VarProcess ap = findVarProcess(vname, this); if (ap != null) { CoordinateAxis axis = ap.makeIntoCoordinateAxis(); // LOOK check if its legal if (!axesList.contains(axis)) axesList.add(axis); } } } if (addCoordVariables) { for (Dimension d : v.getDimensions()) { List coordVars = coordVarMap.get(d); if (coordVars == null) continue; for (VarProcess vp : coordVars) { CoordinateAxis axis = vp.makeIntoCoordinateAxis(); if (!axesList.contains(axis)) axesList.add(axis); } } } return axesList; } void addCoordinateTransform(CoordinateTransform ct) { if (cs == null) { parseInfo.format(" %s: no CoordinateSystem for CoordinateTransformVariable: %s%n", v.getFullName(), ct.getName()); return; } cs.addCoordinateTransform(ct); } } protected VariableDS makeCoordinateTransformVariable(NetcdfDataset ds, CoordinateTransform ct) { VariableDS v = CoordTransBuilder.makeDummyTransformVariable(ds, ct); parseInfo.format(" made CoordinateTransformVariable: %s%n", ct.getName()); return v; } /** * @deprecated use CoordTransBuilder.makeDummyTransformVariable */ static public VariableDS makeDummyTransformVariable(NetcdfDataset ds, CoordinateTransform ct) { return CoordTransBuilder.makeDummyTransformVariable(ds, ct); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy