ucar.nc2.dataset.conv.M3IOConvention 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._Coordinate;
import ucar.nc2.constants.AxisType;
import ucar.nc2.util.CancelTask;
import ucar.nc2.dataset.*;
import ucar.unidata.geoloc.projection.*;
import ucar.unidata.geoloc.ProjectionRect;
import java.util.*;
/**
* Models-3/EDSS Input/Output netcf format.
*
* The Models-3/EDSS Input/Output Applications Programming Interface (I/O API)
* is the standard data access library for both NCSC's EDSS project and EPA's Models-3.
*
* 6/24/09: Modified to support multiple projection types of data by Qun He and Alexis Zubrow
* added makePolarStereographicProjection, UTM, and modified latlon
*
* 09/2010 [email protected] add projections 7,8,9,10
*
* @author [email protected]
* @author caron
* @see http://www.baronams.com/products/ioapi/index.html
*/
public class M3IOConvention extends CoordSysBuilder {
private static final double earthRadius = 6370.000; // km
/**
* Do we think this is a M3IO file.
*
* @param ncfile the NetcdfFile to test
* @return true if we think this is a M3IO file.
*/
public static boolean isMine(NetcdfFile ncfile) {
return (null != ncfile.findGlobalAttribute("XORIG"))
&& (null != ncfile.findGlobalAttribute("YORIG"))
&& (null != ncfile.findGlobalAttribute("XCELL"))
&& (null != ncfile.findGlobalAttribute("YCELL"))
&& (null != ncfile.findGlobalAttribute("NCOLS"))
&& (null != ncfile.findGlobalAttribute("NROWS"));
// M3IOVGGridConvention - is this true for this class ??
// return ncFile.findGlobalAttribute( "VGLVLS" ) != null && isValidM3IOFile_( ncFile );
}
public M3IOConvention() {
this.conventionName = "M3IO";
}
public void augmentDataset(NetcdfDataset ncd, CancelTask cancelTask) {
if (null != ncd.findVariable("x")) return; // check if its already been done - aggregating enhanced datasets.
if (null != ncd.findVariable("lon")) return; // check if its already been done - aggregating enhanced datasets.
constructCoordAxes(ncd);
ncd.finish();
}
private CoordinateTransform ct = null;
protected void constructCoordAxes(NetcdfDataset ds) {
Dimension dimx = ds.findDimension("COL");
int nx = dimx.getLength();
Dimension dimy = ds.findDimension("ROW");
int ny = dimy.getLength();
int projType = findAttributeInt(ds, "GDTYP");
boolean isLatLon = (projType == 1);
if (isLatLon) {
//ds.addCoordinateAxis( makeCoordAxis( ds, "lon", "COL", nx, "XORIG", "XCELL", "degrees east"));
//ds.addCoordinateAxis( makeCoordAxis( ds, "lat", "ROW", ny, "YORIG", "YCELL", "degrees north"));
ds.addCoordinateAxis(makeCoordLLAxis(ds, "lon", "COL", nx, "XORIG", "XCELL", "degrees east"));
ds.addCoordinateAxis(makeCoordLLAxis(ds, "lat", "ROW", ny, "YORIG", "YCELL", "degrees north"));
ct = makeLatLongProjection(ds);
VariableDS v = makeCoordinateTransformVariable(ds, ct);
ds.addVariable(null, v);
v.addAttribute(new Attribute(_Coordinate.Axes, "lon lat"));
} else {
ds.addCoordinateAxis(makeCoordAxis(ds, "x", "COL", nx, "XORIG", "XCELL", "km"));
ds.addCoordinateAxis(makeCoordAxis(ds, "y", "ROW", ny, "YORIG", "YCELL", "km"));
if (projType == 2)
ct = makeLCProjection(ds);
else if (projType == 3)
ct = makeTMProjection(ds);
else if (projType == 4)
ct = makeSTProjection(ds);
else if (projType == 5)
ct = makeUTMProjection(ds);
else if (projType == 6) // Was 7. See http://www.baronams.com/products/ioapi/GRIDS.html
ct = makePolarStereographicProjection(ds);
else if (projType == 7)
ct = makeEquitorialMercatorProjection(ds);
else if (projType == 8)
ct = makeTransverseMercatorProjection(ds);
else if (projType == 9)
ct = makeAlbersProjection(ds);
else if (projType == 10)
ct = makeLambertAzimuthalProjection(ds);
if (ct != null) {
VariableDS v = makeCoordinateTransformVariable(ds, ct);
ds.addVariable(null, v);
v.addAttribute(new Attribute(_Coordinate.Axes, "x y"));
}
}
makeZCoordAxis(ds, "LAY", "VGLVLS", "sigma");
makeTimeCoordAxis(ds, "TSTEP");
}
private CoordinateAxis makeCoordAxis(NetcdfDataset ds, String name, String dimName, int n,
String startName, String incrName, String unitName) {
double start = .001 * findAttributeDouble(ds, startName); // km
double incr = .001 * findAttributeDouble(ds, incrName); // km
start = start + incr / 2.; // shifting x and y to central
CoordinateAxis v = new CoordinateAxis1D(ds, null, name, DataType.DOUBLE, dimName, unitName,
"synthesized coordinate from " + startName + " " + incrName + " global attributes");
v.setValues(n, start, incr);
return v;
}
private CoordinateAxis makeCoordLLAxis(NetcdfDataset ds, String name, String dimName, int n,
String startName, String incrName, String unitName) {
// Makes coordinate axes for Lat/Lon
double start = findAttributeDouble(ds, startName); // degrees
double incr = findAttributeDouble(ds, incrName); // degrees
// The coordinate value should be the cell center.
// I recommend also adding a bounds coordinate variable for clarity in the future.
start = start + incr / 2.; // shiftin lon and lat to central
CoordinateAxis v = new CoordinateAxis1D(ds, null, name, DataType.DOUBLE, dimName, unitName,
"synthesized coordinate from " + startName + " " + incrName + " global attributes");
v.setValues(n, start, incr);
return v;
}
private void makeZCoordAxis(NetcdfDataset ds, String dimName, String levelsName, String unitName) {
Dimension dimz = ds.findDimension(dimName);
int nz = dimz.getLength();
ArrayDouble.D1 dataLev = new ArrayDouble.D1(nz);
ArrayDouble.D1 dataLayers = new ArrayDouble.D1(nz + 1);
// layer values are a numeric global attribute array !!
Attribute layers = ds.findGlobalAttribute("VGLVLS");
for (int i = 0; i <= nz; i++)
dataLayers.set(i, layers.getNumericValue(i).doubleValue());
for (int i = 0; i < nz; i++) {
double midpoint = (dataLayers.get(i) + dataLayers.get(i + 1)) / 2;
dataLev.set(i, midpoint);
}
CoordinateAxis v = new CoordinateAxis1D(ds, null, "level", DataType.DOUBLE, dimName, unitName,
"synthesized coordinate from " + levelsName + " global attributes");
v.setCachedData(dataLev, true);
v.addAttribute(new Attribute("positive", "down"));
v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.GeoZ.toString()));
// layer edges
String edge_name = "layer";
Dimension lay_edge = new Dimension(edge_name, nz + 1);
ds.addDimension(null, lay_edge);
CoordinateAxis vedge = new CoordinateAxis1D(ds, null, edge_name, DataType.DOUBLE, edge_name, unitName,
"synthesized coordinate from " + levelsName + " global attributes");
vedge.setCachedData(dataLayers, true);
v.setBoundaryRef(edge_name);
ds.addCoordinateAxis(v);
ds.addCoordinateAxis(vedge);
}
private void makeTimeCoordAxis(NetcdfDataset ds, String timeName) {
int start_date = findAttributeInt(ds, "SDATE");
int start_time = findAttributeInt(ds, "STIME");
int time_step = findAttributeInt(ds, "TSTEP");
int year = start_date / 1000;
int doy = start_date % 1000;
int hour = start_time / 10000;
start_time = start_time % 10000;
int min = start_time / 100;
int sec = start_time % 100;
Calendar cal = new GregorianCalendar(new SimpleTimeZone(0, "GMT"));
cal.clear();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.DAY_OF_YEAR, doy);
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, min);
cal.set(Calendar.SECOND, sec);
//cal.setTimeZone( new SimpleTimeZone(0, "GMT"));
java.text.SimpleDateFormat dateFormatOut = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormatOut.setTimeZone(java.util.TimeZone.getTimeZone("GMT"));
String units = "seconds since " + dateFormatOut.format(cal.getTime()) + " UTC";
// parse the time step
hour = time_step / 10000;
time_step = time_step % 10000;
min = time_step / 100;
sec = time_step % 100;
time_step = hour * 3600 + min * 60 + sec;
Dimension dimt = ds.findDimension(timeName);
int nt = dimt.getLength();
ArrayInt.D1 data = new ArrayInt.D1(nt, false);
for (int i = 0; i < nt; i++) {
data.set(i, i * time_step);
}
// create the coord axis
CoordinateAxis1D timeCoord = new CoordinateAxis1D(ds, null, "time", DataType.INT, timeName, units,
"synthesized time coordinate from SDATE, STIME, STEP global attributes");
timeCoord.setCachedData(data, true);
timeCoord.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Time.toString()));
ds.addCoordinateAxis(timeCoord);
}
private CoordinateTransform makeLatLongProjection(NetcdfDataset ds) {
//double lat0 = findAttributeDouble(ds, "YCENT");
// Get lower left and upper right corners of domain in lat/lon
double x1 = findAttributeDouble(ds, "XORIG");
double y1 = findAttributeDouble(ds, "YORIG");
double x2 = x1 + findAttributeDouble(ds, "XCELL") * findAttributeDouble(ds, "NCOLS");
double y2 = y1 + findAttributeDouble(ds, "YCELL") * findAttributeDouble(ds, "NROWS");
LatLonProjection ll = new LatLonProjection("LatitudeLongitudeProjection", new ProjectionRect(x1, y1, x2, y2));
return new ProjectionCT("LatitudeLongitudeProjection", "FGDC", ll);
}
private CoordinateTransform makeLCProjection(NetcdfDataset ds) {
double par1 = findAttributeDouble(ds, "P_ALP");
double par2 = findAttributeDouble(ds, "P_BET");
double lon0 = findAttributeDouble(ds, "XCENT");
double lat0 = findAttributeDouble(ds, "YCENT");
LambertConformal lc = new LambertConformal(lat0, lon0, par1, par2, 0.0, 0.0, earthRadius);
return new ProjectionCT("LambertConformalProjection", "FGDC", lc);
}
private CoordinateTransform makePolarStereographicProjection(NetcdfDataset ds) {
//boolean n_polar = (findAttributeDouble(ds, "P_ALP") == 1.0);
//double central_meridian = findAttributeDouble(ds, "P_GAM");
double lon0 = findAttributeDouble(ds, "XCENT");
double lat0 = findAttributeDouble(ds, "YCENT");
double latts = findAttributeDouble(ds, "P_BET");
Stereographic sg = new Stereographic(latts, lat0, lon0, 0.0, 0.0, earthRadius);
//sg.setCentralMeridian(centeral_meridian);
return new ProjectionCT("PolarStereographic", "FGDC", sg);
}
private CoordinateTransform makeEquitorialMercatorProjection(NetcdfDataset ds) {
double lon0 = findAttributeDouble(ds, "XCENT");
double par = findAttributeDouble(ds, "P_ALP");
Mercator p = new Mercator(lon0, par, 0.0, 0.0, earthRadius);
return new ProjectionCT("EquitorialMercator", "FGDC", p);
}
private CoordinateTransform makeTransverseMercatorProjection(NetcdfDataset ds) {
double lat0 = findAttributeDouble(ds, "P_ALP");
double tangentLon = findAttributeDouble(ds, "P_GAM");
TransverseMercator p = new TransverseMercator(lat0, tangentLon, 1.0, 0.0, 0.0, earthRadius);
return new ProjectionCT("TransverseMercator", "FGDC", p);
}
private CoordinateTransform makeAlbersProjection(NetcdfDataset ds) {
double lat0 = findAttributeDouble(ds, "YCENT");
double lon0 = findAttributeDouble(ds, "XCENT");
double par1 = findAttributeDouble(ds, "P_ALP");
double par2 = findAttributeDouble(ds, "P_BET");
AlbersEqualArea p = new AlbersEqualArea(lat0, lon0, par1, par2, 0.0, 0.0, earthRadius);
return new ProjectionCT("Albers", "FGDC", p);
}
private CoordinateTransform makeLambertAzimuthalProjection(NetcdfDataset ds) {
double lat0 = findAttributeDouble(ds, "YCENT");
double lon0 = findAttributeDouble(ds, "XCENT");
LambertAzimuthalEqualArea p =
new LambertAzimuthalEqualArea(lat0, lon0, 0.0, 0.0, earthRadius);
return new ProjectionCT("LambertAzimuthal", "FGDC", p);
}
private CoordinateTransform makeSTProjection(NetcdfDataset ds) {
double latt = findAttributeDouble(ds, "PROJ_ALPHA");
if (Double.isNaN(latt))
latt = findAttributeDouble(ds, "P_ALP");
double lont = findAttributeDouble(ds, "PROJ_BETA");
if (Double.isNaN(lont))
lont = findAttributeDouble(ds, "P_BET");
/**
* Construct a Stereographic Projection.
* @param latt tangent point of projection, also origin of projecion coord system
* @param lont tangent point of projection, also origin of projecion coord system
* @param scale scale factor at tangent point, "normally 1.0 but may be reduced"
*/
Stereographic st = new Stereographic(latt, lont, 1.0, 0.0, 0.0, earthRadius);
return new ProjectionCT("StereographicProjection", "FGDC", st);
}
private CoordinateTransform makeTMProjection(NetcdfDataset ds) {
double lat0 = findAttributeDouble(ds, "PROJ_ALPHA");
if (Double.isNaN(lat0))
lat0 = findAttributeDouble(ds, "P_ALP");
double tangentLon = findAttributeDouble(ds, "PROJ_BETA");
if (Double.isNaN(tangentLon))
tangentLon = findAttributeDouble(ds, "P_BET");
/**
* Construct a TransverseMercator Projection.
* @param lat0 origin of projection coord system is at (lat0, tangentLon)
* @param tangentLon longitude that the cylinder is tangent at ("central meridian")
* @param scale scale factor along the central meridian
*/
TransverseMercator tm = new TransverseMercator(lat0, tangentLon, 1.0, 0.0, 0.0, earthRadius);
return new ProjectionCT("MercatorProjection", "FGDC", tm);
}
/**
* Intend to use EPSG system parameters
*
* @param ds dataset
* @return CoordinateTransform
*/
private CoordinateTransform makeUTMProjection(NetcdfDataset ds) {
int zone = (int) findAttributeDouble(ds, "P_ALP");
double ycent = findAttributeDouble(ds, "YCENT");
//double lon0 = findAttributeDouble( "X_CENT");
//double lat0 = findAttributeDouble( "Y_CENT");
/**
* Construct a UTM Projection.
* @param zone - UTM zone
* @param if ycent < 0, then isNorth = False
*/
boolean isNorth = true;
if (ycent < 0)
isNorth = false;
UtmProjection utm = new UtmProjection(zone, isNorth);
return new ProjectionCT("UTM", "EPSG", utm);
}
/////////////////////////////////////////////////////////////////////////
protected AxisType getAxisType(NetcdfDataset ds, VariableEnhanced ve) {
Variable v = (Variable) ve;
String vname = v.getShortName();
if (vname.equalsIgnoreCase("x"))
return AxisType.GeoX;
if (vname.equalsIgnoreCase("y"))
return AxisType.GeoY;
if (vname.equalsIgnoreCase("lat"))
return AxisType.Lat;
if (vname.equalsIgnoreCase("lon"))
return AxisType.Lon;
if (vname.equalsIgnoreCase("time"))
return AxisType.Time;
if (vname.equalsIgnoreCase("level"))
return AxisType.GeoZ;
return null;
}
protected void makeCoordinateTransforms(NetcdfDataset ds) {
if (ct != null) {
VarProcess vp = findVarProcess(ct.getName(), null);
if (vp != null)
vp.ct = ct;
}
super.makeCoordinateTransforms(ds);
}
private double findAttributeDouble(NetcdfDataset ds, String attname) {
Attribute att = ds.findGlobalAttributeIgnoreCase(attname);
if ((att == null) || (att.isString())) return Double.NaN;
return att.getNumericValue().doubleValue();
}
private int findAttributeInt(NetcdfDataset ds, String attname) {
Attribute att = ds.findGlobalAttributeIgnoreCase(attname);
return att.getNumericValue().intValue();
}
}