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

ucar.nc2.dt.grid.NetcdfCFWriter 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.dt.grid;

import ucar.unidata.geoloc.LatLonRect;
import ucar.unidata.geoloc.Projection;
import ucar.unidata.geoloc.ProjectionPointImpl;
import ucar.unidata.geoloc.LatLonPointImpl;
import ucar.unidata.geoloc.projection.LatLonProjection;
import ucar.nc2.*;
import ucar.nc2.units.DateFormatter;
import ucar.nc2.units.DateRange;
import ucar.nc2.dt.GridDatatype;
import ucar.nc2.dt.GridCoordSystem;
import ucar.nc2.dataset.*;
import ucar.nc2.dataset.transform.AbstractCoordTransBuilder;
import ucar.nc2.constants._Coordinate;
import ucar.nc2.constants.AxisType;
import ucar.ma2.Range;
import ucar.ma2.InvalidRangeException;
import ucar.ma2.DataType;
import ucar.ma2.Array;

import java.io.IOException;
import java.util.*;
import java.text.ParseException;

/**
 * Write a CF compliant Netcdf-3 file from any gridded dataset. The datasets can optionally be subsetted by a lat/lon
 * bounding box and/or a time range.
 *
 * @author caron
 */
public class NetcdfCFWriter {
  static private long maxSize = 2 * 1000 * 1000 * 1000; // 2 Gb
  static private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NetcdfCFWriter.class);


  /**
   * Write a CF compliant Netcdf-3 file from any gridded dataset.
   *
   * @param location    write to this location on disk
   * @param gds         A gridded dataset
   * @param gridList    the list of grid names to be written, must not be empty. Full name (not short).
   * @param llbb        optional lat/lon bounding box
   * @param range       optional time range
   * @param addLatLon   should 2D lat/lon variables be added, if its a projection coordainte system?
   * @param horizStride x,y stride
   * @param stride_z    not implemented yet
   * @param stride_time not implemented yet
   * @throws IOException           if write or read error
   * @throws InvalidRangeException if subset is illegal
   */
  public void makeFile(String location, ucar.nc2.dt.GridDataset gds, List gridList,
          LatLonRect llbb, DateRange range,
          boolean addLatLon,
          int horizStride, int stride_z, int stride_time)
          throws IOException, InvalidRangeException {
    makeFile(location, gds, gridList, llbb, horizStride, null, range, stride_time, addLatLon);
  }

  public void makeFile(String location, ucar.nc2.dt.GridDataset gds, List gridList,
          LatLonRect llbb, int horizStride,
          Range zRange,
          DateRange dateRange, int stride_time,
          boolean addLatLon)
          throws IOException, InvalidRangeException {

    FileWriter writer = new FileWriter(location, false);
    NetcdfDataset ncd = (NetcdfDataset) gds.getNetcdfFile();

    // global attributes
    for (Attribute att : gds.getGlobalAttributes())
      writer.writeGlobalAttribute(att);

    writer.writeGlobalAttribute(new Attribute("Conventions", "CF-1.0"));
    writer.writeGlobalAttribute(new Attribute("History",
            "Translated to CF-1.0 Conventions by Netcdf-Java CDM (NetcdfCFWriter)\n" +
                    "Original Dataset = " + gds.getLocationURI() + "; Translation Date = " + new Date()));

    ArrayList varList = new ArrayList();
    ArrayList varNameList = new ArrayList();
    ArrayList axisList = new ArrayList();

    // add each desired Grid to the new file
    long total_size = 0;
    for (String gridName : gridList) {
      if (varNameList.contains(gridName))
        continue;

      varNameList.add(gridName);

      GridDatatype grid = gds.findGridDatatype(gridName);
      GridCoordSystem gcsOrg = grid.getCoordinateSystem();
      CoordinateAxis1DTime timeAxis = gcsOrg.getTimeAxis1D();

      // make subset if needed
      Range timeRange = null;
      if ((dateRange != null) && (timeAxis != null)) {
        int startIndex = timeAxis.findTimeIndexFromDate(dateRange.getStart().getDate());
        int endIndex = timeAxis.findTimeIndexFromDate(dateRange.getEnd().getDate());
        if (startIndex < 0)
          throw new InvalidRangeException("start time=" + dateRange.getStart().getDate() + " must be >= " + timeAxis.getTimeDate(0));
        if (endIndex < 0)
          throw new InvalidRangeException("end time=" + dateRange.getEnd().getDate() + " must be >= " + timeAxis.getTimeDate(0));
        timeRange = new Range(startIndex, endIndex);
      }

      if ((null != timeRange) || (zRange != null) || (llbb != null) || (horizStride > 1)) {
        grid = grid.makeSubset(timeRange, zRange, llbb, 1, horizStride, horizStride);
      }

      Variable gridV = (Variable) grid.getVariable();
      varList.add(gridV);
      total_size += gridV.getSize() * gridV.getElementSize();

      // add coordinate axes
      GridCoordSystem gcs = grid.getCoordinateSystem();
      for (CoordinateAxis axis : gcs.getCoordinateAxes()) {
        if (!varNameList.contains(axis.getName())) {
          varNameList.add(axis.getName());
          varList.add(axis); // LOOK dont we have to subset these ??
          axisList.add(axis);
        }
      }

      // add coordinate transform variables
      for (CoordinateTransform ct : gcs.getCoordinateTransforms()) {
        Variable v = ncd.findVariable(ct.getName());
        if (!varNameList.contains(ct.getName()) && (null != v)) {
          varNameList.add(ct.getName());
          varList.add(v);
        }
      }

      // optional lat/lon
      if (addLatLon) {
        Projection proj = gcs.getProjection();
        if ((null != proj) && !(proj instanceof LatLonProjection)) {
          addLatLon2D(ncd, varList, proj, gcs.getXHorizAxis(), gcs.getYHorizAxis());
          addLatLon = false;
        }
      }
    }

    // check size is ok
    if (total_size > maxSize) {
      log.info("Reject request size = {} Mbytes", total_size);
      throw new IllegalArgumentException("Request too big=" + total_size+" Mbytes, max="+maxSize);
    }

    writer.writeVariables(varList);

    // now add CF annotations as needed - dont change original ncd or gds
    NetcdfFileWriteable ncfile = writer.getNetcdf();
    Group root = ncfile.getRootGroup();
    for (String gridName : gridList) {
      GridDatatype grid = gds.findGridDatatype(gridName);
      Variable newV = root.findVariable(gridName);
      if (newV == null) {
        log.warn("NetcdfCFWriter cant find "+gridName+" in gds "+gds.getLocationURI());
        continue;
      }

      // annotate Variable for CF
      StringBuilder sbuff = new StringBuilder();
      GridCoordSystem gcs = grid.getCoordinateSystem();
      for (Variable axis : gcs.getCoordinateAxes()) {
        sbuff.append(axis.getName()).append(" ");
      }
      if (addLatLon)
        sbuff.append("lat lon");
      newV.addAttribute(new Attribute("coordinates", sbuff.toString()));

      // looking for coordinate transform variables
      for (CoordinateTransform ct : gcs.getCoordinateTransforms()) {
        Variable v = ncd.findVariable(ct.getName());
        if (ct.getTransformType() == TransformType.Projection)
          newV.addAttribute(new Attribute("grid_mapping", v.getName()));
      }
    }

    for (CoordinateAxis axis : axisList) {
      Variable newV = root.findVariable(axis.getShortName()); // LOOK ???
      if ((axis.getAxisType() == AxisType.Height) || (axis.getAxisType() == AxisType.Pressure) || (axis.getAxisType() == AxisType.GeoZ)) {
        if (null != axis.getPositive())
          newV.addAttribute(new Attribute("positive", axis.getPositive()));
      }
      if (axis.getAxisType() == AxisType.Lat) {
        newV.addAttribute(new Attribute("units", "degrees_north"));
        newV.addAttribute(new Attribute("standard_name", "latitude"));
      }
      if (axis.getAxisType() == AxisType.Lon) {
        newV.addAttribute(new Attribute("units", "degrees_east"));
        newV.addAttribute(new Attribute("standard_name", "longitude"));
      }
      if (axis.getAxisType() == AxisType.GeoX) {
        newV.addAttribute(new Attribute("standard_name", "projection_x_coordinate"));
      }
      if (axis.getAxisType() == AxisType.GeoY) {
        newV.addAttribute(new Attribute("standard_name", "projection_y_coordinate"));
      }
    }

    // coordinate transform variables : must convert false easting, northing to km
    List ctvList = new ArrayList();
    for (ucar.nc2.dt.GridDataset.Gridset gridSet : gds.getGridsets()) {
      ucar.nc2.dt.GridCoordSystem gcs = gridSet.getGeoCoordSystem();
      ProjectionCT pct = gcs.getProjectionCT();
      if (pct != null) {
        Variable v = root.findVariable(pct.getName()); // look for the ctv
        if ((v != null) && !ctvList.contains(v)) {
          convertProjectionCTV((NetcdfDataset) gds.getNetcdfFile(), v);
          ctvList.add(v);
        }
      }
    }

    // LOOK not dealing with crossing the seam

    writer.finish(); // this writes the data to the new file.
  }

  private void convertProjectionCTV(NetcdfDataset ds, Variable ctv) {
    Attribute att = ctv.findAttribute(_Coordinate.TransformType);
    if ((null != att) && att.getStringValue().equals("Projection")) {
      Attribute east = ctv.findAttribute("false_easting");
      Attribute north = ctv.findAttribute("false_northing");
      if ((null != east) || (null != north)) {
        double scalef = AbstractCoordTransBuilder.getFalseEastingScaleFactor(ds, ctv);
        if (scalef != 1.0) {
          convertAttribute(ctv, east, scalef);
          convertAttribute(ctv, north, scalef);
        }
      }
    }
  }

  private void convertAttribute(Variable ctv, Attribute att, double scalef) {
    if (att == null) return;
    double val = scalef * att.getNumericValue().doubleValue();
    ctv.addAttribute(new Attribute(att.getName(), val));
  }

  private void addLatLon2D(NetcdfFile ncfile, List varList, Projection proj,
          CoordinateAxis xaxis, CoordinateAxis yaxis) throws IOException {

    double[] xData = (double[]) xaxis.read().get1DJavaArray(double.class);
    double[] yData = (double[]) yaxis.read().get1DJavaArray(double.class);

    List dims = new ArrayList();
    dims.add(yaxis.getDimension(0));
    dims.add(xaxis.getDimension(0));

    Variable latVar = new Variable(ncfile, null, null, "lat");
    latVar.setDataType(DataType.DOUBLE);
    latVar.setDimensions(dims);
    latVar.addAttribute(new Attribute("units", "degrees_north"));
    latVar.addAttribute(new Attribute("long_name", "latitude coordinate"));
    latVar.addAttribute(new Attribute("standard_name", "latitude"));
    latVar.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lat.toString()));

    Variable lonVar = new Variable(ncfile, null, null, "lon");
    lonVar.setDataType(DataType.DOUBLE);
    lonVar.setDimensions(dims);
    lonVar.addAttribute(new Attribute("units", "degrees_east"));
    lonVar.addAttribute(new Attribute("long_name", "longitude coordinate"));
    lonVar.addAttribute(new Attribute("standard_name", "longitude"));
    lonVar.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lon.toString()));

    int nx = xData.length;
    int ny = yData.length;

    // create the data
    ProjectionPointImpl projPoint = new ProjectionPointImpl();
    LatLonPointImpl latlonPoint = new LatLonPointImpl();
    double[] latData = new double[nx * ny];
    double[] lonData = new double[nx * ny];
    for (int i = 0; i < ny; i++) {
      for (int j = 0; j < nx; j++) {
        projPoint.setLocation(xData[j], yData[i]);
        proj.projToLatLon(projPoint, latlonPoint);
        latData[i * nx + j] = latlonPoint.getLatitude();
        lonData[i * nx + j] = latlonPoint.getLongitude();
      }
    }
    Array latDataArray = Array.factory(DataType.DOUBLE, new int[]{ny, nx}, latData);
    latVar.setCachedData(latDataArray, false);

    Array lonDataArray = Array.factory(DataType.DOUBLE, new int[]{ny, nx}, lonData);
    lonVar.setCachedData(lonDataArray, false);

    varList.add(latVar);
    varList.add(lonVar);
  }

  public static void test1() throws IOException, InvalidRangeException, ParseException {
    String fileIn = "C:/data/ncmodels/NAM_CONUS_80km_20051206_0000.nc";
    String fileOut = "C:/temp/cf3.nc";

    ucar.nc2.dt.GridDataset gds = ucar.nc2.dt.grid.GridDataset.open(fileIn);

    NetcdfCFWriter writer = new NetcdfCFWriter();

    List gridList = new ArrayList();
    gridList.add("RH");
    gridList.add("T");

    DateFormatter format = new DateFormatter();
    Date start = format.isoDateTimeFormat("2005-12-06T18:00:00Z");
    Date end = format.isoDateTimeFormat("2005-12-07T18:00:00Z");

    writer.makeFile(fileOut, gds, gridList,
            new LatLonRect(new LatLonPointImpl(37, -109), 400, 7),
            new DateRange(start, end),
            true,
            1, 1, 1);

  }

  public static void main(String args[]) throws IOException, InvalidRangeException, ParseException {
    String fileIn = "dods://motherlode.ucar.edu/repository/entry/show/output:data.opendap/entryid:c41a3a26-57e5-4b15-b8b1-a8762b6f02c7/dodsC/entry";
    String fileOut = "C:/temp/testCF.nc";

    ucar.nc2.dt.GridDataset gds = ucar.nc2.dt.grid.GridDataset.open(fileIn);

    NetcdfCFWriter writer = new NetcdfCFWriter();

    List gridList = new ArrayList();
    gridList.add("Z_sfc");

    DateFormatter format = new DateFormatter();
    Date start = format.isoDateTimeFormat("2003-06-01T03:00:00Z");
    Date end = format.isoDateTimeFormat("2004-01-01T00:00:00Z");

    writer.makeFile(fileOut, gds, gridList, null,
            // new LatLonRect(new LatLonPointImpl(30, -109), 10, 50),
            new DateRange(start, end),
            true,
            1, 1, 1);

  }


}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy