ucar.nc2.dataset.conv.WRFConvention Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
* See LICENSE for license information.
*/
package ucar.nc2.dataset.conv;
import ucar.ma2.*;
import ucar.nc2.*;
import ucar.nc2.constants.CDM;
import ucar.nc2.constants.CF;
import ucar.nc2.constants._Coordinate;
import ucar.nc2.constants.AxisType;
import ucar.nc2.time.*;
import ucar.nc2.units.SimpleUnit;
import ucar.nc2.util.CancelTask;
import ucar.nc2.dataset.*;
import ucar.nc2.dataset.transform.WRFEtaTransformBuilder;
import ucar.unidata.geoloc.*;
import ucar.unidata.geoloc.projection.*;
import ucar.unidata.util.StringUtil2;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* WRF netcdf output files.
*
* Note: Apparently WRF netcdf files before version 2 didnt output the projection origin, so
* we cant properly georeference them.
*
* This Convention currently only supports ARW output, identified as DYN_OPT=2 or GRIDTYPE=C
*
* @author caron
*/
public class WRFConvention extends CoordSysBuilder {
// static private java.text.SimpleDateFormat dateFormat;
// static {
// dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
// dateFormat.setTimeZone(java.util.TimeZone.getTimeZone("GMT"));
//}
public static boolean isMine(NetcdfFile ncfile) {
if (null == ncfile.findDimension("south_north")) return false;
// ARW only
Attribute att = ncfile.findGlobalAttribute("DYN_OPT");
if (att != null) {
if (att.getNumericValue().intValue() != 2) return false;
} else {
att = ncfile.findGlobalAttribute("GRIDTYPE");
if (att != null) {
if (!att.getStringValue().equalsIgnoreCase("C") &&
!att.getStringValue().equalsIgnoreCase("E"))
return false;
}
}
att = ncfile.findGlobalAttribute("MAP_PROJ");
return att != null;
}
private double centerX = 0.0, centerY = 0.0;
private ProjectionCT projCT = null;
private boolean gridE = false;
public WRFConvention() {
this.conventionName = "WRF";
}
/* Implementation notes
There are 2 WRFs : NMM ("Non-hydrostatic Mesoscale Model" developed at NOAA/NCEP) and
1) NMM ("Non-hydrostatic Mesoscale Model" developed at NOAA/NCEP)
GRIDTYPE="E"
DYN_OPT = 4
This is a staggered grid that requires special processing.
2) ARW ("Advanced Research WRF", developed at MMM)
GRIDTYPE="C"
DYN_OPT = 2
DX, DY grid spaceing in meters (must be equal)
the Arakawa C staggered grid (see ARW 2.2 p 3-17)
the + are the "non-staggered" grid:
+ V + V + V +
U T U T U T U
+ V + V + V +
U T U T U T U
+ V + V + V +
U T U T U T U
+ V + V + V +
*/
/* ARW Users Guide p 3-19
7. MAP_PROJ_NAME: Character string specifying type of map projection. Valid entries are:
"polar" -> Polar stereographic
"lambert" -> Lambert conformal (secant and tangent)
"mercator" -> Mercator
8. MOAD_KNOWN_LAT/MOAD_KNOWN_LON (= CEN_LAT, CEN_LON):
Real latitude and longitude of the center point in the grid.
9. MOAD_STAND_LATS (= TRUE_LAT1/2):
2 real values for the "true" latitudes (where grid spacing is exact).
Must be between -90 and 90, and the values selected depend on projection:
Polar-stereographic: First value must be the latitude at which the grid
spacing is true. Most users will set this equal to their center latitude.
Second value must be +/-90. for NH/SH grids.
Lambert Conformal: Both values should have the same sign as the center
latitude. For a tangential lambert conformal, set both to the same value
(often equal to the center latitude). For a secant Lambert Conformal, they
may be set to different values.
Mercator: The first value should be set to the latitude you wish your grid
spacing to be true (often your center latitude). Second value is not used.
10. MOAD_STAND_LONS (=STAND_LON): This is one entry specifying the longitude in degrees East (-
180->180) that is parallel to the y-axis of your grid, (sometimes referred to as the
orientation of the grid). This should be set equal to the center longitude in most cases.
---------------------------------
version 3.1
http://www.mmm.ucar.edu/wrf/users/docs/user_guide_V3.1/users_guide_chap5.htm
The definition for map projection options:
map_proj = 1: Lambert Conformal
2: Polar Stereographic
3: Mercator
6: latitude and longitude (including global)
*/
public void augmentDataset(NetcdfDataset ds, CancelTask cancelTask) {
if (null != ds.findVariable("x")) return; // check if its already been done - aggregating enhanced datasets.
Attribute att = ds.findGlobalAttribute("GRIDTYPE");
gridE = att != null && att.getStringValue().equalsIgnoreCase("E");
// kludge in fixing the units
List vlist = ds.getVariables();
for (Variable v : vlist) {
att = v.findAttributeIgnoreCase(CDM.UNITS);
if (att != null) {
String units = att.getStringValue();
if (units != null)
v.addAttribute(new Attribute(CDM.UNITS, normalize(units))); // removes the old
}
}
// make projection transform
att = ds.findGlobalAttribute("MAP_PROJ");
int projType = att.getNumericValue().intValue();
boolean isLatLon = false;
if (projType == 203) {
/* centerX = centralLon;
centerY = centralLat;
ds.addCoordinateAxis( makeLonCoordAxis( ds, "longitude", ds.findDimension("west_east")));
ds.addCoordinateAxis( makeLatCoordAxis( ds, "latitude", ds.findDimension("south_north"))); */
Variable glat = ds.findVariable("GLAT");
if (glat == null) {
parseInfo.format("Projection type 203 - expected GLAT variable not found%n");
} else {
glat.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lat.toString()));
if (gridE) glat.addAttribute(new Attribute(_Coordinate.Stagger, CDM.ARAKAWA_E));
glat.setDimensions("south_north west_east");
glat.setCachedData(convertToDegrees(glat), false);
glat.addAttribute(new Attribute(CDM.UNITS, CDM.LAT_UNITS));
}
Variable glon = ds.findVariable("GLON");
if (glon == null) {
parseInfo.format("Projection type 203 - expected GLON variable not found%n");
} else {
glon.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lon.toString()));
if (gridE) glon.addAttribute(new Attribute(_Coordinate.Stagger, CDM.ARAKAWA_E));
glon.setDimensions("south_north west_east");
glon.setCachedData(convertToDegrees(glon), false);
glon.addAttribute(new Attribute(CDM.UNITS, CDM.LON_UNITS));
}
VariableDS v = new VariableDS(ds, null, null, "LatLonCoordSys", DataType.CHAR, "", null, null);
v.addAttribute(new Attribute(_Coordinate.Axes, "GLAT GLON Time"));
Array data = Array.factory(DataType.CHAR, new int[]{}, new char[]{' '});
v.setCachedData(data, true);
ds.addVariable(null, v);
Variable dataVar = ds.findVariable("LANDMASK");
dataVar.addAttribute(new Attribute(_Coordinate.Systems, "LatLonCoordSys"));
} else {
double lat1 = findAttributeDouble(ds, "TRUELAT1");
double lat2 = findAttributeDouble(ds, "TRUELAT2");
double centralLat = findAttributeDouble(ds, "CEN_LAT"); // center of grid
double centralLon = findAttributeDouble(ds, "CEN_LON"); // center of grid
double standardLon = findAttributeDouble(ds, "STAND_LON"); // true longitude
double standardLat = findAttributeDouble(ds, "MOAD_CEN_LAT");
ProjectionImpl proj = null;
switch (projType) {
case 0: // for diagnostic runs with no georeferencing
proj = new FlatEarth();
projCT = new ProjectionCT("flat_earth", "FGDC", proj);
// System.out.println(" using LC "+proj.paramsToString());
break;
case 1:
proj = new LambertConformal(standardLat, standardLon, lat1, lat2, 0.0, 0.0, 6370);
projCT = new ProjectionCT("Lambert", "FGDC", proj);
// System.out.println(" using LC "+proj.paramsToString());
break;
case 2:
// Thanks to Heiko Klein for figuring out WRF Stereographic
double lon0 = (Double.isNaN(standardLon)) ? centralLon : standardLon;
double lat0 = (Double.isNaN(centralLat)) ? lat2 : centralLat; // ?? 7/20/2010
double scaleFactor = (1 + Math.abs( Math.sin(Math.toRadians(lat1)))) / 2.; // R Schmunk 9/10/07
// proj = new Stereographic(lat2, lon0, scaleFactor);
proj = new Stereographic(lat0, lon0, scaleFactor, 0.0, 0.0, 6370);
projCT = new ProjectionCT("Stereographic", "FGDC", proj);
break;
case 3:
proj = new Mercator(standardLon, lat1, 0.0, 0.0, 6370); // thanks to Robert Schmunk with edits for non-MOAD grids
projCT = new ProjectionCT("Mercator", "FGDC", proj);
// proj = new TransverseMercator(standardLat, standardLon, 1.0);
//projCT = new ProjectionCT("TransverseMercator", "FGDC", proj);
break;
case 6:
// version 3 "lat-lon", including global
// http://www.mmm.ucar.edu/wrf/users/workshops/WS2008/presentations/1-2.pdf
// use 2D XLAT, XLONG
isLatLon = true;
for (Variable v : vlist) {
if (v.getShortName().startsWith("XLAT")) {
v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lat.toString()));
removeConstantTimeDim(ds, v);
} else if (v.getShortName().startsWith("XLONG")) {
v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lon.toString()));
removeConstantTimeDim(ds, v);
} else if (v.getShortName().equals("T")) { // ANOTHER MAJOR KLUDGE to pick up 4D fields
v.addAttribute(new Attribute(_Coordinate.Axes, "Time XLAT XLONG z"));
} else if (v.getShortName().equals("U")) {
v.addAttribute(new Attribute(_Coordinate.Axes, "Time XLAT_U XLONG_U z"));
} else if (v.getShortName().equals("V")) {
v.addAttribute(new Attribute(_Coordinate.Axes, "Time XLAT_V XLONG_V z"));
} else if (v.getShortName().equals("W")) {
v.addAttribute(new Attribute(_Coordinate.Axes, "Time XLAT XLONG z_stag"));
}
}
break;
default:
parseInfo.format("ERROR: unknown projection type = %s%n", projType);
break;
}
if (proj != null) {
LatLonPointImpl lpt1 = new LatLonPointImpl(centralLat, centralLon); // center of the grid
ProjectionPoint ppt1 = proj.latLonToProj(lpt1, new ProjectionPointImpl());
centerX = ppt1.getX();
centerY = ppt1.getY();
if (debug) {
System.out.println("centerX=" + centerX);
System.out.println("centerY=" + centerY);
}
}
// make axes
if (!isLatLon) {
ds.addCoordinateAxis(makeXCoordAxis(ds, "x", ds.findDimension("west_east")));
ds.addCoordinateAxis(makeXCoordAxis(ds, "x_stag", ds.findDimension("west_east_stag")));
ds.addCoordinateAxis(makeYCoordAxis(ds, "y", ds.findDimension("south_north")));
ds.addCoordinateAxis(makeYCoordAxis(ds, "y_stag", ds.findDimension("south_north_stag")));
}
ds.addCoordinateAxis(makeZCoordAxis(ds, "z", ds.findDimension("bottom_top")));
ds.addCoordinateAxis(makeZCoordAxis(ds, "z_stag", ds.findDimension("bottom_top_stag")));
if (projCT != null) {
VariableDS v = makeCoordinateTransformVariable(ds, projCT);
v.addAttribute(new Attribute(_Coordinate.AxisTypes, "GeoX GeoY"));
if (gridE) v.addAttribute(new Attribute(_Coordinate.Stagger, CDM.ARAKAWA_E));
ds.addVariable(null, v);
}
}
// time coordinate variations
if (ds.findVariable("Time") == null) { // Can skip this if its already there, eg from NcML
CoordinateAxis taxis = makeTimeCoordAxis(ds, "Time", ds.findDimension("Time"));
if (taxis == null)
taxis = makeTimeCoordAxis(ds, "Time", ds.findDimension("Times"));
if (taxis != null)
ds.addCoordinateAxis(taxis);
}
ds.addCoordinateAxis(makeSoilDepthCoordAxis(ds, "ZS"));
ds.finish();
}
private void removeConstantTimeDim(NetcdfDataset ds, Variable v) {
int[] shape = v.getShape();
if (v.getRank() == 3 && shape[0] == 1) { // remove time dependcies - MAJOR KLUDGE
Variable view;
try {
view = v.slice(0, 0);
} catch (InvalidRangeException e) {
log.error("Cant remove first dimension in variable {}", v);
return;
}
ds.removeVariable(null, v.getShortName());
ds.addVariable(null, view);
}
}
private Array convertToDegrees(Variable v) {
Array data;
try {
data = v.read();
data = data.reduce();
} catch (IOException ioe) {
throw new RuntimeException("data read failed on " + v.getFullName() + "=" + ioe.getMessage());
}
IndexIterator ii = data.getIndexIterator();
while (ii.hasNext()) {
ii.setDoubleCurrent(Math.toDegrees(ii.getDoubleNext()));
}
return data;
}
// pretty much WRF specific
private String normalize(String units) {
switch (units) {
case "fraction":
units = "";
break;
case "dimensionless":
units = "";
break;
case "NA":
units = "";
break;
case "-":
units = "";
break;
default:
units = StringUtil2.substitute(units, "**", "^");
units = StringUtil2.remove(units, '}');
units = StringUtil2.remove(units, '{');
break;
}
return units;
}
/////////////////////////////////////////////////////////////////////////
protected void makeCoordinateTransforms(NetcdfDataset ds) {
if (projCT != null) {
VarProcess vp = findVarProcess(projCT.getName(), null);
vp.isCoordinateTransform = true;
vp.ct = projCT;
}
super.makeCoordinateTransforms(ds);
}
protected AxisType getAxisType(NetcdfDataset ds, VariableEnhanced ve) {
Variable v = (Variable) ve;
String vname = v.getShortName();
if (vname.equalsIgnoreCase("x") || vname.equalsIgnoreCase("x_stag"))
return AxisType.GeoX;
if (vname.equalsIgnoreCase("lon"))
return AxisType.Lon;
if (vname.equalsIgnoreCase("y") || vname.equalsIgnoreCase("y_stag"))
return AxisType.GeoY;
if (vname.equalsIgnoreCase("lat"))
return AxisType.Lat;
if (vname.equalsIgnoreCase("z") || vname.equalsIgnoreCase("z_stag"))
return AxisType.GeoZ;
if (vname.equalsIgnoreCase("Z"))
return AxisType.Height;
if (vname.equalsIgnoreCase("time") || vname.equalsIgnoreCase("times"))
return AxisType.Time;
String unit = ve.getUnitsString();
if (unit != null) {
if (SimpleUnit.isCompatible("millibar", unit))
return AxisType.Pressure;
if (SimpleUnit.isCompatible("m", unit))
return AxisType.Height;
}
return null;
}
/**
* Does increasing values of Z go vertical up?
*
* @param v for thsi axis
* @return "up" if this is a Vertical (z) coordinate axis which goes up as coords get bigger,
* else return "down"
*/
public String getZisPositive(CoordinateAxis v) {
return "down"; //eta coords decrease upward
}
//////////////////////////////////////////////////////////////////////////////////////////////
private CoordinateAxis makeLonCoordAxis(NetcdfDataset ds, String axisName, Dimension dim) {
if (dim == null) return null;
double dx = 4 * findAttributeDouble(ds, "DX");
int nx = dim.getLength();
double startx = centerX - dx * (nx - 1) / 2;
CoordinateAxis v = new CoordinateAxis1D(ds, null, axisName, DataType.DOUBLE, dim.getShortName(), "degrees_east", "synthesized longitude coordinate");
v.setValues(nx, startx, dx);
v.addAttribute(new Attribute(_Coordinate.AxisType, "Lon"));
if (!axisName.equals(dim.getShortName()))
v.addAttribute(new Attribute(_Coordinate.AliasForDimension, dim.getShortName()));
return v;
}
private CoordinateAxis makeLatCoordAxis(NetcdfDataset ds, String axisName, Dimension dim) {
if (dim == null) return null;
double dy = findAttributeDouble(ds, "DY");
int ny = dim.getLength();
double starty = centerY - dy * (ny - 1) / 2;
CoordinateAxis v = new CoordinateAxis1D(ds, null, axisName, DataType.DOUBLE, dim.getShortName(), "degrees_north", "synthesized latitude coordinate");
v.setValues( ny, starty, dy);
v.addAttribute(new Attribute(_Coordinate.AxisType, "Lat"));
if (!axisName.equals(dim.getShortName()))
v.addAttribute(new Attribute(_Coordinate.AliasForDimension, dim.getShortName()));
return v;
}
private CoordinateAxis makeXCoordAxis(NetcdfDataset ds, String axisName, Dimension dim) {
if (dim == null) return null;
double dx = findAttributeDouble(ds, "DX") / 1000.0; // km ya just gotta know
int nx = dim.getLength();
double startx = centerX - dx * (nx - 1) / 2; // ya just gotta know
//System.out.println(" originX= "+originX+" startx= "+startx);
CoordinateAxis v = new CoordinateAxis1D(ds, null, axisName, DataType.DOUBLE, dim.getShortName(), "km", "synthesized GeoX coordinate from DX attribute");
v.setValues(nx, startx, dx);
v.addAttribute(new Attribute(_Coordinate.AxisType, "GeoX"));
if (!axisName.equals(dim.getShortName()))
v.addAttribute(new Attribute(_Coordinate.AliasForDimension, dim.getShortName()));
if (gridE) v.addAttribute(new Attribute(_Coordinate.Stagger, CDM.ARAKAWA_E));
return v;
}
private CoordinateAxis makeYCoordAxis(NetcdfDataset ds, String axisName, Dimension dim) {
if (dim == null) return null;
double dy = findAttributeDouble(ds, "DY") / 1000.0;
int ny = dim.getLength();
double starty = centerY - dy * (ny - 1) / 2; // - dy/2; // ya just gotta know
//System.out.println(" originY= "+originY+" starty= "+starty);
CoordinateAxis v = new CoordinateAxis1D(ds, null, axisName, DataType.DOUBLE, dim.getShortName(), "km", "synthesized GeoY coordinate from DY attribute");
v.setValues(ny, starty, dy);
v.addAttribute(new Attribute(_Coordinate.AxisType, "GeoY"));
if (!axisName.equals(dim.getShortName()))
v.addAttribute(new Attribute(_Coordinate.AliasForDimension, dim.getShortName()));
if (gridE) v.addAttribute(new Attribute(_Coordinate.Stagger, CDM.ARAKAWA_E));
return v;
}
private CoordinateAxis makeZCoordAxis(NetcdfDataset ds, String axisName, Dimension dim) {
if (dim == null) return null;
String fromWhere = axisName.endsWith("stag") ? "ZNW" : "ZNU";
CoordinateAxis v = new CoordinateAxis1D(ds, null, axisName, DataType.DOUBLE, dim.getShortName(), "", "eta values from variable " + fromWhere);
v.addAttribute(new Attribute(CF.POSITIVE, CF.POSITIVE_DOWN)); // eta coordinate is 1.0 at bottom, 0 at top
v.addAttribute(new Attribute(_Coordinate.AxisType, "GeoZ"));
if (!axisName.equals(dim.getShortName()))
v.addAttribute(new Attribute(_Coordinate.AliasForDimension, dim.getShortName()));
// create eta values from file variables: ZNU, ZNW
// But they are a function of time though the values are the same in the sample file
// NOTE: Use first time sample assuming all are the same!
Variable etaVar = ds.findVariable(fromWhere);
if (etaVar == null) return makeFakeCoordAxis(ds, axisName, dim);
int n = etaVar.getShape(1); //number of eta levels
int[] origin = new int[]{0, 0};
int[] shape = new int[]{1, n};
try {
Array array = etaVar.read(origin, shape);//read first time slice
ArrayDouble.D1 newArray = new ArrayDouble.D1(n);
IndexIterator it = array.getIndexIterator();
int count = 0;
while (it.hasNext()) {
double d = it.getDoubleNext();
newArray.set(count++, d);
}
v.setCachedData(newArray, true);
} catch (Exception e) {
e.printStackTrace();
}//ADD: error?
return v;
}
private CoordinateAxis makeFakeCoordAxis(NetcdfDataset ds, String axisName, Dimension dim) {
if (dim == null) return null;
CoordinateAxis v = new CoordinateAxis1D(ds, null, axisName, DataType.SHORT, dim.getShortName(), "", "synthesized coordinate: only an index");
v.addAttribute(new Attribute(_Coordinate.AxisType, "GeoZ"));
if (!axisName.equals(dim.getShortName()))
v.addAttribute(new Attribute(_Coordinate.AliasForDimension, dim.getShortName()));
v.setValues(dim.getLength(), 0, 1);
return v;
}
private CoordinateAxis makeTimeCoordAxis(NetcdfDataset ds, String axisName, Dimension dim) {
if (dim == null) return null;
int nt = dim.getLength();
Variable timeV = ds.findVariable("Times");
if (timeV == null) return null;
Array timeData;
try {
timeData = timeV.read();
} catch (IOException ioe) {
return null;
}
ArrayDouble.D1 values = new ArrayDouble.D1(nt);
int count = 0;
if (timeData instanceof ArrayChar) {
ArrayChar.StringIterator iter = ((ArrayChar) timeData).getStringIterator();
String testTimeStr = ((ArrayChar) timeData).getString(0);
boolean isCanonicalIsoStr = true;
// Maybe too specific to require WRF to give 10 digits or
// dashes for the date (e.g. yyyy-mm-dd)?
final String wrfDateWithUnderscore = "([\\-\\d]{10})_";
final Pattern wrfDateWithUnderscorePattern = Pattern.compile(wrfDateWithUnderscore);
Matcher m = wrfDateWithUnderscorePattern.matcher(testTimeStr);
if (wrfDateWithUnderscorePattern.matcher(testTimeStr) != null) {
isCanonicalIsoStr = false;
}
while (iter.hasNext()) {
String dateS = iter.next();
try {
CalendarDate cd;
if (isCanonicalIsoStr) {
cd = CalendarDateFormatter.isoStringToCalendarDate(null, dateS);
} else {
cd = CalendarDateFormatter.isoStringToCalendarDate(null, dateS.replaceFirst("_", "T"));
}
values.set(count++, (double) cd.getMillis() / 1000);
} catch (IllegalArgumentException e) {
parseInfo.format("ERROR: cant parse Time string = <%s> err= %s%n", dateS, e.getMessage());
// one more try
String startAtt = ds.findAttValueIgnoreCase(null, "START_DATE", null);
if ((nt == 1) && (null != startAtt)) {
try {
CalendarDate cd = CalendarDateFormatter.isoStringToCalendarDate(null, startAtt);
values.set(0, (double) cd.getMillis() / 1000);
} catch (IllegalArgumentException e2) {
parseInfo.format("ERROR: cant parse global attribute START_DATE = <%s> err=%s%n", startAtt, e2.getMessage());
}
}
}
}
} else {
IndexIterator iter = timeData.getIndexIterator();
while (iter.hasNext()) {
String dateS = (String) iter.next();
try {
CalendarDate cd = CalendarDateFormatter.isoStringToCalendarDate(null, dateS);
values.set(count++, (double) cd.getMillis() / 1000);
} catch (IllegalArgumentException e) {
parseInfo.format("ERROR: cant parse Time string = %s%n", dateS);
}
}
}
CoordinateAxis v = new CoordinateAxis1D(ds, null, axisName, DataType.DOUBLE, dim.getShortName(),
"secs since 1970-01-01 00:00:00", "synthesized time coordinate from Times(time)");
v.addAttribute(new Attribute(_Coordinate.AxisType, "Time"));
if (!axisName.equals(dim.getShortName()))
v.addAttribute(new Attribute(_Coordinate.AliasForDimension, dim.getShortName()));
v.setCachedData(values, true);
return v;
}
private VariableDS makeSoilDepthCoordAxis(NetcdfDataset ds, String coordVarName) {
Variable coordVar = ds.findVariable(coordVarName);
if (null == coordVar)
return null;
Dimension soilDim = null;
List dims = coordVar.getDimensions();
for (Dimension d : dims) {
if (d.getShortName().startsWith("soil_layers"))
soilDim = d;
}
if (null == soilDim)
return null;
if (coordVar.getRank() == 1) {
coordVar.addAttribute(new Attribute(CF.POSITIVE, CF.POSITIVE_DOWN)); // soil depth gets larger as you go down
coordVar.addAttribute(new Attribute(_Coordinate.AxisType, "GeoZ"));
if (!coordVarName.equals(soilDim.getShortName()))
coordVar.addAttribute(new Attribute(_Coordinate.AliasForDimension, soilDim.getShortName()));
return (VariableDS) coordVar;
}
String units = ds.findAttValueIgnoreCase(coordVar, CDM.UNITS, "");
CoordinateAxis v = new CoordinateAxis1D(ds, null, "soilDepth", DataType.DOUBLE, soilDim.getShortName(), units, "soil depth");
v.addAttribute(new Attribute(CF.POSITIVE, CF.POSITIVE_DOWN)); // soil depth gets larger as you go down
v.addAttribute(new Attribute(_Coordinate.AxisType, "GeoZ"));
v.addAttribute(new Attribute(CDM.UNITS, CDM.UNITS));
if (!v.getShortName().equals(soilDim.getShortName()))
v.addAttribute(new Attribute(_Coordinate.AliasForDimension, soilDim.getShortName()));
//read first time slice
int n = coordVar.getShape(1);
int[] origin = new int[]{0, 0};
int[] shape = new int[]{1, n};
try {
Array array = coordVar.read(origin, shape);
ArrayDouble.D1 newArray = new ArrayDouble.D1(n);
IndexIterator it = array.getIndexIterator();
int count = 0;
while (it.hasNext()) {
double d = it.getDoubleNext();
newArray.set(count++, d);
}
v.setCachedData(newArray, true);
} catch (Exception e) {
e.printStackTrace();
}
return v;
}
private double findAttributeDouble(NetcdfDataset ds, String attname) {
Attribute att = ds.findGlobalAttributeIgnoreCase(attname);
if (att == null) return Double.NaN;
return att.getNumericValue().doubleValue();
}
/**
* Assign CoordinateTransform objects to Coordinate Systems.
*/
protected void assignCoordinateTransforms(NetcdfDataset ncDataset) {
super.assignCoordinateTransforms(ncDataset);
// any cs whose got a vertical coordinate with no units
List csys = ncDataset.getCoordinateSystems();
for (CoordinateSystem cs : csys) {
if (cs.getZaxis() != null) {
String units = cs.getZaxis().getUnitsString();
if ((units == null) || (units.trim().length() == 0)) {
VerticalCT vct = makeWRFEtaVerticalCoordinateTransform(ncDataset, cs);
if (vct != null)
cs.addCoordinateTransform(vct);
parseInfo.format("***Added WRFEta verticalCoordinateTransform to %s%n", cs.getName());
}
}
}
}
private VerticalCT makeWRFEtaVerticalCoordinateTransform(NetcdfDataset ds, CoordinateSystem cs) {
if ((null == ds.findVariable("PH")) || (null == ds.findVariable("PHB")) ||
(null == ds.findVariable("P")) || (null == ds.findVariable("PB")))
return null;
WRFEtaTransformBuilder builder = new WRFEtaTransformBuilder(cs);
return builder.makeCoordinateTransform(ds, null);
}
}
/*
NMM . output from gridgen. these are the input files to WRF i think
netcdf Q:/grid/netcdf/wrf/geo_nmm.d01.nc {
dimensions:
Time = UNLIMITED; // (1 currently)
DateStrLen = 19;
west_east = 136;
south_north = 309;
land_cat = 24;
soil_cat = 16;
month = 12;
variables:
char Times(Time=1, DateStrLen=19);
float XLAT_M(Time=1, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XY ";
:units = "degrees latitude";
:description = "Latitude on mass grid";
:stagger = "M";
float XLONG_M(Time=1, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XY ";
:units = "degrees longitude";
:description = "Longitude on mass grid";
:stagger = "M";
float XLAT_V(Time=1, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XY ";
:units = "degrees latitude";
:description = "Latitude on velocity grid";
:stagger = "V";
float XLONG_V(Time=1, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XY ";
:units = "degrees longitude";
:description = "Longitude on velocity grid";
:stagger = "V";
float E(Time=1, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XY ";
:units = "-";
:description = "Coriolis E parameter";
:stagger = "M";
float F(Time=1, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XY ";
:units = "-";
:description = "Coriolis F parameter";
:stagger = "M";
float LANDMASK(Time=1, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XY ";
:units = "none";
:description = "Landmask : 1=land, 0=water";
:stagger = "M";
float LANDUSEF(Time=1, land_cat=24, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XYZ";
:units = "category";
:description = "24-category USGS landuse";
:stagger = "M";
float LU_INDEX(Time=1, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XY ";
:units = "category";
:description = "Dominant category";
:stagger = "M";
float HGT_M(Time=1, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XY ";
:units = "meters MSL";
:description = "Topography height";
:stagger = "M";
float HGT_V(Time=1, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XY ";
:units = "meters MSL";
:description = "Topography height";
:stagger = "V";
float SOILTEMP(Time=1, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XY ";
:units = "Kelvin";
:description = "Annual mean deep soil temperature";
:stagger = "M";
float SOILCTOP(Time=1, soil_cat=16, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XYZ";
:units = "category";
:description = "16-category top-layer soil type";
:stagger = "M";
float SOILCBOT(Time=1, soil_cat=16, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XYZ";
:units = "category";
:description = "16-category bottom-layer soil type";
:stagger = "M";
float ALBEDO12M(Time=1, month=12, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XYZ";
:units = "percent";
:description = "Monthly surface albedo";
:stagger = "M";
float GREENFRAC(Time=1, month=12, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XYZ";
:units = "fraction";
:description = "Monthly green fraction";
:stagger = "M";
float SNOALB(Time=1, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XY ";
:units = "percent";
:description = "Maximum snow albedo";
:stagger = "M";
float SLOPECAT(Time=1, south_north=309, west_east=136);
:FieldType = 104; // int
:MemoryOrder = "XY ";
:units = "category";
:description = "Dominant category";
:stagger = "M";
:TITLE = "OUTPUT FROM GRIDGEN";
:SIMULATION_START_DATE = "0000-00-00_00:00:00";
:WEST-EAST_GRID_DIMENSION = 136; // int
:SOUTH-NORTH_GRID_DIMENSION = 309; // int
:BOTTOM-TOP_GRID_DIMENSION = 0; // int
:WEST-EAST_PATCH_START_UNSTAG = 1; // int
:WEST-EAST_PATCH_END_UNSTAG = 136; // int
:WEST-EAST_PATCH_START_STAG = 1; // int
:WEST-EAST_PATCH_END_STAG = 136; // int
:SOUTH-NORTH_PATCH_START_UNSTAG = 1; // int
:SOUTH-NORTH_PATCH_END_UNSTAG = 309; // int
:SOUTH-NORTH_PATCH_START_STAG = 1; // int
:SOUTH-NORTH_PATCH_END_STAG = 309; // int
:GRIDTYPE = "E";
:DX = 0.111143f; // float
:DY = 0.106776f; // float
:DYN_OPT = 4; // int
:CEN_LAT = 21.0f; // float
:CEN_LON = 80.0f; // float
:TRUELAT1 = 1.0E20f; // float
:TRUELAT2 = 1.0E20f; // float
:MOAD_CEN_LAT = 0.0f; // float
:STAND_LON = 1.0E20f; // float
:POLE_LAT = 90.0f; // float
:POLE_LON = 0.0f; // float
:corner_lats = 3.8832555f, 36.602543f, 36.602543f, 3.8832555f, 3.8931324f, 36.61482f, 36.59018f, 3.873307f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f; // float
:corner_lons = 65.589096f, 61.982986f, 98.01701f, 94.410904f, 65.69548f, 62.114895f, 98.14889f, 94.51727f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f; // float
:MAP_PROJ = 203; // int
:MMINLU = "USGS";
:ISWATER = 16; // int
:ISICE = 24; // int
:ISURBAN = 1; // int
:ISOILWATER = 14; // int
:grid_id = 1; // int
:parent_id = 1; // int
:i_parent_start = 0; // int
:j_parent_start = 0; // int
:i_parent_end = 136; // int
:j_parent_end = 309; // int
:parent_grid_ratio = 1; // int
}
*/