ucar.nc2.internal.dataset.conv.DefaultConventions Maven / Gradle / Ivy
The newest version!
package ucar.nc2.internal.dataset.conv;
import java.io.IOException;
import java.util.HashMap;
import java.util.Optional;
import java.util.StringTokenizer;
import javax.annotation.Nullable;
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.Attribute;
import ucar.nc2.Dimension;
import ucar.nc2.NetcdfFile;
import ucar.nc2.Variable;
import ucar.nc2.constants.AxisType;
import ucar.nc2.constants.CF;
import ucar.nc2.constants._Coordinate;
import ucar.nc2.dataset.NetcdfDataset;
import ucar.nc2.dataset.ProjectionCT;
import ucar.nc2.dataset.VariableDS;
import ucar.nc2.internal.dataset.CoordSystemBuilder;
import ucar.nc2.dataset.spi.CoordSystemBuilderFactory;
import ucar.nc2.time.CalendarDate;
import ucar.nc2.units.SimpleUnit;
import ucar.nc2.util.CancelTask;
import ucar.unidata.geoloc.ProjectionImpl;
import ucar.unidata.geoloc.projection.LambertConformal;
import ucar.unidata.geoloc.projection.Stereographic;
import ucar.unidata.geoloc.projection.TransverseMercator;
/** Default Coordinate Conventions. Used when no other is specified or recognized. */
public class DefaultConventions extends CoordSystemBuilder {
private static final Logger logger = LoggerFactory.getLogger(ucar.nc2.dataset.conv.DefaultConvention.class);
protected ProjectionCT projCT;
private DefaultConventions(NetcdfDataset.Builder datasetBuilder) {
super(datasetBuilder);
this.conventionName = "Default";
}
@Override
public void augmentDataset(CancelTask cancelTask) {
projCT = makeProjectionCT();
if (projCT != null) {
VariableDS.Builder vb = makeCoordinateTransformVariable(projCT);
rootGroup.addVariable(vb);
String xname = findCoordinateName(AxisType.GeoX);
String yname = findCoordinateName(AxisType.GeoY);
if (xname != null && yname != null) {
vb.addAttribute(new Attribute(_Coordinate.Axes, xname + " " + yname));
}
}
}
@Override
protected void identifyCoordinateAxes() {
// Look for coord_axis or coord_alias attribute
for (VarProcess vp : varList) {
if (vp.isCoordinateVariable) {
continue;
}
String dimName = findAlias(vp.vb);
if (dimName.isEmpty()) {
continue;
}
Optional dimOpt = this.rootGroup.findDimension(dimName);
dimOpt.ifPresent(dime -> {
vp.isCoordinateAxis = true;
parseInfo.format(" Coordinate Axis added (alias) = %s for dimension %s%n", vp, dimName);
});
}
// coordinates is an alias for _CoordinateAxes
for (VarProcess vp : varList) {
if (vp.coordinateAxes == null) { // dont override if already set
String coordsString = vp.vb.getAttributeContainer().findAttributeString(CF.COORDINATES, null);
if (coordsString != null) {
vp.coordinates = coordsString;
}
}
}
super.identifyCoordinateAxes();
/////////////////////////
// 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(vp.vb);
if (atype != null) {
map.put(atype, vp);
}
}
}
// look for time axes based on units
if (map.get(AxisType.Time) == null) {
for (VarProcess vp : varList) {
String unit = vp.vb.getUnits();
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.vb.getFullName(), unit);
// break; // allow multiple time coords
}
}
}
// look for missing axes by using name hueristics
for (VarProcess vp : varList) {
if (vp.isCoordinateVariable) {
continue;
}
AxisType atype = getAxisType(vp.vb);
if (atype != null) {
if (map.get(atype) == null) {
vp.isCoordinateAxis = true;
parseInfo.format(" Coordinate Axis added (Default forced) = %s for axis %s%n", vp.vb.getFullName(), atype);
map.put(atype, vp);
}
}
}
}
/**
* look for aliases.
*
* @param axisType look for this axis type
* @return name of axis of that type
*/
@Nullable
private String findCoordinateName(AxisType axisType) {
for (Variable.Builder vb : rootGroup.vbuilders) {
if (vb instanceof VariableDS.Builder) {
VariableDS.Builder vds = (VariableDS.Builder) vb;
if (axisType == getAxisType(vds)) {
return vds.getFullName();
}
}
}
return null;
}
/*
* @Override
* protected void makeCoordinateTransforms() {
* if (projCT != null) {
* VarProcess vp = findVarProcess(projCT.getName(), null);
* if (vp != null) {
* vp.ct = projCT;
* }
* }
* super.makeCoordinateTransforms();
* }
*/
@Override
@Nullable
protected AxisType getAxisType(VariableDS.Builder vb) {
AxisType result = getAxisTypeCoards(vb);
if (result != null) {
return result;
}
String vname = vb.shortName;
if (vname == null) {
return null;
}
String unit = vb.getUnits();
if (unit == null) {
unit = "";
}
String desc = vb.getDescription();
if (desc == null) {
desc = "";
}
if (vname.equalsIgnoreCase("x") || findAlias(vb).equalsIgnoreCase("x")) {
return AxisType.GeoX;
}
if (vname.equalsIgnoreCase("lon") || vname.equalsIgnoreCase("longitude") || findAlias(vb).equalsIgnoreCase("lon")) {
return AxisType.Lon;
}
if (vname.equalsIgnoreCase("y") || findAlias(vb).equalsIgnoreCase("y")) {
return AxisType.GeoY;
}
if (vname.equalsIgnoreCase("lat") || vname.equalsIgnoreCase("latitude") || findAlias(vb).equalsIgnoreCase("lat")) {
return AxisType.Lat;
}
if (vname.equalsIgnoreCase("lev") || findAlias(vb).equalsIgnoreCase("lev")
|| (vname.equalsIgnoreCase("level") || findAlias(vb).equalsIgnoreCase("level"))) {
return AxisType.GeoZ;
}
if (vname.equalsIgnoreCase("z") || findAlias(vb).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;
}
logger.info("Unit '" + unit + "' is not compatible with meters for axis '" + vname + "'");
}
if (vname.equalsIgnoreCase("time") || findAlias(vb).equalsIgnoreCase("time")) {
if (SimpleUnit.isDateUnit(unit)) {
return AxisType.Time;
}
logger.info("Unit '" + unit + "' not recognized as a time unit for axis '" + vname + "'");
}
if (vname.equalsIgnoreCase("time") && vb.dataType == DataType.STRING) {
if (vb.orgVar != null) {
try {
Array firstValue = vb.orgVar.read("0");
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 LOOK
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(VariableDS.Builder vb) {
String alias = vb.getAttributeContainer().findAttributeString("coord_axis", null);
if (alias == null) {
alias = vb.getAttributeContainer().findAttributeString("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
@Nullable
private AxisType getAxisTypeCoards(VariableDS.Builder vb) {
String unit = vb.getUnits();
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 = vb.getAttributeContainer().findAttributeString("positive", null);
if (positive != null) {
if (SimpleUnit.isCompatible("m", unit)) {
return AxisType.Height;
} else {
return AxisType.GeoZ;
}
}
return null;
}
private ProjectionCT makeProjectionCT() {
// look for projection in global attribute
String projection = rootGroup.getAttributeContainer().findAttributeString("projection", null);
if (null == projection) {
parseInfo.format("Default Conventions error: NO projection name found %n");
return null;
}
String params = rootGroup.getAttributeContainer().findAttributeString("projection_params", null);
if (null == params) {
params = rootGroup.getAttributeContainer().findAttributeString("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);
}
public static class Factory implements CoordSystemBuilderFactory {
@Override
@Nullable
public String getConventionName() {
return null;
}
public boolean isMine(NetcdfFile ncfile) {
// this is to test DefaultConventions, not needed when we remove old convention builders.
// return ncfile.getLocation().endsWith("amsr-avhrr-v2.20040729.nc");
return false;
}
@Override
public CoordSystemBuilder open(NetcdfDataset.Builder datasetBuilder) {
return new DefaultConventions(datasetBuilder);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy