ucar.nc2.iosp.grid.GridVertCoord Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of netcdf Show documentation
Show all versions of netcdf Show documentation
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-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.grid;
import ucar.ma2.*;
import ucar.nc2.*;
import ucar.nc2.constants.AxisType;
import ucar.nc2.constants._Coordinate;
import ucar.nc2.units.SimpleUnit;
import java.util.*;
/**
* A Vertical Coordinate variable for a Grid variable.
*
* @author caron
*/
public class GridVertCoord implements Comparable {
static private org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(GridVertCoord.class);
/**
* typical record for this vertical coordinate
*/
protected GridRecord typicalRecord;
protected String levelName;
protected GridTableLookup lookup;
protected int seq = 0;
protected double[] coordValues = null;
protected boolean usesBounds = false;
protected boolean isVerticalCoordinate = false;
/**
* vertical pressure factors
*/
protected double[] factors = null;
/**
* positive direction
*/
private String positive = "up";
/**
* units
*/
protected String units;
/**
* levels
*/
protected List levels = new ArrayList(); // LevelCoord
/**
* Create a new GridVertCoord with the given name.
* Used by deprecated GridIndex2NC.makeDefinedCoord()
*
* @param name name
*/
protected GridVertCoord(String name) {
this.levelName = name;
}
/**
* Create a new GridVertCoord with the appropriate params
*
* @param records list of GridRecords that make up this coord
* @param levelName the name of the level
* @param lookup the lookup table
* @param hcs Horizontal coordinate
*/
protected GridVertCoord(List records, String levelName, GridTableLookup lookup, GridHorizCoordSys hcs) {
this.typicalRecord = records.get(0);
this.levelName = levelName;
this.lookup = lookup;
this.isVerticalCoordinate = lookup.isVerticalCoordinate(typicalRecord);
//isVerticalCoordinate = lookup.isVerticalCoordinate(typicalRecord);
positive = lookup.isPositiveUp(typicalRecord) ? "up" : "down";
units = lookup.getLevelUnit(typicalRecord);
usesBounds = lookup.isLayer(this.typicalRecord);
for (GridRecord record : records) {
if (coordIndex(record) < 0) {
levels.add(new LevelCoord(record.getLevel1(), record.getLevel2()));
/* check if assumption violated
if (!isVerticalCoordinate && (levels.size() > 1)) {
if (GridServiceProvider.debugVert) {
logger.warn( "GribCoordSys: unused level coordinate has > 1 levels = "
+ levelName + " " + record.getLevelType1() + " "
+ levels.size());
}
} */
}
}
Collections.sort(levels);
if (positive.equals("down")) {
Collections.reverse(levels);
}
if (GridServiceProvider.debugVert) {
System.out.println("GribVertCoord: " + getVariableName() + "("
+ typicalRecord.getLevelType1()
+ ") isVertDimensionUsed= " + isVertDimensionUsed()
+ " positive=" + positive + " units=" + units);
}
}
/*
* Create a new GridVertCoord for a layer
* Used by deprecated GridIndex2NC.makeDefinedCoord()
*
* @deprecated
* @param record layer record
* @param levelName name of this level
* @param lookup lookup table
* @param level1 level 1
* @param level2 level 2
*
GridVertCoord(GridRecord record, String levelName, GridTableLookup lookup, double[] level1, double[] level2) {
this.typicalRecord = record;
this.levelName = levelName;
this.lookup = lookup;
//dontUseVertical = !lookup.isVerticalCoordinate(record);
positive = lookup.isPositiveUp(record) ? "up" : "down";
units = lookup.getLevelUnit(record);
usesBounds = lookup.isLayer(this.typicalRecord);
levels = new ArrayList(level1.length);
for (int i = 0; i < level1.length; i++) {
levels.add(new LevelCoord(level1[i], (level2 == null) ? 0.0 : level2[i]));
}
Collections.sort(levels);
if (positive.equals("down")) {
Collections.reverse(levels);
}
//isVerticalCoordinate = (levels.size() > 1);
} */
/**
* Set the sequence number
*
* @param seq the sequence number
*/
void setSequence(int seq) {
this.seq = seq;
}
/**
* Set the level name
*
* @return the level name
*/
String getLevelName() {
return levelName;
}
/**
* Get the variable name
*
* @return the variable name
*/
public String getVariableName() {
return (seq == 0) ? levelName : levelName + seq; // more than one with same levelName
}
/**
* Get the number of levels
*
* @return number of levels
*/
int getNLevels() {
return levels.size();
}
/**
* vert coordinates are used when nlevels > 1, otherwise use isVerticalCoordinate
* @return if vert dimension should be used
*/
boolean isVertDimensionUsed() {
return (getNLevels() == 1) ? isVerticalCoordinate : true;
}
/**
* Match levels
*
* @param records records to match
* @return true if they have the same levels
*/
boolean matchLevels(List records) {
// first create a new list
List levelList = new ArrayList(records.size());
for (GridRecord record : records) {
LevelCoord lc = new LevelCoord(record.getLevel1(), record.getLevel2());
if (!levelList.contains(lc)) {
levelList.add(lc);
}
}
Collections.sort(levelList);
if (positive.equals("down")) {
Collections.reverse(levelList);
}
// gotta equal existing list
return levelList.equals(levels);
}
/**
* Add this coord as a dimension to the netCDF file
*
* @param ncfile file to add to
* @param g group in the file
*/
void addDimensionsToNetcdfFile(NetcdfFile ncfile, Group g) {
if (!isVertDimensionUsed())
return;
int nlevs = levels.size();
if ( coordValues != null )
nlevs = coordValues.length;
ncfile.addDimension(g, new Dimension(getVariableName(), nlevs, true));
}
protected String getLevelDesc() {
return lookup.getLevelDescription(typicalRecord);
}
protected void addExtraAttributes(Variable v) {
v.addAttribute(new Attribute("level_type", Integer.toString(typicalRecord.getLevelType1())));
}
/**
* Add this coord as a variable in the netCDF file
*
* @param ncfile netCDF file to add to
* @param g group in file
*/
void addToNetcdfFile(NetcdfFile ncfile, Group g) {
if (!isVertDimensionUsed()) {
typicalRecord = null; // allow gc
return;
}
if (g == null) {
g = ncfile.getRootGroup();
}
// coordinate axis
Variable v = new Variable(ncfile, g, null, getVariableName());
v.setDataType(DataType.DOUBLE);
String desc = getLevelDesc();
v.addAttribute(new Attribute("long_name", desc));
v.addAttribute(new Attribute("units", lookup.getLevelUnit(typicalRecord)));
// positive attribute needed for CF-1 Height and Pressure
if (positive != null) {
v.addAttribute(new Attribute("positive", positive));
}
if (units != null) {
AxisType axisType;
if (SimpleUnit.isCompatible("millibar", units)) {
axisType = AxisType.Pressure;
} else if (SimpleUnit.isCompatible("m", units)) {
axisType = AxisType.Height;
} else {
axisType = AxisType.GeoZ;
}
addExtraAttributes(v);
v.addAttribute(new Attribute(_Coordinate.AxisType, axisType.toString()));
}
if (coordValues == null) {
coordValues = new double[levels.size()];
for (int i = 0; i < levels.size(); i++) {
LevelCoord lc = (LevelCoord) levels.get(i);
coordValues[i] = lc.mid;
}
}
Array dataArray = Array.factory(DataType.DOUBLE, new int[]{coordValues.length}, coordValues);
v.setDimensions(getVariableName());
v.setCachedData(dataArray, true);
ncfile.addVariable(g, v);
if (usesBounds) {
Dimension bd = ucar.nc2.dataset.DatasetConstructor.getBoundsDimension(ncfile);
String bname = getVariableName() + "_bounds";
v.addAttribute(new Attribute("bounds", bname));
v.addAttribute(new Attribute(_Coordinate.ZisLayer, "true"));
Variable b = new Variable(ncfile, g, null, bname);
b.setDataType(DataType.DOUBLE);
b.setDimensions(getVariableName() + " " + bd.getShortName());
b.addAttribute(new Attribute("long_name",
"bounds for " + v.getFullName()));
b.addAttribute(new Attribute("units",
lookup.getLevelUnit(typicalRecord)));
Array boundsArray = Array.factory(DataType.DOUBLE,
new int[]{coordValues.length,
2});
ucar.ma2.Index ima = boundsArray.getIndex();
for (int i = 0; i < coordValues.length; i++) {
LevelCoord lc = (LevelCoord) levels.get(i);
boundsArray.setDouble(ima.set(i, 0), lc.value1);
boundsArray.setDouble(ima.set(i, 1), lc.value2);
}
b.setCachedData(boundsArray, true);
ncfile.addVariable(g, b);
}
if (factors != null) {
// check if already created
if (g == null) {
g = ncfile.getRootGroup();
}
if ( g.findVariable ( "hybrida" ) != null)
return ;
v.addAttribute(new Attribute("standard_name", "atmosphere_hybrid_sigma_pressure_coordinate" ));
v.addAttribute(new Attribute("formula_terms", "ap: hybrida b: hybridb ps: Pressure" ));
// create hybrid factor variables
// add hybrida variable
Variable ha = new Variable(ncfile, g, null, "hybrida");
ha.setDataType(DataType.DOUBLE);
ha.addAttribute(new Attribute("long_name", "level_a_factor" ));
ha.addAttribute(new Attribute("units", ""));
ha.setDimensions(getVariableName());
// add data
int middle = factors.length / 2;
double[] adata;
double[] bdata;
if( levels.size() < middle ) { // only partial data wanted
adata = new double[ levels.size() ];
bdata = new double[ levels.size() ];
} else {
adata = new double[ middle ];
bdata = new double[ middle ];
}
for( int i = 0; i < middle && i < levels.size(); i++ )
adata[ i ] = factors[ i ];
Array haArray = Array.factory(DataType.DOUBLE, new int[]{adata.length},adata);
ha.setCachedData(haArray, true);
ncfile.addVariable(g, ha);
// add hybridb variable
Variable hb = new Variable(ncfile, g, null, "hybridb");
hb.setDataType(DataType.DOUBLE);
hb.addAttribute(new Attribute("long_name", "level_b_factor" ));
hb.addAttribute(new Attribute("units", ""));
hb.setDimensions(getVariableName());
// add data
for( int i = 0; i < middle && i < levels.size(); i++ )
bdata[ i ] = factors[ i + middle ];
Array hbArray = Array.factory(DataType.DOUBLE, new int[]{bdata.length},bdata);
hb.setCachedData(hbArray, true);
ncfile.addVariable(g, hb);
/* // TODO: delete next time modifying code
double[] adata = new double[ middle ];
for( int i = 0; i < middle; i++ )
adata[ i ] = factors[ i ];
Array haArray = Array.factory(DataType.DOUBLE, new int[]{adata.length}, adata);
ha.setCachedData(haArray, true);
ncfile.addVariable(g, ha);
// add hybridb variable
Variable hb = new Variable(ncfile, g, null, "hybridb");
hb.setDataType(DataType.DOUBLE);
hb.addAttribute(new Attribute("long_name", "level_b_factor" ));
//hb.addAttribute(new Attribute("standard_name", "atmosphere_hybrid_sigma_pressure_coordinate" ));
hb.addAttribute(new Attribute("units", ""));
hb.setDimensions(getVariableName());
// add data
double[] bdata = new double[ middle ];
for( int i = 0; i < middle; i++ )
bdata[ i ] = factors[ i + middle ];
Array hbArray = Array.factory(DataType.DOUBLE, new int[]{bdata.length}, bdata);
hb.setCachedData(hbArray, true);
ncfile.addVariable(g, hb);
*/
}
// allow gc
// typicalRecord = null;
}
/**
* Get the index of the particular record
*
* @param record record in question
* @return the index or -1 if not found
*/
int getIndex(GridRecord record) {
if (!isVertDimensionUsed())
return 0;
return coordIndex(record);
}
/**
* Compare this to another
*
* @param gv the other GridVertCoord
* @return the comparison
*/
public int compareTo(GridVertCoord gv) {
return getLevelName().compareToIgnoreCase(gv.getLevelName());
}
public double getCoord(int i) {
return (coordValues == null) ? 0.0 : coordValues[i];
}
/**
* A level coordinate
*/
protected class LevelCoord implements Comparable {
/**
* midpoint
*/
public double mid;
/**
* top/bottom values
*/
public double value1, value2;
/**
* Create a new LevelCoord
*
* @param value1 top
* @param value2 bottom
*/
LevelCoord(double value1, double value2) {
this.value1 = value1;
this.value2 = value2;
if (usesBounds && (value1 > value2)) {
this.value1 = value2;
this.value2 = value1;
}
mid = usesBounds ? (value1 + value2) / 2 : value1;
}
/**
* Compare to another LevelCoord
*
* @param o another LevelCoord
* @return the comparison
*/
public int compareTo(Object o) {
LevelCoord other = (LevelCoord) o;
// if (closeEnough(value1, other.value1) && closeEnough(value2, other.value2)) return 0;
if (mid < other.mid) {
return -1;
}
if (mid > other.mid) {
return 1;
}
return 0;
}
/**
* Check for equality
*
* @param oo object in question
* @return true if equal
*/
public boolean equals(Object oo) {
if (this == oo) {
return true;
}
if (!(oo instanceof LevelCoord)) {
return false;
}
LevelCoord other = (LevelCoord) oo;
return (ucar.nc2.util.Misc.closeEnough(value1, other.value1)
&& ucar.nc2.util.Misc.closeEnough(value2, other.value2));
}
/**
* Generate a hashcode
*
* @return the hashcode
*/
public int hashCode() {
return (int) (value1 * 100000 + value2 * 100);
}
}
/**
* Get the coordinate index for the record
*
* @param record record in question
* @return index or -1 if not found
*/
private int coordIndex(GridRecord record) {
double val = record.getLevel1();
double val2 = record.getLevel2();
if (usesBounds && (val > val2)) {
val = record.getLevel2();
val2 = record.getLevel1();
}
for (int i = 0; i < levels.size(); i++) {
LevelCoord lc = (LevelCoord) levels.get(i);
if (usesBounds) {
if (ucar.nc2.util.Misc.closeEnough(lc.value1, val) && ucar.nc2.util.Misc.closeEnough(lc.value2, val2)) {
return i;
}
} else {
if (ucar.nc2.util.Misc.closeEnough(lc.value1, val)) {
return i;
}
}
}
return -1;
}
@Override
public String toString() {
return "GridVertCoord{" +
"levelName='" + levelName + '\'' +
", seq=" + seq +
'}';
}
}