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

ucar.nc2.iosp.hdf4.HdfEos 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.

There is a newer version: 4.3.22
Show newest version
/*
 * Copyright 1998-2009 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.iosp.hdf4;

import ucar.nc2.*;
import ucar.nc2.dataset.CoordinateSystem;
import ucar.nc2.constants._Coordinate;
import ucar.nc2.constants.AxisType;
import ucar.nc2.constants.FeatureType;
import ucar.ma2.Array;
import ucar.ma2.DataType;
import ucar.ma2.ArrayChar;

import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Formatter;

import org.jdom.Element;

/**
 * Parse structural metadata from HDF-EOS.
 * This allows us to use shared dimensions, identify Coordinate Axes, and the FeatureType.
 *
 * @author caron
 * @since Jul 23, 2007
 */
public class HdfEos {
  static private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(HdfEos.class);
  static boolean showWork = false; // set in debug
  static private final String GEOLOC_FIELDS = "Geolocation Fields";
  //static private final String GEOLOC_FIELDS2 = "Geolocation_Fields";
  static private final String DATA_FIELDS = "Data Fields";
  //static private final String DATA_FIELDS2 = "Data_Fields";

  /**
   * Amend the given NetcdfFile with metadata from HDF-EOS structMetadata.
   * All Variables named StructMetadata.n, where n= 1, 2, 3 ... are read in and their contents concatenated
   * to make the structMetadata String.
   *
   * @param ncfile Amend this file
   * @param eosGroup the group containing variables named StructMetadata.*
   * @throws IOException on read error
   * @return true if HDF-EOS info was found
   */
  static public boolean amendFromODL(NetcdfFile ncfile, Group eosGroup) throws IOException {
    String smeta = getStructMetadata(eosGroup);
    if (smeta == null) return false;

    new HdfEos().amendFromODL(ncfile, smeta);
    return true;
  }

  static public void getEosInfo(NetcdfFile ncfile, Group eosGroup, Formatter f) throws IOException {
    String smeta = getStructMetadata(eosGroup);
    if (smeta == null) {
      f.format("No StructMetadata variables in group %s %n", eosGroup.getName());
      return;
    }
    f.format("raw = %n%s%n", smeta);
    ODLparser parser = new ODLparser();
    parser.parseFromString(smeta.toString()); // now we have the ODL in JDOM elements
    ByteArrayOutputStream bos = new ByteArrayOutputStream(1000 * 1000);
    parser.showDoc(bos);
    f.format("parsed = %n%s%n", bos.toString());
  }

  static private String getStructMetadata(Group eosGroup) throws IOException {
    StringBuilder sbuff = null;
    String structMetadata = null;

    int n = 0;
    while (true) {
      Variable structMetadataVar = eosGroup.findVariable("StructMetadata." + n);
      if (structMetadataVar == null) break;
      if ((structMetadata != null) && (sbuff == null)) { // more than 1 StructMetadata
        sbuff = new StringBuilder(64000);
        sbuff.append(structMetadata);
      }

      // read and parse the ODL
      Array A = structMetadataVar.read();
      ArrayChar ca = (ArrayChar) A;
      structMetadata = ca.getString(); // common case only StructMetadata.0, avoid extra copy

      if (sbuff != null)
        sbuff.append(structMetadata);
      n++;
    }
    return (sbuff != null) ? sbuff.toString() : structMetadata;
  }

  /**
   * Amend the given NetcdfFile with metadata from HDF-EOS structMetadata
   *
   * @param ncfile Amend this file
   * @param structMetadata  structMetadata as String
   * @throws IOException on read error
   */
  private void amendFromODL(NetcdfFile ncfile, String structMetadata) throws IOException {
    Group rootg = ncfile.getRootGroup();

    ODLparser parser = new ODLparser();
    Element root = parser.parseFromString(structMetadata); // now we have the ODL in JDOM elements
    FeatureType featureType = null;

    // SWATH
    Element swathStructure = root.getChild("SwathStructure");
    if (swathStructure != null) {
      List swaths = (List) swathStructure.getChildren();
      for (Element elemSwath : swaths) {
        Element swathNameElem = elemSwath.getChild("SwathName");
        if (swathNameElem == null) {
          log.warn("No SwathName element in " + elemSwath.getName());
          continue;
        }
        String swathName = swathNameElem.getText();
        Group swathGroup = findGroupNested(rootg, swathName);
        //if (swathGroup == null)
        //  swathGroup = findGroupNested(rootg, H4header.createValidObjectName(swathName));

        if (swathGroup != null) {
          featureType = amendSwath(ncfile, elemSwath, swathGroup);
        } else {
          log.warn("Cant find swath group " + swathName);
        }
      }
    }

    // GRID
    Element gridStructure = root.getChild("GridStructure");
    if (gridStructure != null) {
      List grids = (List) gridStructure.getChildren();
      for (Element elemGrid : grids) {
        Element gridNameElem = elemGrid.getChild("GridName");
        if (gridNameElem == null) {
          log.warn("Ne GridName element in " + elemGrid.getName());
          continue;
        }
        String gridName = gridNameElem.getText();
        Group gridGroup = findGroupNested(rootg, gridName);
        //if (gridGroup == null)
        //  gridGroup = findGroupNested(rootg, H4header.createValidObjectName(gridName));
        if (gridGroup != null) {
          featureType = amendGrid(elemGrid, gridGroup);
        } else {
          log.warn("Cant find Grid group " + gridName);
        }
      }
    }

    // POINT - NOT DONE YET
    Element pointStructure = root.getChild("PointStructure");
    if (pointStructure != null) {
      List pts = (List) pointStructure.getChildren();
      for (Element elem : pts) {
        Element nameElem = elem.getChild("PointName");
        if (nameElem == null) {
          log.warn("No PointName element in " + elem.getName());
          continue;
        }
        String name = nameElem.getText();
        Group ptGroup = findGroupNested(rootg, name);
        //if (ptGroup == null)
        //  ptGroup = findGroupNested(rootg, H4header.createValidObjectName(name));
        if (ptGroup != null) {
          featureType = FeatureType.POINT;
        } else {
          log.warn("Cant find Point group " + name);
        }
      }
    }

    if (featureType != null) {
      if (showWork) System.out.println("***EOS featureType= "+featureType.toString());
      rootg.addAttribute(new Attribute("cdm_data_type", featureType.toString()));
    }

  }

  private FeatureType amendSwath(NetcdfFile ncfile, Element swathElem, Group parent) {
    FeatureType featureType = FeatureType.SWATH;
    List unknownDims = new ArrayList();

    // Dimensions
    Element d = swathElem.getChild("Dimension");
    List dims = (List) d.getChildren();
    for (Element elem : dims) {
      String name = elem.getChild("DimensionName").getText();
      name = H4header.createValidObjectName(name);

      if (name.equalsIgnoreCase("scalar"))
        continue;
      String sizeS = elem.getChild("Size").getText();
      int length = Integer.parseInt(sizeS);
      if (length > 0) {
        Dimension dim = new Dimension(name, length);
        parent.addDimension(dim);
        if (showWork) System.out.printf(" Add dimension %s %n",dim);
      } else {
        log.warn("Dimension "+name+" has size "+sizeS);
        Dimension udim = new Dimension(name, 1);
        udim.setGroup(parent);
        unknownDims.add( udim);
        if (showWork) System.out.printf(" Add dimension %s %n", udim);
      }
    }

    // Dimension Maps
    Element dmap = swathElem.getChild("DimensionMap");
    List dimMaps = (List) dmap.getChildren();
    for (Element elem : dimMaps) {
      String geoDimName = elem.getChild("GeoDimension").getText();
      geoDimName = H4header.createValidObjectName(geoDimName);
      String dataDimName = elem.getChild("DataDimension").getText();
      dataDimName = H4header.createValidObjectName(dataDimName);

      String offsetS = elem.getChild("Offset").getText();
      String incrS = elem.getChild("Increment").getText();
      int offset = Integer.parseInt(offsetS);
      int incr = Integer.parseInt(incrS);

      // make new variable for this dimension map
      Variable v = new Variable(ncfile, parent, null, dataDimName);
      v.setDimensions(geoDimName);
      v.setDataType(DataType.INT);
      int npts = (int) v.getSize();
      Array data = Array.makeArray(v.getDataType(), npts, offset, incr);
      v.setCachedData(data, true);
      v.addAttribute(new Attribute("_DimensionMap", ""));
      parent.addVariable(v);
      if (showWork) System.out.printf(" Add dimensionMap %s %n", v);
    }

    // Geolocation Variables
    Group geoFieldsG = parent.findGroup(GEOLOC_FIELDS);
    if (geoFieldsG != null) {
      Variable latAxis = null, lonAxis = null;
      Element floc = swathElem.getChild("GeoField");
      List varsLoc = (List) floc.getChildren();
      for (Element elem : varsLoc) {
        String varname = elem.getChild("GeoFieldName").getText();
        Variable v = geoFieldsG.findVariable(varname);
        //if (v == null)
        //  v = geoFieldsG.findVariable( H4header.createValidObjectName(varname));
        assert v != null : varname;
        AxisType axis = addAxisType(v);
        if (axis == AxisType.Lat) latAxis = v;
        if (axis == AxisType.Lon) lonAxis = v;

        Element dimList = elem.getChild("DimList");
        List values = (List) dimList.getChildren("value");
        setSharedDimensions( v, values, unknownDims);
        if (showWork) System.out.printf(" set coordinate %s %n", v);
      }
      if ((latAxis != null) && (lonAxis != null)) {
        List xyDomain = CoordinateSystem.makeDomain(new Variable[] {latAxis, lonAxis});
       if (xyDomain.size() < 2) featureType = FeatureType.PROFILE;  // ??
      }

    }

    // Data Variables
    Group dataG = parent.findGroup(DATA_FIELDS);
    if (dataG != null) {

      Element f = swathElem.getChild("DataField");
      List vars = (List) f.getChildren();
      for (Element elem : vars) {
        Element dataFieldNameElem = elem.getChild("DataFieldName");
        if (dataFieldNameElem == null) continue;
        String varname = dataFieldNameElem.getText();
        Variable v = dataG.findVariable(varname);
        //if (v == null)
        //  v = dataG.findVariable( H4header.createValidObjectName(varname));
        if (v == null) {
          log.error("Cant find variable "+varname);
          continue;
        }

        Element dimList = elem.getChild("DimList");
        List values = (List) dimList.getChildren("value");
        setSharedDimensions( v, values, unknownDims);
      }
    }

    return featureType;
  }

  private AxisType addAxisType(Variable v) {
    String name = v.getShortName();
    if (name.equalsIgnoreCase("Latitude") || name.equalsIgnoreCase("GeodeticLatitude")) {
      v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lat.toString()));
      v.addAttribute(new Attribute("units", "degrees_north"));
      return AxisType.Lat;

    } else if (name.equalsIgnoreCase("Longitude")) {
      v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lon.toString()));
      v.addAttribute(new Attribute("units", "degrees_east"));
      return AxisType.Lon;

    } else if (name.equalsIgnoreCase("Time")) {
      v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Time.toString()));
      if (v.findAttribute("units") == null)
        v.addAttribute(new Attribute("units", "secs since 1970-01-01 00:00:00")); // default units I hope
      return AxisType.Time;

    } else if (name.equalsIgnoreCase("Pressure")) {
      v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Pressure.toString()));
      return AxisType.Pressure;

    } else if (name.equalsIgnoreCase("Altitude")) {
      v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Height.toString()));
      v.addAttribute(new Attribute("positive", "up")); // probably
      return AxisType.Height;
     }

    return null;
  }


  private FeatureType amendGrid(Element gridElem, Group parent) {
    List unknownDims = new ArrayList();

    // always has x and y dimension
    String xdimSizeS = gridElem.getChild("XDim").getText();
    String ydimSizeS = gridElem.getChild("YDim").getText();
    int xdimSize = Integer.parseInt(xdimSizeS);
    int ydimSize = Integer.parseInt(ydimSizeS);
    parent.addDimension(new Dimension("XDim", xdimSize));
    parent.addDimension(new Dimension("YDim", ydimSize));

    // global Dimensions
    Element d = gridElem.getChild("Dimension");
    List dims = (List) d.getChildren();
    for (Element elem : dims) {
      String name = elem.getChild("DimensionName").getText();
      name = H4header.createValidObjectName(name);
      if (name.equalsIgnoreCase("scalar"))
        continue;

      String sizeS = elem.getChild("Size").getText();
      int length = Integer.parseInt(sizeS);
      Dimension old = parent.findDimension(name);
      if ((old == null) || (old.getLength() != length)) {
        if (length > 0) {
          Dimension dim = new Dimension(name, length);
          parent.addDimension(dim);
          if (showWork) System.out.printf(" Add dimension %s %n", dim);
        } else {
          log.warn("Dimension "+name+" has size "+sizeS);
          Dimension udim = new Dimension(name, 1);
          udim.setGroup(parent);
          unknownDims.add( udim);
          if (showWork) System.out.printf(" Add dimension %s %n", udim);
        }
      }
    }

    // Geolocation Variables
    Group geoFieldsG = parent.findGroup(GEOLOC_FIELDS);
    //if (geoFieldsG == null)  geoFieldsG = parent.findGroup(GEOLOC_FIELDS2);

    if (geoFieldsG != null) {
      Element floc = gridElem.getChild("GeoField");
      List varsLoc = (List) floc.getChildren();
      for (Element elem : varsLoc) {
        String varname = elem.getChild("GeoFieldName").getText();
        Variable v = geoFieldsG.findVariable(varname);
        //if (v == null)
        //  v = geoFieldsG.findVariable( H4header.createValidObjectName(varname));
        assert v != null : varname;

        Element dimList = elem.getChild("DimList");
        List values = (List) dimList.getChildren("value");
        setSharedDimensions( v, values, unknownDims);
      }
    }

    // Data Variables
    Group dataG = parent.findGroup(DATA_FIELDS);
    //if (dataG == null) dataG = parent.findGroup(DATA_FIELDS2);

    if (dataG != null) {
      Element f = gridElem.getChild("DataField");
      List vars = (List) f.getChildren();
      for (Element elem : vars) {
        String varname = elem.getChild("DataFieldName").getText();
        Variable v = dataG.findVariable(varname);
        //if (v == null)
        //  v = dataG.findVariable( H4header.createValidObjectName(varname));
        assert v != null : varname;

        Element dimList = elem.getChild("DimList");
        List values = (List) dimList.getChildren("value");
        setSharedDimensions( v, values, unknownDims);
      }
    }

    // get projection
    String projS = null;
    Element projElem = gridElem.getChild("Projection");
    if (projElem != null)
      projS = projElem.getText();
    boolean isLatLon = "GCTP_GEO".equals(projS);

    // look for XDim, YDim coordinate variables
    if (isLatLon) {
      for (Variable v : dataG.getVariables()) {
        if (v.isCoordinateVariable()) {
          if (v.getShortName().equals("YDim"))
            v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lat.toString()));
          if (v.getShortName().equals("XDim"))
            v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lon.toString()));
        }
      }

    }
    return FeatureType.GRID;
  }

  // convert to shared dimensions
  private void setSharedDimensions(Variable v, List values, List unknownDims) {
    if (values.size() == 0)
      return;

    // remove the "scalar" dumbension
    Iterator iter = values.iterator();
    while (iter.hasNext()) {
      Element value = iter.next();
      String dimName = value.getText();
      if (dimName.equalsIgnoreCase("scalar"))
        iter.remove();
    }

    // gotta have same number of dimensions
    List oldDims = v.getDimensions();
    if (oldDims.size() != values.size()) {
      log.error("Different number of dimensions for "+ v);
      return;
    }

    List newDims = new ArrayList();
    Group group = v.getParentGroup();

    for (int i=0; i unknownDims, Dimension oldDim) {
    for (Dimension dim : unknownDims) {
      if (dim.getName().equals(wantDim)) {
        int len = oldDim.getLength();
        if (len == 0)
          dim.setUnlimited( true); // allow zero length dimension !!
        dim.setLength(len); // use existing (anon) dimension
        Group parent = dim.getGroup();
        parent.addDimension(dim);  // add to the parent
        unknownDims.remove(dim); // remove from list LOOK is this ok?
        log.warn("unknownDim " + wantDim+" length set to "+oldDim.getLength());
        return dim;
      }
    }
    return null;
  }

  // look for a group with the given name. recurse into subgroups if needed. breadth first
  private Group findGroupNested(Group parent, String name) {

    for (Group g : parent.getGroups()) {
      if (g.getShortName().equals(name))
        return g;
    }
    for (Group g : parent.getGroups()) {
      Group result = findGroupNested(g, name);
      if (result != null)
        return result;
    }
    return null;
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy