ucar.nc2.dataset.conv.CF1Convention Maven / Gradle / Ivy
/*
* 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.dataset.conv;
import ucar.nc2.*;
import ucar.nc2.constants._Coordinate;
import ucar.nc2.constants.AxisType;
import ucar.nc2.constants.CF;
import ucar.nc2.units.SimpleUnit;
import ucar.nc2.util.CancelTask;
import ucar.nc2.dataset.*;
import ucar.ma2.Array;
import ucar.ma2.IndexIterator;
import ucar.ma2.DataType;
import java.util.*;
import java.io.IOException;
/**
* CF-1 Convention.
* see http://www.cgd.ucar.edu/cms/eaton/cf-metadata/index.html
*
*
* "The CF conventions for climate and forecast metadata are designed to promote the
* processing and sharing of files created with the netCDF API. The conventions define
* metadata that provide a definitive description of what the data in each variable
* represents, and of the spatial and temporal properties of the data.
* This enables users of data from different sources to decide which quantities are
* comparable, and facilitates building applications with powerful extraction, regridding,
* and display capabilities."
*
*
* @author caron
*/
public class CF1Convention extends CSMConvention {
/**
* Guess the value of ZisPositive based on z axis name and units
*
* @param zaxisName z coordinate axis name
* @param vertCoordUnits z coordinate axis name
* @return CF.POSITIVE_UP or CF.POSITIVE_DOWN
*/
public static String getZisPositive(String zaxisName, String vertCoordUnits) {
if (vertCoordUnits == null) return CF.POSITIVE_UP;
if (SimpleUnit.isCompatible("millibar", vertCoordUnits))
return CF.POSITIVE_DOWN;
if (SimpleUnit.isCompatible("m", vertCoordUnits))
return CF.POSITIVE_UP;
// dunno - make it up
return CF.POSITIVE_UP;
}
private static String[] vertical_coords = {
"atmosphere_sigma_coordinate",
"atmosphere_hybrid_sigma_pressure_coordinate",
"atmosphere_hybrid_height_coordinate",
"atmosphere_sleve_coordinate",
"ocean_sigma_coordinate",
"ocean_s_coordinate",
"ocean_sigma_z_coordinate",
"ocean_double_sigma_coordinate",
"ocean_s_coordinate_g1", // -sachin 03/25/09
"ocean_s_coordinate_g2"};
public CF1Convention() {
this.conventionName = "CF-1.X";
}
public void augmentDataset(NetcdfDataset ds, CancelTask cancelTask) throws IOException {
boolean got_grid_mapping = false;
// look for transforms
List vars = ds.getVariables();
for (Variable v : vars) {
// look for special standard_names
String sname = ds.findAttValueIgnoreCase(v, CF.STANDARD_NAME, null);
if (sname != null) {
sname = sname.trim();
if (sname.equalsIgnoreCase("atmosphere_ln_pressure_coordinate")) { // LOOK why isnt this with other Transforms?
makeAtmLnCoordinate(ds, v);
continue;
}
if (sname.equalsIgnoreCase("forecast_reference_time")) { // experimental
v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.RunTime.toString()));
continue;
}
if (sname.equalsIgnoreCase("ensemble")) { // experimental
v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Ensemble.toString()));
continue;
}
for (String vertical_coord : vertical_coords)
if (sname.equalsIgnoreCase(vertical_coord)) {
v.addAttribute(new Attribute(_Coordinate.TransformType, TransformType.Vertical.toString()));
if (v.findAttribute(_Coordinate.Axes) == null)
v.addAttribute(new Attribute(_Coordinate.Axes, v.getName())); // LOOK: may also be time dependent
}
}
// look for horiz transforms. only ones that are referenced by another variable.
String grid_mapping = ds.findAttValueIgnoreCase(v, CF.GRID_MAPPING, null);
if (grid_mapping != null) {
Variable gridMap = ds.findVariable(grid_mapping);
if (gridMap == null) {
Group g = v.getParentGroup(); // might be group reletive - CF does not specify
gridMap = g.findVariable(grid_mapping);
}
if (gridMap != null) {
gridMap.addAttribute(new Attribute(_Coordinate.TransformType, TransformType.Projection.toString()));
gridMap.addAttribute(new Attribute(_Coordinate.AxisTypes, "GeoX GeoY"));
got_grid_mapping = true;
}
}
}
if (!got_grid_mapping) { // see if there are any grid mappings anyway
for (Variable v : ds.getVariables()) {
String grid_mapping_name = ds.findAttValueIgnoreCase(v, CF.GRID_MAPPING_NAME, null);
if (grid_mapping_name != null) {
v.addAttribute(new Attribute(_Coordinate.TransformType, TransformType.Projection.toString()));
v.addAttribute(new Attribute(_Coordinate.AxisTypes, "GeoX GeoY"));
}
}
}
// make corrections for specific datasets
String src = ds.findAttValueIgnoreCase(null, "Source", "");
if (src.equals("NOAA/National Climatic Data Center")) {
String title = ds.findAttValueIgnoreCase(null, "title", "");
avhrr_oiv2 = title.indexOf("OI-V2") > 0;
}
ds.finish();
}
private boolean avhrr_oiv2 = false;
// this is here because it doesnt fit into 3D array thing.
private void makeAtmLnCoordinate(NetcdfDataset ds, Variable v) {
// get the formula attribute
String formula = ds.findAttValueIgnoreCase(v, "formula_terms", null);
if (null == formula) {
String msg = " Need attribute 'formula_terms' on Variable " + v.getName() + "\n";
parseInfo.format(msg);
userAdvice.format(msg);
return;
}
// parse the formula string
Variable p0Var = null, levelVar = null;
StringTokenizer stoke = new StringTokenizer(formula, " :");
while (stoke.hasMoreTokens()) {
String toke = stoke.nextToken();
if (toke.equalsIgnoreCase("p0")) {
String name = stoke.nextToken();
p0Var = ds.findVariable(name);
} else if (toke.equalsIgnoreCase("lev")) {
String name = stoke.nextToken();
levelVar = ds.findVariable(name);
}
}
if (null == p0Var) {
String msg = " Need p0:varName on Variable " + v.getName() + " formula_terms\n";
parseInfo.format(msg);
userAdvice.format(msg);
return;
}
if (null == levelVar) {
String msg = " Need lev:varName on Variable " + v.getName() + " formula_terms\n";
parseInfo.format(msg);
userAdvice.format(msg);
return;
}
String units = ds.findAttValueIgnoreCase(p0Var, "units", "hPa");
// create the data and the variable
try { // p(k) = p0 * exp(-lev(k))
double p0 = p0Var.readScalarDouble();
Array levelData = levelVar.read();
Array pressureData = Array.factory(double.class, levelData.getShape());
IndexIterator ii = levelData.getIndexIterator();
IndexIterator iip = pressureData.getIndexIterator();
while (ii.hasNext()) {
double val = p0 * Math.exp(-1.0 * ii.getDoubleNext());
iip.setDoubleNext(val);
}
CoordinateAxis1D p = new CoordinateAxis1D(ds, null, v.getShortName() + "_pressure", DataType.DOUBLE,
levelVar.getDimensionsString(), units,
"Vertical Pressure coordinate synthesized from atmosphere_ln_pressure_coordinate formula");
p.setCachedData(pressureData, false);
p.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Pressure.toString()));
p.addAttribute(new Attribute(_Coordinate.AliasForDimension, p.getDimensionsString()));
ds.addVariable(null, p);
//Dimension d = p.getDimension(0);
//d.addCoordinateVariable(p);
parseInfo.format(" added Vertical Pressure coordinate %s\n", p.getName());
} catch (IOException e) {
String msg = " Unable to read variables from " + v.getName() + " formula_terms\n";
parseInfo.format(msg);
userAdvice.format(msg);
}
}
/* vertical coordinate will be identifiable by:
1. units of pressure; or
2. the presence of the positive attribute with a value of up or down (case insensitive).
3. Optionally, the vertical type may be indicated additionally by providing the standard_name attribute with an appropriate value, and/or the axis attribute with the value Z.
*/
// we assume that coordinate axes get identified by
// 1) being coordinate variables or
// 2) being listed in coordinates attribute.
/**
* Augment CSM axis type identification with "projection_x_coordinate", "projection_y_coordinate"
* and the various dimensionless vertical coordinates
*/
protected AxisType getAxisType(NetcdfDataset ncDataset, VariableEnhanced v) {
String sname = ncDataset.findAttValueIgnoreCase((Variable) v, CF.STANDARD_NAME, null);
if (sname != null) {
sname = sname.trim();
if (sname.equalsIgnoreCase("latitude"))
return AxisType.Lat;
if (sname.equalsIgnoreCase("longitude"))
return AxisType.Lon;
if (sname.equalsIgnoreCase("projection_x_coordinate") || sname.equalsIgnoreCase("grid_longitude"))
return AxisType.GeoX;
if (sname.equalsIgnoreCase("projection_y_coordinate") || sname.equalsIgnoreCase("grid_latitude"))
return AxisType.GeoY;
for (String vertical_coord : vertical_coords)
if (sname.equalsIgnoreCase(vertical_coord))
return AxisType.GeoZ;
}
AxisType at = super.getAxisType(ncDataset, v);
// check axis attribute - only for X, Y, Z
if (at == null) {
String axis = ncDataset.findAttValueIgnoreCase((Variable) v, "axis", null);
if (axis != null) {
axis = axis.trim();
String unit = v.getUnitsString();
if (axis.equalsIgnoreCase("X")) {
if (SimpleUnit.isCompatible("m", unit))
return AxisType.GeoX;
} else if (axis.equalsIgnoreCase("Y")) {
if (SimpleUnit.isCompatible("m", unit))
return AxisType.GeoY;
} else if (axis.equalsIgnoreCase("Z")) {
if (unit == null) return AxisType.GeoZ;
if (SimpleUnit.isCompatible("m", unit))
return AxisType.Height;
else if (SimpleUnit.isCompatible("mbar", unit))
return AxisType.Pressure;
else
return AxisType.GeoZ;
}
}
}
if ((at == null) && avhrr_oiv2) {
if (v.getShortName().equals("zlev"))
return AxisType.Height;
}
return at;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy