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

ucar.nc2.dataset.conv.DefaultConvention 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.conv;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ucar.ma2.Array;
import ucar.ma2.ArrayObject;
import ucar.ma2.DataType;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.*;
import ucar.nc2.constants.CF;
import ucar.nc2.dataset.*;
import ucar.nc2.time.CalendarDate;
import ucar.nc2.util.CancelTask;
import ucar.nc2.constants.AxisType;
import ucar.nc2.constants._Coordinate;
import ucar.nc2.units.SimpleUnit;
import ucar.unidata.geoloc.ProjectionImpl;
import ucar.unidata.geoloc.projection.LambertConformal;
import ucar.unidata.geoloc.projection.TransverseMercator;
import ucar.unidata.geoloc.projection.Stereographic;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.StringTokenizer;

/**
 * Default Coordinate Conventions.
 * Split from GDV.
 * @author caron
 * @since Dec 17, 2008
 */
public class DefaultConvention extends CoordSysBuilder {
  static private final Logger logger = LoggerFactory.getLogger(DefaultConvention.class);

    protected ProjectionCT projCT = null;

    public DefaultConvention() {
      this.conventionName = "Default";
    }

    public void augmentDataset(NetcdfDataset ds, CancelTask cancelTask) {
      projCT = makeProjectionCT(ds);
      if (projCT != null) {
        VariableDS v = makeCoordinateTransformVariable(ds, projCT);
        ds.addVariable(null, v);

        String xname = findCoordinateName(ds, AxisType.GeoX);
        String yname = findCoordinateName(ds, AxisType.GeoY);
        if (xname != null && yname != null)
          v.addAttribute(new Attribute(_Coordinate.Axes, xname + " " + yname));
      }
      ds.finish();
    }

    @Override
    protected void findCoordinateAxes(NetcdfDataset ds) {

      for (VarProcess vp : varList) {
        if (vp.isCoordinateVariable) continue;

        Variable ncvar = vp.v;
        if (!(ncvar instanceof VariableDS)) continue; // cant be a structure

        String dimName = findAlias(ds, ncvar);
        if (dimName.length() == 0) // none
          continue;
        Dimension dim = ds.findDimension(dimName);
        if (null != dim) {
          vp.isCoordinateAxis = true;
          parseInfo.format(" Coordinate Axis added (alias) = %s for dimension %s%n", vp.v.getFullName(), dimName);
        }
      }

      // coordinates is an alias for _CoordinateAxes
      for (VarProcess vp : varList) {
        if (vp.coordAxes == null) { // dont override if already set
          String coordsString = ds.findAttValueIgnoreCase(vp.v, CF.COORDINATES, null);
          if (coordsString != null) {
            vp.coordinates = coordsString;
          }
        }
      }

      super.findCoordinateAxes(ds);

      /////////////////////////
      // now we start forcing
      HashMap map = new HashMap<>();

      // find existing axes, so we dont duplicate
      for (VarProcess vp : varList) {
        if (vp.isCoordinateAxis) {
          AxisType atype = getAxisType(ds, (VariableEnhanced) vp.v);
          if (atype != null)
            map.put(atype, vp);
        }
      }

      // look for time axes based on units
      if (map.get(AxisType.Time) == null) {
        for (VarProcess vp : varList) {
          Variable ncvar = vp.v;
          if (!(ncvar instanceof VariableDS)) continue; // cant be a structure
          String unit = ncvar.getUnitsString();

          if (unit != null && SimpleUnit.isDateUnit(unit)) {
            vp.isCoordinateAxis = true;
            map.put(AxisType.Time, vp);
            parseInfo.format(" Time Coordinate Axis added (unit) = %s from unit %s%n", vp.v.getFullName(), unit);
            //break; // allow multiple time coords
          }
        }
      }

      // look for missing axes by using name hueristics
      for (VarProcess vp : varList) {
        if (vp.isCoordinateVariable) continue;
        Variable ncvar = vp.v;
        if (!(ncvar instanceof VariableDS)) continue; // cant be a structure

        AxisType atype = getAxisType(ds, (VariableEnhanced) vp.v);
        if (atype != null) {
          if (map.get(atype) == null) {
            vp.isCoordinateAxis = true;
            parseInfo.format(" Coordinate Axis added (Default forced) = %s for axis %s%n", vp.v.getFullName(), atype);
            map.put(atype, vp);
          }
        }
      }
    }

    /**
     * look for aliases.
     *
     * @param ds       containing dataset
     * @param axisType look for this axis type
     * @return name of axis of that type
     */
    private String findCoordinateName(NetcdfDataset ds, AxisType axisType) {
      List vlist = ds.getVariables();
      for (Variable aVlist : vlist) {
        VariableEnhanced ve = (VariableEnhanced) aVlist;
        if (axisType == getAxisType(ds, ve)) {
          return ve.getFullName();
        }
      }
      return null;
    }

    protected void makeCoordinateTransforms(NetcdfDataset ds) {
      if (projCT != null) {
        VarProcess vp = findVarProcess(projCT.getName(), null);
        if (vp != null)
          vp.ct = projCT;
      }
      super.makeCoordinateTransforms(ds);
    }

    protected AxisType getAxisType(NetcdfDataset ds, VariableEnhanced ve) {
      AxisType result = getAxisTypeCoards(ds, ve);
      if (result != null) return result;

      Variable v = (Variable) ve;
      String vname = v.getShortName();
      if (vname == null)
        return null;
      String unit = v.getUnitsString();
      if (unit == null) unit = "";
      String desc = v.getDescription();
      if (desc == null) desc = "";

      if (vname.equalsIgnoreCase("x") || findAlias(ds, v).equalsIgnoreCase("x"))
        return AxisType.GeoX;

      if (vname.equalsIgnoreCase("lon") || vname.equalsIgnoreCase("longitude") || findAlias(ds, v).equalsIgnoreCase("lon"))
        return AxisType.Lon;

      if (vname.equalsIgnoreCase("y") || findAlias(ds, v).equalsIgnoreCase("y"))
        return AxisType.GeoY;

      if (vname.equalsIgnoreCase("lat") || vname.equalsIgnoreCase("latitude") || findAlias(ds, v).equalsIgnoreCase("lat"))
        return AxisType.Lat;

      if (vname.equalsIgnoreCase("lev") || findAlias(ds, v).equalsIgnoreCase("lev") ||
              (vname.equalsIgnoreCase("level") || findAlias(ds, v).equalsIgnoreCase("level")))
        return AxisType.GeoZ;

      if (vname.equalsIgnoreCase("z") || findAlias(ds, v).equalsIgnoreCase("z") ||
          vname.equalsIgnoreCase("altitude") || desc.contains("altitude") || vname.equalsIgnoreCase("depth") ||
          vname.equalsIgnoreCase("elev") || vname.equalsIgnoreCase("elevation")) {
        if (SimpleUnit.isCompatible("m", unit)) // units of meters
          return AxisType.Height;
    }

      if (vname.equalsIgnoreCase("time") || findAlias(ds, v).equalsIgnoreCase("time"))  {
        if (SimpleUnit.isDateUnit(unit))
          return AxisType.Time;
      }

      if (vname.equalsIgnoreCase("time") && v.getDataType() == DataType.STRING) {
        try {
          Array firstValue = v.read("0");
          // System.out.printf("%s%n", NCdumpW.printArray(firstValue, "firstValue", null));
          if (firstValue instanceof ArrayObject.D1) {
            ArrayObject.D1 sarry = (ArrayObject.D1) firstValue;
            String firstStringValue = (String) sarry.get(0);
            if (CalendarDate.parseISOformat(null, firstStringValue) != null) // valid iso date string
              return AxisType.Time;
          }
        } catch (IOException | InvalidRangeException e) {
          logger.warn("time string error", e);
        }
      }

      return null;
    }

    // look for an coord_axis or coord_alias attribute
    private String findAlias(NetcdfDataset ds, Variable v) {
      String alias = ds.findAttValueIgnoreCase(v, "coord_axis", null);
      if (alias == null)
        alias = ds.findAttValueIgnoreCase(v, "coord_alias", "");
      if (alias == null) alias = "";
      return alias;
    }

  // replicated from COARDS, but we need to diverge from COARDS
    // we assume that coordinate axes get identified by being coordinate variables
   private AxisType getAxisTypeCoards( NetcdfDataset ncDataset, VariableEnhanced v) {

     String unit = v.getUnitsString();
     if (unit == null)
       return null;

     if( unit.equalsIgnoreCase("degrees_east") ||
             unit.equalsIgnoreCase("degrees_E") ||
             unit.equalsIgnoreCase("degreesE") ||
             unit.equalsIgnoreCase("degree_east") ||
             unit.equalsIgnoreCase("degree_E") ||
             unit.equalsIgnoreCase("degreeE"))
       return AxisType.Lon;

     if ( unit.equalsIgnoreCase("degrees_north") ||
             unit.equalsIgnoreCase("degrees_N") ||
             unit.equalsIgnoreCase("degreesN") ||
             unit.equalsIgnoreCase("degree_north") ||
             unit.equalsIgnoreCase("degree_N") ||
             unit.equalsIgnoreCase("degreeN"))
       return AxisType.Lat;

     if (SimpleUnit.isDateUnit(unit)) // || SimpleUnit.isTimeUnit(unit)) removed dec 18, 2008
       return AxisType.Time;

     // look for other z coordinate
     //if (SimpleUnit.isCompatible("m", unit))
     //  return AxisType.Height;
     if (SimpleUnit.isCompatible("mbar", unit))
       return AxisType.Pressure;
     if (unit.equalsIgnoreCase("level") || unit.equalsIgnoreCase("layer") || unit.equalsIgnoreCase("sigma_level"))
       return AxisType.GeoZ;

     String positive = ncDataset.findAttValueIgnoreCase((Variable) v, "positive", null);
     if (positive != null) {
       if (SimpleUnit.isCompatible("m", unit))
         return AxisType.Height;
       else
         return AxisType.GeoZ;
     }
     return null;
   }

    private ProjectionCT makeProjectionCT(NetcdfDataset ds) {
      // look for projection in global attribute
      String projection = ds.findAttValueIgnoreCase(null, "projection", null);
      if (null == projection) {
        parseInfo.format("Default Conventions error: NO projection name found %n");
        return null;
      }
      String params = ds.findAttValueIgnoreCase(null, "projection_params", null);
      if (null == params) params = ds.findAttValueIgnoreCase(null, "proj_params", null);
      if (null == params) {
        parseInfo.format("Default Conventions error: NO projection parameters found %n");
        return null;
      }

      // parse the parameters
      int count = 0;
      double[] p = new double[4];
      try {
        // new way : just the parameters
        StringTokenizer stoke = new StringTokenizer(params, " ,");
        while (stoke.hasMoreTokens() && (count < 4)) {
          p[count++] = Double.parseDouble(stoke.nextToken());
        }
      } catch (NumberFormatException e) {
        // old way : every other one
        StringTokenizer stoke = new StringTokenizer(params, " ,");
        while (stoke.hasMoreTokens() && (count < 4)) {
          stoke.nextToken(); // skip
          p[count++] = Double.parseDouble(stoke.nextToken());
        }
      }

      parseInfo.format("Default Conventions projection %s params = %f %f %f %f%n", projection, p[0],p[1],p[2],p[3]);

      ProjectionImpl proj;
      if (projection.equalsIgnoreCase("LambertConformal"))
        proj = new LambertConformal(p[0], p[1], p[2], p[3]);
      else if (projection.equalsIgnoreCase("TransverseMercator"))
        proj = new TransverseMercator(p[0], p[1], p[2]);
      else if (projection.equalsIgnoreCase("Stereographic") || projection.equalsIgnoreCase("Oblique_Stereographic"))
        proj = new Stereographic(p[0], p[1], p[2]);
      else {
        parseInfo.format("Default Conventions error: Unknown projection %s%n",projection);
        return null;
      }

      return new ProjectionCT(proj.getClassName(), "FGDC", proj);
    }

}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy