ucar.nc2.ft.point.standard.plug.CFpointObs 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.ft.point.standard.plug;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
import ucar.ma2.DataType;
import ucar.nc2.Dimension;
import ucar.nc2.Structure;
import ucar.nc2.Variable;
import ucar.nc2.constants.AxisType;
import ucar.nc2.constants.CDM;
import ucar.nc2.constants.CF;
import ucar.nc2.constants.FeatureType;
import ucar.nc2.dataset.CoordSysBuilder;
import ucar.nc2.dataset.CoordinateAxis;
import ucar.nc2.dataset.NetcdfDataset;
import ucar.nc2.dataset.VariableDS;
import ucar.nc2.ft.point.standard.CoordSysEvaluator;
import ucar.nc2.ft.point.standard.Evaluator;
import ucar.nc2.ft.point.standard.JoinArray;
import ucar.nc2.ft.point.standard.Table;
import ucar.nc2.ft.point.standard.TableConfig;
import ucar.nc2.ft.point.standard.TableConfigurerImpl;
/**
* CF "point obs" Convention.
*
* @author caron
* @see "http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.6/cf-conventions.html#discrete-sampling-geometries"
* @see "http://www.unidata.ucar.edu/software/netcdf-java/reference/FeatureDatasets/CFencodingTable.html"
* @since Nov 3, 2008
*/
public class CFpointObs extends TableConfigurerImpl {
protected enum Encoding {
single, // single feature in files
multidim, // feature(s1, s2, ...)
raggedContiguous,// row_size
raggedIndex, // stn_index
flat, // not used
// multiStructure // multiple structures (nc4)
}
public boolean isMine(FeatureType wantFeatureType, NetcdfDataset ds) {
String conv = ds.findAttValueIgnoreCase(null, CDM.CONVENTIONS, null);
if (conv == null) return false;
List names = CoordSysBuilder.breakupConventionNames(conv);
for (String s : names) {
if (s.startsWith("CF-1"))
return true;
}
return false;
}
public TableConfig getConfig(FeatureType wantFeatureType, NetcdfDataset ds, Formatter errlog) throws IOException {
EncodingInfo info = new EncodingInfo();
// figure out the actual feature type of the dataset
CF.FeatureType ftype = CF.FeatureType.getFeatureTypeFromGlobalAttribute(ds);
if (ftype == null) ftype = CF.FeatureType.point;
// make sure lat, lon, time coordinates exist
if (!checkCoordinates(ds, info, errlog)) return null; // fail fast
switch (ftype) {
case point:
return getPointConfig(ds, info, errlog);
case timeSeries:
return getStationConfig(ds, info, errlog);
case profile:
return getProfileConfig(ds, info, errlog);
case trajectory:
return getTrajectoryConfig(ds, info, errlog);
case timeSeriesProfile:
return getTimeSeriesProfileConfig(ds, info, errlog);
case trajectoryProfile:
return getSectionConfig(ds, info, errlog);
}
return null;
}
protected boolean checkCoordinates(NetcdfDataset ds, EncodingInfo info, Formatter errlog) {
boolean ok = true;
info.time = CoordSysEvaluator.findCoordByType(ds, AxisType.Time);
if (info.time == null) {
errlog.format("CFpointObs cant find a Time coordinate %n");
ok = false;
}
// find lat coord
info.lat = CoordSysEvaluator.findCoordByType(ds, AxisType.Lat);
if (info.lat == null) {
errlog.format("CFpointObs cant find a Latitude coordinate %n");
ok = false;
}
// find lon coord
info.lon = CoordSysEvaluator.findCoordByType(ds, AxisType.Lon);
if (info.lon == null) {
errlog.format("CFpointObs cant find a Longitude coordinate %n");
ok = false;
}
if (!ok) return false;
// dimensions must match
List dimLat = info.lat.getDimensions();
List dimLon = info.lon.getDimensions();
if (!dimLat.equals(dimLon)) {
errlog.format("CFpointObs Lat, Lon coordinate dimensions must match lat=%s lon=%s %n", info.lat.getNameAndDimensions(), info.lon.getNameAndDimensions());
ok = false;
}
return ok;
}
/////////////////////////////////////////////////////////////////////////////////
protected TableConfig getPointConfig(NetcdfDataset ds, EncodingInfo info, Formatter errlog) throws IOException {
if (info.time.getRank() != 1) {
errlog.format("CFpointObs type=point: coord time must have rank 1, coord var= %s %n", info.time.getNameAndDimensions());
return null;
}
Dimension obsDim = info.time.getDimension(0);
TableConfig obsTable = makeSingle(ds, obsDim, errlog);
obsTable.featureType = FeatureType.POINT;
return obsTable;
}
//////////////////////////////////////////////////////////////////////////////////
protected TableConfig getStationConfig(NetcdfDataset ds, EncodingInfo info, Formatter errlog) throws IOException {
if (!identifyEncodingStation(ds, info, CF.FeatureType.timeSeries, errlog))
return null;
// make station table
TableConfig stnTable = makeStationTable(ds, FeatureType.STATION, info, errlog);
if (stnTable == null) return null;
Dimension obsDim = info.childDim;
TableConfig obsTable = null;
switch (info.encoding) {
case single:
obsTable = makeSingle(ds, obsDim, errlog);
break;
case multidim:
obsTable = makeMultidimInner(ds, stnTable, info.childDim, info, errlog);
if (info.time.getRank() == 1) { // join time(time)
obsTable.addJoin(new JoinArray(info.time, JoinArray.Type.raw, 0));
obsTable.time = info.time.getFullName();
}
break;
case raggedContiguous:
stnTable.numRecords = info.ragged_rowSize.getFullName();
obsTable = makeRaggedContiguousChildTable(ds, info.parentDim, info.childDim, info.childStruct, errlog);
break;
case raggedIndex:
obsTable = makeRaggedIndexChildTable(ds, info.parentDim, info.childDim, info.ragged_parentIndex, errlog);
break;
case flat:
info.set(Encoding.flat, obsDim);
obsTable = makeStructTable(ds, FeatureType.STATION, info, errlog);
obsTable.parentIndex = (info.instanceId == null) ? null : info.instanceId.getFullName();
Variable stnIdVar = Evaluator.findVariableWithAttributeAndDimension(ds, CF.CF_ROLE, CF.STATION_ID, obsDim, errlog);
if (stnIdVar == null)
stnIdVar = Evaluator.findVariableWithAttributeAndDimension(ds, CF.STANDARD_NAME, CF.STATION_ID, obsDim, errlog);
obsTable.stnId = (stnIdVar == null) ? null : stnIdVar.getFullName();
obsTable.stnDesc = Evaluator.findNameOfVariableWithAttributeValue(ds, CF.STANDARD_NAME, CF.PLATFORM_NAME);
if (obsTable.stnDesc == null)
obsTable.stnDesc = Evaluator.findNameOfVariableWithAttributeValue(ds, CF.STANDARD_NAME, CF.STATION_DESC);
obsTable.stnWmoId = Evaluator.findNameVariableWithStandardNameAndDimension(ds, CF.STATION_WMOID, obsDim, errlog);
obsTable.stnAlt = Evaluator.findNameVariableWithStandardNameAndDimension(ds, CF.SURFACE_ALTITUDE, obsDim, errlog);
if (obsTable.stnAlt == null)
obsTable.stnAlt = Evaluator.findNameVariableWithStandardNameAndDimension(ds, CF.STATION_ALTITUDE, obsDim, errlog);
break;
}
if (obsTable == null) return null;
stnTable.addChild(obsTable);
return stnTable;
}
////
protected TableConfig getProfileConfig(NetcdfDataset ds, EncodingInfo info, Formatter errlog) throws IOException {
if (!identifyEncodingProfile(ds, info, errlog)) return null;
TableConfig profileTable = makeStructTable(ds, FeatureType.PROFILE, info, errlog);
if (profileTable == null) return null;
profileTable.feature_id = identifyIdVariableName(ds, CF.FeatureType.profile);
if (profileTable.feature_id == null) {
errlog.format("CFpointObs getProfileConfig cant find a profile id %n");
}
// obs table
VariableDS z = CoordSysEvaluator.findCoordByType(ds, AxisType.Height);
if (z == null) z = CoordSysEvaluator.findCoordByType(ds, AxisType.Pressure);
if (z == null) z = CoordSysEvaluator.findCoordByType(ds, AxisType.GeoZ);
if (z == null) {
errlog.format("CFpointObs getProfileConfig cant find a Height coordinate %n");
return null;
}
if (info.childStruct == null)
info.childStruct = z.getParentStructure();
TableConfig obsTable = null;
switch (info.encoding) {
case single:
obsTable = makeSingle(ds, info.childDim, errlog);
break;
case multidim:
obsTable = makeMultidimInner(ds, profileTable, info.childDim, info, errlog);
if (z.getRank() == 1) {// z(z)
obsTable.addJoin(new JoinArray(z, JoinArray.Type.raw, 0));
obsTable.elev = z.getFullName();
}
break;
case raggedContiguous:
profileTable.numRecords = info.ragged_rowSize.getFullName();
obsTable = makeRaggedContiguousChildTable(ds, info.parentDim, info.childDim, info.childStruct, errlog);
break;
case raggedIndex:
obsTable = makeRaggedIndexChildTable(ds, info.parentDim, info.childDim, info.ragged_parentIndex, errlog);
break;
case flat:
throw new UnsupportedOperationException("CFpointObs: profile flat encoding");
}
if (obsTable == null) return null;
profileTable.addChild(obsTable);
return profileTable;
}
////
protected TableConfig getTrajectoryConfig(NetcdfDataset ds, EncodingInfo info, Formatter errlog) throws IOException {
if (!identifyEncodingTraj(ds, info, errlog)) return null;
TableConfig trajTable = makeStructTable(ds, FeatureType.TRAJECTORY, info, errlog);
if (trajTable == null) return null;
trajTable.feature_id = identifyIdVariableName(ds, CF.FeatureType.trajectory);
if (trajTable.feature_id == null) {
errlog.format("CFpointObs getTrajectoryConfig cant find a trajectoy id %n");
}
// obs table
//Dimension obsDim = time.getDimension(time.getRank() - 1); // may be time(time) or time(traj, obs)
TableConfig obsConfig = null;
switch (info.encoding) {
case single:
obsConfig = makeSingle(ds, info.childDim, errlog);
break;
case multidim:
obsConfig = makeMultidimInner(ds, trajTable, info.childDim, info, errlog);
if (info.time.getRank() == 1) { // join time(obs) or time(time)
obsConfig.addJoin(new JoinArray(info.time, JoinArray.Type.raw, 0));
obsConfig.time = info.time.getFullName();
}
break;
case raggedContiguous:
trajTable.numRecords = info.ragged_rowSize.getFullName();
obsConfig = makeRaggedContiguousChildTable(ds, info.parentDim, info.childDim, info.childStruct, errlog);
break;
case raggedIndex:
obsConfig = makeRaggedIndexChildTable(ds, info.parentDim, info.childDim, info.ragged_parentIndex, errlog);
break;
case flat:
throw new UnsupportedOperationException("CFpointObs: trajectory flat encoding");
}
if (obsConfig == null) return null;
trajTable.addChild(obsConfig);
return trajTable;
}
////
protected TableConfig getTimeSeriesProfileConfig(NetcdfDataset ds, EncodingInfo info, Formatter errlog) throws IOException {
if (!identifyEncodingTimeSeriesProfile(ds, info, CF.FeatureType.timeSeriesProfile, errlog)) return null;
VariableDS time = CoordSysEvaluator.findCoordByType(ds, AxisType.Time);
if (time == null) return null;
if (time.getRank() == 0 && time.getParentStructure() == null) {
errlog.format("CFpointObs timeSeriesProfile cannot have a scalar time coordinate%n"); // why ?
return null;
}
/* distinguish multidim from flat
if ((info.encoding == Encoding.multidim) && (time.getRank() < 3) && (z.getRank() < 3)) {
Variable parentId = identifyParent(ds, CF.FeatureType.timeSeriesProfile);
if ((parentId != null) && (parentId.getRank() == 1) && (parentId.getDimension(0).equals(time.getDimension(0)))) {
if (time.getRank() == 1) // multidim time must be 2 or 3 dim
info = new EncodingInfo(Encoding.flat, parentId);
else if (time.getRank() == 2) {
Dimension zDim = z.getDimension(z.getRank() - 1); // may be z(z) or z(profile, z)
if (zDim.equals(time.getDimension(1))) // flat 2D time will have time as inner dim
info = new EncodingInfo(Encoding.flat, parentId);
}
}
} */
TableConfig stationTable = makeStationTable(ds, FeatureType.STATION_PROFILE, info, errlog);
if (stationTable == null) return null;
//Dimension stationDim = ds.findDimension(stationTable.dimName);
//Dimension profileDim = null;
//Dimension zDim = null;
VariableDS z = info.alt;
switch (info.encoding) {
case single: {
assert ((time.getRank() >= 1) && (time.getRank() <= 2)) : "time must be rank 1 or 2";
assert ((z.getRank() >= 1) && (z.getRank() <= 2)) : "z must be rank 1 or 2";
if (time.getRank() == 2) {
if (z.getRank() == 2) // 2d time, 2d z
assert time.getDimensions().equals(z.getDimensions()) : "rank-2 time and z dimensions must be the same";
else // 2d time, 1d z
assert time.getDimension(1).equals(z.getDimension(0)) : "rank-2 time must have z inner dimension";
//profileDim = time.getDimension(0);
//zDim = time.getDimension(1);
} else { // 1d time
if (z.getRank() == 2) { // 1d time, 2d z
assert z.getDimension(0).equals(time.getDimension(0)) : "rank-2 z must have time outer dimension";
//profileDim = z.getDimension(0);
//zDim = z.getDimension(1);
} else { // 1d time, 1d z
assert !time.getDimension(0).equals(z.getDimension(0)) : "time and z dimensions must be different";
//profileDim = time.getDimension(0);
//zDim = z.getDimension(0);
}
}
// make profile table
TableConfig profileTable = makeStructTable(ds, FeatureType.PROFILE, new EncodingInfo().set(Encoding.multidim, info.childDim), errlog);
if (profileTable == null) return null;
if (time.getRank() == 1) {// join time(time)
//profileTable.addJoin(new JoinArray(time, JoinArray.Type.raw, 0));
profileTable.addJoin(new JoinArray(time, JoinArray.Type.level, 1));
profileTable.time = time.getFullName();
}
stationTable.addChild(profileTable);
// make the inner (z) table
TableConfig zTable = makeMultidimInner(ds, profileTable, info.grandChildDim, info, errlog);
if (z.getRank() == 1) { // join z(z)
zTable.addJoin(new JoinArray(z, JoinArray.Type.raw, 0));
zTable.elev = z.getFullName();
}
profileTable.addChild(zTable);
break;
}
case multidim: {
assert ((time.getRank() >= 1) && (time.getRank() <= 3)) : "time must be rank 2 or 3";
assert ((z.getRank() == 1) || (z.getRank() == 3)) : "z must be rank 1 or 3";
if (time.getRank() == 3) {
if (z.getRank() == 3) // 3d time, 3d z
assert time.getDimensions().equals(z.getDimensions()) : "rank-3 time and z dimensions must be the same";
else // 3d time, 1d z
assert time.getDimension(2).equals(z.getDimension(0)) : "rank-3 time must have z inner dimension";
//profileDim = time.getDimension(1);
//zDim = time.getDimension(2);
} else if (time.getRank() == 2) { // 2d time
if (z.getRank() == 3) { // 2d time, 3d z
assert z.getDimension(1).equals(time.getDimension(1)) : "rank-2 time must have time inner dimension";
//profileDim = z.getDimension(1);
//zDim = z.getDimension(2);
} else { // 2d time, 1d z
assert !time.getDimension(0).equals(z.getDimension(0)) : "time and z dimensions must be different";
assert !time.getDimension(1).equals(z.getDimension(0)) : "time and z dimensions must be different";
//profileDim = time.getDimension(1);
//zDim = z.getDimension(0);
}
} else { // 1d time
if (z.getRank() == 1) {
assert !time.getDimension(0).equals(z.getDimension(0)) : "time and z dimensions must be different";
}
}
TableConfig profileTable = makeMultidimInner(ds, stationTable, info.childDim, info, errlog);
if (profileTable == null) return null;
if (time.getRank() == 1) {// join time(time)
profileTable.addJoin(new JoinArray(time, JoinArray.Type.level, 1));
profileTable.time = time.getFullName();
}
stationTable.addChild(profileTable);
// make the inner (z) table
TableConfig zTable = makeMultidimInner3D(ds, stationTable, profileTable, info.grandChildDim, errlog);
if (z.getRank() == 1) { // join z(z)
zTable.addJoin(new JoinArray(z, JoinArray.Type.raw, 0));
zTable.elev = z.getFullName();
}
profileTable.addChild(zTable);
break;
}
case raggedIndex: {
TableConfig profileTable = makeRaggedIndexChildTable(ds, info.parentDim, info.childDim, info.ragged_parentIndex, errlog);
stationTable.addChild(profileTable);
profileTable.numRecords = info.ragged_rowSize.getFullName();
TableConfig obsTable = makeRaggedContiguousChildTable(ds, info.childDim, info.grandChildDim, info.grandChildStruct, errlog);
profileTable.addChild(obsTable);
break;
}
case raggedContiguous: // NOT USED
throw new UnsupportedOperationException("CFpointObs: timeSeriesProfile raggedContiguous encoding not allowed");
/*
case flat:
//profileDim = time.getDimension(0); // may be time(profile) or time(profile, z)
Variable parentId = identifyParent(ds, CF.FeatureType.timeSeriesProfile);
TableConfig profileTable = makeStructTable(ds, FeatureType.PROFILE, info, errlog);
profileTable.parentIndex = parentId.getName();
profileTable.stnId = findNameVariableWithStandardNameAndDimension(ds, CF.STATION_ID, info.childDim, errlog);
profileTable.stnDesc = findNameVariableWithStandardNameAndDimension(ds, CF.STATION_DESC, info.childDim, errlog);
profileTable.stnWmoId = findNameVariableWithStandardNameAndDimension(ds, CF.STATION_WMOID, info.childDim, errlog);
profileTable.stnAlt = findNameVariableWithStandardNameAndDimension(ds, CF.STATION_ALTITUDE, info.childDim, errlog);
stationTable.addChild(profileTable);
//zDim = z.getDimension(z.getRank() - 1); // may be z(z) or z(profile, z)
TableConfig zTable = makeMultidimInner(ds, profileTable, info.grandChildDim, errlog);
if (z.getRank() == 1) // z(z)
zTable.addJoin(new JoinArray(z, JoinArray.Type.raw, 0));
profileTable.addChild(zTable);
break; */
}
return stationTable;
}
protected TableConfig getSectionConfig(NetcdfDataset ds, EncodingInfo info, Formatter errlog) throws IOException {
if (!identifyEncodingSection(ds, info, CF.FeatureType.trajectoryProfile, errlog)) return null;
TableConfig parentTable = makeStructTable(ds, FeatureType.TRAJECTORY_PROFILE, info, errlog);
if (parentTable == null) return null;
parentTable.feature_id = identifyIdVariableName(ds, CF.FeatureType.trajectoryProfile);
if (parentTable.feature_id == null) {
errlog.format("CFpointObs:getSectionConfig cant find a section id %n");
}
//Dimension sectionDim = ds.findDimension(parentTable.dimName);
//Dimension profileDim = null;
//Dimension zDim = null;
VariableDS time = info.time;
VariableDS z = info.alt;
switch (info.encoding) {
case single: {
assert ((time.getRank() >= 1) && (time.getRank() <= 2)) : "time must be rank 1 or 2";
assert ((z.getRank() >= 1) && (z.getRank() <= 2)) : "z must be rank 1 or 2";
if (time.getRank() == 2) {
if (z.getRank() == 2) // 2d time, 2d z
assert time.getDimensions().equals(z.getDimensions()) : "rank-2 time and z dimensions must be the same";
else // 2d time, 1d z
assert time.getDimension(1).equals(z.getDimension(0)) : "rank-2 time must have z inner dimension";
//profileDim = time.getDimension(0);
//zDim = time.getDimension(1);
} else { // 1d time
if (z.getRank() == 2) { // 1d time, 2d z
assert z.getDimension(0).equals(time.getDimension(0)) : "rank-2 z must have time outer dimension";
//profileDim = z.getDimension(0);
//zDim = z.getDimension(1);
} else { // 1d time, 1d z
assert !time.getDimension(0).equals(z.getDimension(0)) : "time and z dimensions must be different";
//profileDim = time.getDimension(0);
//zDim = z.getDimension(0);
}
}
// make profile table
TableConfig profileTable = makeStructTable(ds, FeatureType.PROFILE, new EncodingInfo().set(Encoding.multidim, info.childDim), errlog);
if (profileTable == null) return null;
if (time.getRank() == 1) { // join time(time)
profileTable.addJoin(new JoinArray(time, JoinArray.Type.raw, 0));
profileTable.time = time.getFullName();
}
parentTable.addChild(profileTable);
// make the inner (z) table
TableConfig zTable = makeMultidimInner(ds, profileTable, info.grandChildDim, info, errlog);
if (z.getRank() == 1) { // join z(z)
zTable.addJoin(new JoinArray(z, JoinArray.Type.raw, 0));
zTable.elev = z.getFullName();
}
profileTable.addChild(zTable);
break;
}
case multidim: {
assert ((time.getRank() >= 2) && (time.getRank() <= 3)) : "time must be rank 2 or 3";
assert ((z.getRank() == 1) || (z.getRank() == 3)) : "z must be rank 1 or 3";
if (time.getRank() == 3) {
if (z.getRank() == 3) // 3d time, 3d z
assert time.getDimensions().equals(z.getDimensions()) : "rank-3 time and z dimensions must be the same";
else // 3d time, 1d z
assert time.getDimension(2).equals(z.getDimension(0)) : "rank-3 time must have z inner dimension";
//profileDim = time.getDimension(1);
//zDim = time.getDimension(2);
} else { // 2d time
if (z.getRank() == 3) { // 2d time, 3d z
assert z.getDimension(1).equals(time.getDimension(1)) : "rank-2 time must have time inner dimension";
// profileDim = z.getDimension(1);
//zDim = z.getDimension(2);
} else { // 2d time, 1d z
assert !time.getDimension(0).equals(z.getDimension(0)) : "time and z dimensions must be different";
assert !time.getDimension(1).equals(z.getDimension(0)) : "time and z dimensions must be different";
//profileDim = time.getDimension(1);
//zDim = z.getDimension(0);
}
}
// make profile table
// private TableConfig makeMultidimInner(NetcdfDataset ds, TableConfig parentTable, Dimension obsDim, Formatter errlog) throws IOException {
TableConfig profileTable = makeMultidimInner(ds, parentTable, info.childDim, info, errlog);
if (profileTable == null) return null;
profileTable.feature_id = identifyIdVariableName(ds, CF.FeatureType.profile);
parentTable.addChild(profileTable);
// make the inner (z) table
TableConfig zTable = makeMultidimInner3D(ds, parentTable, profileTable, info.grandChildDim, errlog);
if (z.getRank() == 1) { // join z(z)
zTable.addJoin(new JoinArray(z, JoinArray.Type.raw, 0));
zTable.elev = z.getFullName();
}
profileTable.addChild(zTable);
break;
}
case raggedIndex: {
TableConfig profileTable = makeRaggedIndexChildTable(ds, info.parentDim, info.childDim, info.ragged_parentIndex, errlog);
profileTable.feature_id = identifyIdVariableName(ds, CF.FeatureType.profile);
if (profileTable.feature_id == null) {
errlog.format("CFpointObs:getSectionConfig cant find a profile id %n");
}
parentTable.addChild(profileTable);
profileTable.numRecords = info.ragged_rowSize.getFullName();
TableConfig obsTable = makeRaggedContiguousChildTable(ds, info.childDim, info.grandChildDim, info.grandChildStruct, errlog);
profileTable.addChild(obsTable);
break;
}
case raggedContiguous: {
throw new UnsupportedOperationException("CFpointObs: section raggedContiguous encoding " + info.encoding);
}
/*
case flat:
parentTable.type = Table.Type.Construct; // override default
profileDim = time.getDimension(0); // may be time(profile) or time(profile, z)
Variable parentId = identifyParent(ds, CF.FeatureType.trajectoryProfile);
TableConfig profileTable = makeStructTable(ds, FeatureType.TRAJECTORY_PROFILE, info, errlog);
profileTable.parentIndex = parentId.getName();
profileTable.feature_id = identifyParentId(ds, CF.FeatureType.profile);
parentTable.addChild(profileTable);
zDim = z.getDimension(z.getRank() - 1); // may be z(z) or z(profile, z)
TableConfig zTable = makeMultidimInner(ds, profileTable, zDim, errlog);
if (z.getRank() == 1) // z(z)
zTable.addJoin(new JoinArray(z, JoinArray.Type.raw, 0));
profileTable.addChild(zTable);
break; */
}
return parentTable;
}
/////////////////////////////////////////////////////////////////////
protected static class EncodingInfo {
Encoding encoding;
VariableDS lat, lon, alt, time;
Dimension parentDim, childDim, grandChildDim;
Variable instanceId;
Variable ragged_parentIndex, ragged_rowSize;
Structure parentStruct, childStruct, grandChildStruct;
EncodingInfo set(Encoding encoding, Dimension parentDim) {
this.encoding = encoding;
this.parentDim = parentDim;
return this;
}
EncodingInfo set(Encoding encoding, Dimension parentDim, Dimension childDim) {
this.encoding = encoding;
this.parentDim = parentDim;
this.childDim = childDim;
return this;
}
EncodingInfo set(Encoding encoding, Dimension parentDim, Dimension childDim, Dimension grandChildDim) {
this.encoding = encoding;
this.parentDim = parentDim;
this.childDim = childDim;
this.grandChildDim = grandChildDim;
return this;
}
}
/* given the feature type, figure out the encoding
private EncodingInfo identifyEncoding(NetcdfDataset ds, CF.FeatureType ftype, Formatter errlog) {
Variable ragged_rowSize = Evaluator.findVariableWithAttribute(ds, CF.RAGGED_ROWSIZE);
if (ragged_rowSize != null) {
if (ftype == CF.FeatureType.trajectoryProfile) {
Variable parentId = identifyIdVariable(ds, ftype);
if (parentId == null) {
errlog.format("Section ragged must have section_id variable%n");
return null;
}
return new EncodingInfo(Encoding.raggedContiguous, parentId);
}
return new EncodingInfo(Encoding.raggedContiguous, ragged_rowSize);
}
Variable ragged_parentIndex = Evaluator.findVariableWithAttribute(ds, CF.RAGGED_PARENTINDEX);
if (ragged_parentIndex != null) {
Variable ragged_parentId = identifyIdVariable(ds, ftype);
return new EncodingInfo(Encoding.raggedIndex, ragged_parentId);
}
Variable lat = CoordSysEvaluator.findCoordByType(ds, AxisType.Lat);
if (lat == null) {
errlog.format("Must have a Latitude coordinate%n");
return null;
}
switch (ftype) {
case point:
return new EncodingInfo(Encoding.multidim, (Dimension) null);
case timeSeries:
case profile:
case timeSeriesProfile:
if (lat.getRank() == 0)
return new EncodingInfo(Encoding.single, (Dimension) null);
else if (lat.getRank() == 1)
return new EncodingInfo(Encoding.multidim, lat);
errlog.format("CFpointObs %s Must have Lat/Lon coordinates of rank 0 or 1%n", ftype);
return null;
case trajectory:
case trajectoryProfile:
if (lat.getRank() == 1)
return new EncodingInfo(Encoding.single, (Dimension) null);
else if (lat.getRank() == 2)
return new EncodingInfo(Encoding.multidim, lat);
errlog.format("CFpointObs %s Must have Lat/Lon coordinates of rank 1 or 2%n", ftype);
return null;
}
return null;
} */
// for stations, figure out the encoding
protected boolean identifyEncodingStation(NetcdfDataset ds, EncodingInfo info, CF.FeatureType ftype, Formatter errlog) {
// find the obs dimension
Dimension obsDim = null;
if (info.time.getRank() > 0)
obsDim = info.time.getDimension(info.time.getRank() - 1); // may be time(time) or time(stn, obs)
else if (info.time.getParentStructure() != null) {
Structure parent = info.time.getParentStructure(); // if time axis is a structure member, try pulling dimension out of parent structure
obsDim = parent.getDimension(parent.getRank() - 1);
}
if (obsDim == null) {
errlog.format("CFpointObs: must have a non-scalar Time coordinate%n");
return false;
}
// find the station dimension
if (info.lat.getRank() == 0) {// scalar means single
info.set(Encoding.single, null, obsDim);
return true;
}
Dimension stnDim = info.lat.getDimension(0);
if (obsDim == stnDim) {
info.set(Encoding.flat, null, obsDim); // not used ?
return true;
}
// the raggeds
if (identifyRaggeds(ds, info, stnDim, obsDim, errlog))
return true;
// heres whats left
if (info.lat.getRank() == 1) {
//Encoding e = (info.time.getParentStructure() != null) ? Encoding.multiStructure : Encoding.multidim;
info.set(Encoding.multidim, stnDim, obsDim);
return true;
}
errlog.format("CFpointObs: %s Must have Lat/Lon coordinates of rank 0 or 1%n", ftype);
return false;
}
/**
* Identify ragged array representations for single nests (station, profile, trajectory)
*
* @param ds in this dataset
* @param info put info here
* @param instanceDim the instance dimension, null if not known yet
* @param sampleDim the sample dimension, null if not known yet
* @param errlog error go here
* @return true if ragged array representations is found; side effect: set (info.ragged_rowSize, info.parentStruct) or (info.ragged_parentIndex, info.childStruct)
*/
protected boolean identifyRaggeds(NetcdfDataset ds, EncodingInfo info, Dimension instanceDim, Dimension sampleDim, Formatter errlog) {
// check for contiguous
Evaluator.VarAtt varatt = Evaluator.findVariableWithAttribute(ds, CF.SAMPLE_DIMENSION); // CF 1.6
if (varatt == null) varatt = Evaluator.findVariableWithAttribute(ds, CF.RAGGED_ROWSIZE); // backwards compatibility
if (varatt != null) {
Variable ragged_rowSize = varatt.var;
String sampleDimName = varatt.att.getStringValue();
if (sampleDim != null && !sampleDimName.equals(sampleDim.getShortName())) {
errlog.format("CFpointObs: Contiguous ragged array representation: row_size variable has sample dimension %s must be %s%n", sampleDimName, sampleDim.getShortName());
return false;
}
if (sampleDim == null) {
sampleDim = ds.findDimension(sampleDimName);
if (sampleDim == null) {
errlog.format("CFpointObs: Contiguous ragged array representation: row_size variable has invalid sample dimension %s%n", sampleDimName);
return false;
}
}
Dimension rrDim;
if (ragged_rowSize.getRank() > 0)
rrDim = ragged_rowSize.getDimension(0); // nobs(station)
else if (ragged_rowSize.getParentStructure() != null) {
Structure parent = ragged_rowSize.getParentStructure(); // if ragged_rowSize is a structure member, use dimension of parent structure
rrDim = parent.getDimension(0);
} else {
errlog.format("CFpointObs: Contiguous ragged array representation: row_size variable (%s) must have rank 1%n", ragged_rowSize);
return false;
}
if (instanceDim != null && instanceDim != rrDim) {
errlog.format("CFpointObs: Contiguous ragged array representation: row_size variable has invalid instance dimension %s must be %s%n", rrDim, instanceDim);
return false;
}
instanceDim = rrDim;
if (ragged_rowSize.getDataType() != DataType.INT) {
errlog.format("CFpointObs: Contiguous ragged array representation: row_size variable must be of type integer%n");
return false;
}
info.set(Encoding.raggedContiguous, instanceDim, sampleDim);
info.ragged_rowSize = ragged_rowSize;
info.parentStruct = ragged_rowSize.getParentStructure();
return true;
} // rowsize was found
varatt = Evaluator.findVariableWithAttribute(ds, CF.INSTANCE_DIMENSION); // CF 1.6
if (varatt == null) varatt = Evaluator.findVariableWithAttribute(ds, CF.RAGGED_PARENTINDEX); // backwards compatibility
if (varatt != null) {
Variable ragged_parentIndex = varatt.var;
String instanceDimName = varatt.att.getStringValue();
if (instanceDim != null && !instanceDimName.equals(instanceDim.getShortName())) {
errlog.format("CFpointObs: Indexed ragged array representation: parent_index variable has instance dimension %s must be %s%n", instanceDimName, instanceDim.getShortName());
return false;
}
if (instanceDim == null) {
instanceDim = ds.findDimension(instanceDimName);
if (instanceDim == null) {
errlog.format("CFpointObs: Indexed ragged array representation: parent_index variable has invalid instance dimension %s%n", instanceDimName);
return false;
}
}
if (ragged_parentIndex.getDataType() != DataType.INT) {
errlog.format("CFpointObs: Indexed ragged array representation: parent_index variable must be of type integer%n");
return false;
}
// allow netcdf-4 structures, eg kunicki
if (ragged_parentIndex.isMemberOfStructure()) {
Structure s = ragged_parentIndex.getParentStructure();
if (s.getRank() == 0 || !s.getDimension(0).equals(sampleDim)) {
errlog.format("CFpointObs: Indexed ragged array representation (structure): parent_index variable must be of form Struct { %s }(%s) %n", ragged_parentIndex.getFullName(), sampleDim.getShortName());
return false;
}
} else {
if (ragged_parentIndex.getRank() != 1 || !ragged_parentIndex.getDimension(0).equals(sampleDim)) {
errlog.format("CFpointObs: Indexed ragged array representation: parent_index variable must be of form %s(%s) %n", ragged_parentIndex.getFullName(), sampleDim.getShortName());
return false;
}
}
info.set(Encoding.raggedIndex, instanceDim, sampleDim);
info.ragged_parentIndex = ragged_parentIndex;
info.childStruct = ragged_parentIndex.getParentStructure();
return true;
} // parent index was found
/* kunicki 10/21/2011
Variable ragged_parentIndex = Evaluator.getVariableWithAttributeValue(ds, CF.RAGGED_PARENTINDEX, parentDim.getShortName());
if ((ragged_parentIndex == null) ||
(!ragged_parentIndex.isMemberOfStructure() && (ragged_parentIndex.getRank() == 0 || ragged_parentIndex.getDimension(0).getShortName() != childDim.getShortName()) ||
(ragged_parentIndex.isMemberOfStructure() && (ragged_parentIndex.getParentStructure().getRank() == 0 || ragged_parentIndex.getParentStructure().getDimension(0).getShortName() != childDim.getShortName())))
) {
// if ((null == ragged_parentIndex) || (ragged_parentIndex.getRank() == 0) || (ragged_parentIndex.getDimension(0).getShortName() != childDim.getShortName())) {
errlog.format("there must be a ragged_parent_index variable with outer dimension that matches obs dimension %s%n", childDim.getShortName());
return null;
} */
return false;
}
/**
* Identify ragged array representations for double nests (timeSeries profile, timeSeries trajectory)
*
* This uses the contiguous ragged array representation for each profile (9.5.43.3), and the indexed ragged array
* representation to organise the profiles into time series (9.3.54). The canonical use case is when writing real-time
* data streams that contain profiles from many stations, arriving randomly, with the data for each entire profile written all at once.
*
* @param ds in this dataset
* @param info put info here
* @param errlog error go here
* @return EncodingInfo if ragged array representations is found
*/
protected boolean identifyDoubleRaggeds(NetcdfDataset ds, EncodingInfo info, Formatter errlog) {
// the timeseries are stored as ragged index
Evaluator.VarAtt varatt = Evaluator.findVariableWithAttribute(ds, CF.INSTANCE_DIMENSION);
if (varatt == null) varatt = Evaluator.findVariableWithAttribute(ds, CF.RAGGED_PARENTINDEX);
if (varatt == null) return false;
Variable ragged_parentIndex = varatt.var;
String instanceDimName = varatt.att.getStringValue();
Dimension stationDim = ds.findDimension(instanceDimName);
if (stationDim == null) {
errlog.format("CFpointObs: Indexed ragged array representation: parent_index variable has illegal value for %s = %s%n", CF.INSTANCE_DIMENSION, instanceDimName);
return false;
}
if (ragged_parentIndex.getDataType() != DataType.INT) {
errlog.format("CFpointObs: Indexed ragged array representation: parent_index variable must be of type integer%n");
return false;
}
if (ragged_parentIndex.getRank() != 1 && info.childStruct == null) {
errlog.format("CFpointObs: Indexed ragged array representation: parent_index variable %s must be 1D %n", ragged_parentIndex);
return false;
}
Dimension profileDim = (info.childDim != null) ? info.childDim : ragged_parentIndex.getDimension(0);
// onto the profiles, stored contiguously
varatt = Evaluator.findVariableWithAttribute(ds, CF.SAMPLE_DIMENSION);
if (varatt == null) varatt = Evaluator.findVariableWithAttribute(ds, CF.RAGGED_ROWSIZE);
if (varatt == null) return false;
Variable ragged_rowSize = varatt.var;
String obsDimName = varatt.att.getStringValue();
Dimension obsDim = ds.findDimension(obsDimName);
if (obsDimName == null) {
errlog.format("CFpointObs: Contiguous ragged array representation: parent_index variable has illegal value for %s = %s%n", CF.SAMPLE_DIMENSION, obsDimName);
return false;
}
if (!obsDimName.equals(info.grandChildDim.getShortName())) {
errlog.format("CFpointObs: Contiguous ragged array representation: row_size variable has obs dimension %s must be %s%n", obsDimName, info.childDim);
return false;
}
if (ragged_rowSize.getDataType() != DataType.INT) {
errlog.format("CFpointObs: Contiguous ragged array representation: row_size variable must be of type integer%n");
return false;
}
if (info.childDim == null) { // nc4 ext
Dimension profileDim2 = ragged_rowSize.getDimension(0);
if (profileDim2 != profileDim) {
errlog.format("CFpointObs: Double ragged array representation dimensions do not agree: %s != %s%n", profileDim2.getShortName(), profileDim.getShortName());
return false;
}
}
info.set(Encoding.raggedIndex, stationDim, profileDim, obsDim);
info.ragged_parentIndex = ragged_parentIndex;
info.ragged_rowSize = ragged_rowSize;
return true;
}
protected boolean identifyEncodingProfile(NetcdfDataset ds, EncodingInfo info, Formatter errlog) {
// find the obs dimension
VariableDS z = CoordSysEvaluator.findCoordByType(ds, AxisType.Height);
if (z == null) z = CoordSysEvaluator.findCoordByType(ds, AxisType.Pressure);
if (z == null) z = CoordSysEvaluator.findCoordByType(ds, AxisType.GeoZ);
if (z == null) {
errlog.format("CFpointObs: Must have a Height coordinate%n");
return false;
}
info.alt = z;
Dimension obsDim = null;
if (z.getRank() > 0)
obsDim = z.getDimension(z.getRank() - 1); // may be z(z) or alt(profile, z)
else if (z.getParentStructure() != null) {
Structure parent = z.getParentStructure(); // if time axis is a structure member, try pulling dimension out of parent structure
obsDim = parent.getDimension(parent.getRank() - 1);
}
if (obsDim == null) {
errlog.format("CFpointObs: Must have a non-scalar Height coordinate%n");
return false;
}
if (identifyRaggeds(ds, info, null, obsDim, errlog)) return true;
// parent dimension
Dimension parentDim;
if (z.getRank() > 1) {
parentDim = z.getDimension(0);
info.set(Encoding.multidim, parentDim, obsDim);
return true;
}
VariableDS time = CoordSysEvaluator.findCoordByType(ds, AxisType.Time);
if (time == null) return false;
if (time.getRank() == 0 || time.getDimension(0) == obsDim) {
info.set(Encoding.single, null, obsDim);
return true;
}
parentDim = time.getDimension(0);
info.set(Encoding.multidim, parentDim, obsDim);
return true;
}
protected boolean identifyEncodingTraj(NetcdfDataset ds, EncodingInfo info, Formatter errlog) {
// find the obs dimension
Dimension obsDim = null;
if (info.time.getRank() > 0)
obsDim = info.time.getDimension(info.time.getRank() - 1); // may be time(time) or time(traj, obs)
else if (info.time.getParentStructure() != null) {
Structure parent = info.time.getParentStructure(); // if time axis is a structure member, try pulling dimension out of parent structure
obsDim = parent.getDimension(parent.getRank() - 1);
}
if (obsDim == null) {
errlog.format("CFpointObs: Must have a non-scalar Time coordinate%n");
return false;
}
if (identifyRaggeds(ds, info, null, obsDim, errlog)) return true;
// parent dimension
Dimension parentDim;
if (info.time.getRank() > 1) {
parentDim = info.time.getDimension(0);
info.set(Encoding.multidim, parentDim, obsDim);
return true;
}
if (info.lat.getRank() > 0) { // multidim case
for (Dimension d : info.lat.getDimensions()) {
if (!d.equals(obsDim)) {
info.set(Encoding.multidim, d, obsDim);
return true;
}
}
}
//otherwise its a single traj in the file
info.set(Encoding.single, null, obsDim);
return true;
}
protected boolean identifyEncodingSection(NetcdfDataset ds, EncodingInfo info, CF.FeatureType ftype, Formatter errlog) {
// find the non-station altitude
VariableDS z = findZAxisNotStationAlt(ds);
if (z == null) {
errlog.format("CFpointObs: section must have a z coordinate%n");
return false;
}
if (z.getRank() == 0) {
errlog.format("CFpointObs: section cannot have a scalar z coordinate%n");
return false;
}
info.alt = z;
Dimension obsDim = null;
if (z.getRank() > 0)
obsDim = z.getDimension(z.getRank() - 1); // may be z(z) or alt(profile, z)
else if (z.getParentStructure() != null) {
Structure parent = z.getParentStructure(); // if time axis is a structure member, try pulling dimension out of parent structure
obsDim = parent.getDimension(parent.getRank() - 1);
}
if (obsDim == null) {
errlog.format("CFpointObs: Must have a non-scalar Height coordinate%n");
return false;
}
info.grandChildDim = obsDim;
// parent dimension
Dimension trajDim;
Dimension profileDim;
if (z.getRank() > 2) {
trajDim = z.getDimension(0);
profileDim = z.getDimension(1);
info.set(Encoding.multidim, trajDim, profileDim, obsDim);
return true;
}
if (identifyDoubleRaggeds(ds, info, errlog))
return true;
if (info.time.getRank() > 2) {
trajDim = info.time.getDimension(0);
profileDim = info.time.getDimension(1);
info.set(Encoding.multidim, trajDim, profileDim, obsDim);
return true;
}
if (info.lat.getRank() == 1) {
profileDim = info.lat.getDimension(0);
info.set(Encoding.single, null, profileDim, obsDim);
return true;
}
if (info.lat.getRank() == 2) {
trajDim = info.lat.getDimension(0);
profileDim = info.lat.getDimension(0);
info.set(Encoding.multidim, trajDim, profileDim, obsDim);
return true;
}
// forget flat for now
errlog.format("CFpointObs %s unrecognized form%n", ftype);
return false;
}
protected boolean identifyEncodingTimeSeriesProfile(NetcdfDataset ds, EncodingInfo info, CF.FeatureType ftype, Formatter errlog) {
// find the non-station altitude
VariableDS z = findZAxisNotStationAlt(ds);
if (z == null) {
errlog.format("CFpointObs: timeSeriesProfile must have a z coordinate, not the station altitude%n");
return false;
}
if (z.getRank() == 0) {
errlog.format("CFpointObs: timeSeriesProfile cannot have a scalar z coordinate%n");
return false;
}
Dimension obsDim = z.getDimension(z.getRank() - 1); // may be z(z) or alt(profile, z) or alt(sta,prof,z)
info.alt = z;
info.grandChildDim = obsDim;
// alt(sta,prof,z)
if (z.getRank() > 2) {
Dimension stnDim = z.getDimension(0);
Dimension profileDim = z.getDimension(1);
info.set(Encoding.multidim, stnDim, profileDim, obsDim);
return true;
}
// raggeds
if (identifyDoubleRaggeds(ds, info, errlog))
return true;
Dimension profileDim;
Dimension stnDim;
// single
if (info.lat.getRank() == 0) {
profileDim = info.time.getDimension(0); // may be time(profile) or time(profile, z)
info.set(Encoding.single, null, profileDim, obsDim);
return true;
} else { // lat must use the station dim
stnDim = info.lat.getDimension(0);
}
// multidim from here on
// time(profile) or time(profile, z)
if ((info.time.getRank() == 1) || (info.time.getRank() == 2 && info.time.getDimension(1) == obsDim)) {
profileDim = info.time.getDimension(0);
info.set(Encoding.multidim, stnDim, profileDim, obsDim);
return true;
}
// time(station, profile, z)
if (info.time.getRank() > 2) {
profileDim = info.time.getDimension(1);
info.set(Encoding.multidim, stnDim, profileDim, obsDim);
return true;
}
// time(station, profile)
if (info.time.getRank() == 2 && info.time.getDimension(0) == stnDim) {
profileDim = info.time.getDimension(1);
info.set(Encoding.multidim, stnDim, profileDim, obsDim);
return true;
}
errlog.format("CFpointObs %s unrecognized form%n", ftype);
return false;
}
private String identifyIdVariableName(NetcdfDataset ds, CF.FeatureType ftype) {
Variable v = identifyIdVariable(ds, ftype);
return (v == null) ? null : v.getFullName();
}
private Variable identifyIdVariable(NetcdfDataset ds, CF.FeatureType ftype) {
Variable result;
switch (ftype) {
case timeSeriesProfile:
case timeSeries:
result = Evaluator.findVariableWithAttributeValue(ds, CF.CF_ROLE, CF.TIMESERIES_ID);
if (result != null) return result;
return Evaluator.findVariableWithAttributeValue(ds, CF.STANDARD_NAME, CF.STATION_ID); // old way for backwards compatibility
case trajectory:
case trajectoryProfile:
result = Evaluator.findVariableWithAttributeValue(ds, CF.CF_ROLE, CF.TRAJECTORY_ID);
if (result != null) return result;
return Evaluator.findVariableWithAttributeValue(ds, CF.STANDARD_NAME, CF.TRAJECTORY_ID); // old way for backwards compatibility
case profile:
result = Evaluator.findVariableWithAttributeValue(ds, CF.CF_ROLE, CF.PROFILE_ID);
if (result != null) return result;
return Evaluator.findVariableWithAttributeValue(ds, CF.STANDARD_NAME, CF.PROFILE_ID); // old way for backwards compatibility
default:
return null;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// for station and stationProfile, not flat
private TableConfig makeStationTable(NetcdfDataset ds, FeatureType ftype, EncodingInfo info, Formatter errlog) throws IOException {
Variable lat = CoordSysEvaluator.findCoordByType(ds, AxisType.Lat);
Variable lon = CoordSysEvaluator.findCoordByType(ds, AxisType.Lon);
if (lat == null || lon == null) {
errlog.format("CFpointObs: must have lat and lon coordinates%n");
return null;
}
//Dimension stationDim = (info.encoding == Encoding.single) ? null : lat.getDimension(0); // assumes outer dim of lat is parent dimension, single = scalar
Table.Type stationTableType = Table.Type.Structure;
if (info.encoding == Encoding.single) stationTableType = Table.Type.Top;
if (info.encoding == Encoding.flat) stationTableType = Table.Type.Construct;
Dimension stationDim = (info.encoding == Encoding.flat) ? info.childDim : info.parentDim;
String name = (stationDim == null) ? " single" : stationDim.getShortName();
TableConfig stnTable = new TableConfig(stationTableType, name);
stnTable.featureType = ftype;
// stnId
Variable stnIdVar = Evaluator.findVariableWithAttributeAndDimension(ds, CF.CF_ROLE, CF.TIMESERIES_ID, stationDim, errlog);
if (stnIdVar == null)
stnIdVar = Evaluator.findVariableWithAttributeAndDimension(ds, CF.STANDARD_NAME, CF.STATION_ID, stationDim, errlog);
if (stnIdVar == null) {
errlog.format("CFpointObs: must have a Station id variable with %s = %s%n", CF.CF_ROLE, CF.TIMESERIES_ID);
return null;
}
stnTable.stnId = stnIdVar.getFullName();
info.instanceId = stnIdVar;
stnTable.stnDesc = Evaluator.findNameVariableWithStandardNameAndDimension(ds, CF.PLATFORM_NAME, stationDim, errlog);
if (stnTable.stnDesc == null)
stnTable.stnDesc = Evaluator.findNameVariableWithStandardNameAndDimension(ds, CF.STATION_DESC, stationDim, errlog);
stnTable.stnWmoId = Evaluator.findNameVariableWithStandardNameAndDimension(ds, CF.PLATFORM_ID, stationDim, errlog);
if (stnTable.stnWmoId == null)
stnTable.stnWmoId = Evaluator.findNameVariableWithStandardNameAndDimension(ds, CF.STATION_WMOID, stationDim, errlog);
stnTable.stnAlt = Evaluator.findNameVariableWithStandardNameAndDimension(ds, CF.SURFACE_ALTITUDE, stationDim, errlog);
if (stnTable.stnAlt == null)
stnTable.stnAlt = Evaluator.findNameVariableWithStandardNameAndDimension(ds, CF.STATION_ALTITUDE, stationDim, errlog);
stnTable.lat = lat.getFullName();
stnTable.lon = lon.getFullName();
if (info.encoding != Encoding.single && stationDim != null) {
stnTable.dimName = stationDim.getShortName();
makeStructureInfo(stnTable, ds, stnIdVar.getParentStructure(), stationDim);
}
// LOOK probably need a standard name here
// optional alt coord - detect if its a station height or actually associated with the obs, eg for a profile
if (stnTable.stnAlt == null) {
Variable alt = CoordSysEvaluator.findCoordByType(ds, AxisType.Height);
if (alt != null) {
if ((info.encoding == Encoding.single) && alt.getRank() == 0)
stnTable.stnAlt = alt.getFullName();
if ((info.encoding != Encoding.single) && (lat.getRank() == alt.getRank()) && alt.getRank() > 0 && alt.getDimension(0).equals(stationDim))
stnTable.stnAlt = alt.getFullName();
}
}
return stnTable;
}
private void makeStructureInfo(TableConfig tableConfig, NetcdfDataset ds, Structure parent, Dimension dim) {
tableConfig.dimName = dim.getShortName();
if (parent != null) {
tableConfig.structureType = TableConfig.StructureType.Structure;
tableConfig.structName = parent.getShortName();
} else {
boolean hasNetcdf3Struct = Evaluator.hasNetcdf3RecordStructure(ds) && dim.isUnlimited();
tableConfig.structureType = hasNetcdf3Struct ? TableConfig.StructureType.Structure : TableConfig.StructureType.PsuedoStructure;
tableConfig.structName = hasNetcdf3Struct ? "record" : dim.getShortName();
}
}
private TableConfig makeStructTable(NetcdfDataset ds, FeatureType ftype, EncodingInfo info, Formatter errlog) throws IOException {
Table.Type tableType = Table.Type.Structure;
if (info.encoding == Encoding.single) tableType = Table.Type.Top;
if (info.encoding == Encoding.flat) tableType = Table.Type.ParentId;
String name = (info.parentDim == null) ? " single" : info.parentDim.getShortName();
TableConfig tableConfig = new TableConfig(tableType, name);
tableConfig.lat = matchAxisTypeAndDimension(ds, AxisType.Lat, info.parentDim);
tableConfig.lon = matchAxisTypeAndDimension(ds, AxisType.Lon, info.parentDim);
tableConfig.elev = matchAxisTypeAndDimension(ds, AxisType.Height, info.parentDim);
if (tableConfig.elev == null) tableConfig.elev = matchAxisTypeAndDimension(ds, AxisType.Pressure, info.parentDim);
if (tableConfig.elev == null) tableConfig.elev = matchAxisTypeAndDimension(ds, AxisType.GeoZ, info.parentDim);
tableConfig.time = matchAxisTypeAndDimension(ds, AxisType.Time, info.parentDim);
tableConfig.featureType = ftype;
if (info.encoding != Encoding.single && info.parentDim != null) {
tableConfig.dimName = name;
Structure parent = info.parentStruct;
if (parent == null) {
switch (info.encoding) {
case raggedContiguous:
parent = info.ragged_rowSize.getParentStructure();
break;
case raggedIndex:
parent = info.ragged_parentIndex.getParentStructure();
break;
}
}
makeStructureInfo(tableConfig, ds, parent, info.parentDim);
}
return tableConfig;
}
// test E:/work/signell/traj2D.ncml
private TableConfig makeStructTableTestTraj(NetcdfDataset ds, FeatureType ftype, EncodingInfo info, Formatter errlog) throws IOException {
Table.Type tableType = Table.Type.Structure;
if (info.encoding == Encoding.single) tableType = Table.Type.Top;
if (info.encoding == Encoding.flat) tableType = Table.Type.ParentId;
String name = (info.parentDim == null) ? " single" : info.parentDim.getShortName();
TableConfig tableConfig = new TableConfig(tableType, name);
tableConfig.lat = CoordSysEvaluator.findCoordNameByType(ds, AxisType.Lat);
tableConfig.lon = CoordSysEvaluator.findCoordNameByType(ds, AxisType.Lon);
tableConfig.elev = CoordSysEvaluator.findCoordNameByType(ds, AxisType.Height);
if (tableConfig.elev == null) tableConfig.elev = CoordSysEvaluator.findCoordNameByType(ds, AxisType.Pressure);
if (tableConfig.elev == null) tableConfig.elev = CoordSysEvaluator.findCoordNameByType(ds, AxisType.GeoZ);
tableConfig.time = CoordSysEvaluator.findCoordNameByType(ds, AxisType.Time);
tableConfig.featureType = ftype;
if (info.encoding != Encoding.single && info.parentDim != null) {
tableConfig.dimName = name;
makeStructureInfo(tableConfig, ds, null, info.parentDim);
}
return tableConfig;
}
/////////////////////////////////////////////////////////////////////////////////
private TableConfig makeRaggedContiguousChildTable(NetcdfDataset ds, Dimension parentDim, Dimension childDim, Structure childStruct, Formatter errlog) throws IOException {
TableConfig childTable = new TableConfig(Table.Type.Contiguous, childDim.getShortName());
childTable.dimName = childDim.getShortName();
childTable.lat = matchAxisTypeAndDimension(ds, AxisType.Lat, childDim);
childTable.lon = matchAxisTypeAndDimension(ds, AxisType.Lon, childDim);
childTable.elev = matchAxisTypeAndDimension(ds, AxisType.Height, childDim);
if (childTable.elev == null) childTable.elev = matchAxisTypeAndDimension(ds, AxisType.Pressure, childDim);
if (childTable.elev == null) childTable.elev = matchAxisTypeAndDimension(ds, AxisType.GeoZ, childDim);
childTable.time = matchAxisTypeAndDimension(ds, AxisType.Time, childDim);
makeStructureInfo(childTable, ds, childStruct, childDim);
return childTable;
}
private TableConfig makeRaggedIndexChildTable(NetcdfDataset ds, Dimension parentDim, Dimension childDim, Variable ragged_parentIndex, Formatter errlog) throws IOException {
TableConfig childTable = new TableConfig(Table.Type.ParentIndex, childDim.getShortName());
childTable.dimName = childDim.getShortName();
childTable.lat = matchAxisTypeAndDimension(ds, AxisType.Lat, childDim);
childTable.lon = matchAxisTypeAndDimension(ds, AxisType.Lon, childDim);
childTable.elev = matchAxisTypeAndDimension(ds, AxisType.Height, childDim);
if (childTable.elev == null) childTable.elev = matchAxisTypeAndDimension(ds, AxisType.Pressure, childDim);
if (childTable.elev == null) childTable.elev = matchAxisTypeAndDimension(ds, AxisType.GeoZ, childDim);
childTable.time = matchAxisTypeAndDimension(ds, AxisType.Time, childDim);
makeStructureInfo(childTable, ds, ragged_parentIndex.getParentStructure(), childDim);
childTable.parentIndex = ragged_parentIndex.getFullName();
return childTable;
}
// the inner table of Structure(outer, inner) and middle table of Structure(outer, middle, inner)
private TableConfig makeMultidimInner(NetcdfDataset ds, TableConfig parentTable, Dimension obsDim, EncodingInfo info, Formatter errlog) throws IOException {
Dimension parentDim = ds.findDimension(parentTable.dimName);
Table.Type obsTableType = (parentTable.structureType == TableConfig.StructureType.PsuedoStructure) ? Table.Type.MultidimInnerPsuedo : Table.Type.MultidimInner;
// if (info.time.isMemberOfStructure()) obsTableType = Table.Type.Structure;
TableConfig obsTable = new TableConfig(obsTableType, obsDim.getShortName());
obsTable.lat = matchAxisTypeAndDimension(ds, AxisType.Lat, parentDim, obsDim);
obsTable.lon = matchAxisTypeAndDimension(ds, AxisType.Lon, parentDim, obsDim);
obsTable.elev = matchAxisTypeAndDimension(ds, AxisType.Height, parentDim, obsDim);
if (obsTable.elev == null) obsTable.elev = matchAxisTypeAndDimension(ds, AxisType.Pressure, parentDim, obsDim);
if (obsTable.elev == null) obsTable.elev = matchAxisTypeAndDimension(ds, AxisType.GeoZ, parentDim, obsDim);
obsTable.time = matchAxisTypeAndDimension(ds, AxisType.Time, parentDim, obsDim);
// divide up the variables between the parent and the obs
List obsVars;
List vars = ds.getVariables();
List parentVars = new ArrayList<>(vars.size());
obsVars = new ArrayList<>(vars.size());
for (Variable orgV : vars) {
if (orgV instanceof Structure) continue;
Dimension dim0 = orgV.getDimension(0);
if ((dim0 != null) && dim0.equals(parentDim)) {
if ((orgV.getRank() == 1) || ((orgV.getRank() == 2) && orgV.getDataType() == DataType.CHAR)) {
parentVars.add(orgV.getShortName());
} else {
Dimension dim1 = orgV.getDimension(1);
if ((dim1 != null) && dim1.equals(obsDim))
obsVars.add(orgV.getShortName());
}
}
}
parentTable.vars = parentVars;
// parentTable.vars = parentTable.isPsuedoStructure ? parentVars : null; // restrict to these if psuedoStruct
obsTable.structureType = parentTable.structureType;
obsTable.outerName = parentDim.getShortName();
obsTable.innerName = obsDim.getShortName();
obsTable.dimName = (parentTable.structureType == TableConfig.StructureType.PsuedoStructure) ? obsTable.outerName : obsTable.innerName;
obsTable.structName = obsDim.getShortName();
obsTable.vars = obsVars;
return obsTable;
}
// the inner table of Structure(outer, middle, inner)
private TableConfig makeMultidimInner3D(NetcdfDataset ds, TableConfig outerTable, TableConfig middleTable, Dimension innerDim, Formatter errlog) throws IOException {
Dimension outerDim = ds.findDimension(outerTable.dimName);
Dimension middleDim = ds.findDimension(middleTable.innerName);
Table.Type obsTableType = (outerTable.structureType == TableConfig.StructureType.PsuedoStructure) ? Table.Type.MultidimInnerPsuedo3D : Table.Type.MultidimInner3D;
TableConfig obsTable = new TableConfig(obsTableType, innerDim.getShortName());
obsTable.structureType = TableConfig.StructureType.PsuedoStructure2D;
obsTable.dimName = outerTable.dimName;
obsTable.outerName = middleTable.innerName;
obsTable.innerName = innerDim.getShortName();
obsTable.structName = innerDim.getShortName();
obsTable.lat = matchAxisTypeAndDimension(ds, AxisType.Lat, outerDim, middleDim, innerDim);
obsTable.lon = matchAxisTypeAndDimension(ds, AxisType.Lon, outerDim, middleDim, innerDim);
obsTable.elev = matchAxisTypeAndDimension(ds, AxisType.Height, outerDim, middleDim, innerDim);
if (obsTable.elev == null) obsTable.elev = matchAxisTypeAndDimension(ds, AxisType.Pressure, middleDim, innerDim);
if (obsTable.elev == null) obsTable.elev = matchAxisTypeAndDimension(ds, AxisType.GeoZ, middleDim, innerDim);
obsTable.time = matchAxisTypeAndDimension(ds, AxisType.Time, outerDim, middleDim, innerDim);
// divide up the variables between the 3 tables
List vars = ds.getVariables();
List outerVars = new ArrayList<>(vars.size());
List middleVars = new ArrayList<>(vars.size());
List innerVars = new ArrayList<>(vars.size());
for (Variable orgV : vars) {
if (orgV instanceof Structure) continue;
if ((orgV.getRank() == 1) || ((orgV.getRank() == 2) && orgV.getDataType() == DataType.CHAR)) {
if (outerDim.equals(orgV.getDimension(0)))
outerVars.add(orgV.getShortName());
} else if (orgV.getRank() == 2) {
if (outerDim.equals(orgV.getDimension(0)) && middleDim.equals(orgV.getDimension(1)))
middleVars.add(orgV.getShortName());
} else if (orgV.getRank() == 3) {
if (outerDim.equals(orgV.getDimension(0)) && middleDim.equals(orgV.getDimension(1)) && innerDim.equals(orgV.getDimension(2)))
innerVars.add(orgV.getShortName());
}
}
outerTable.vars = outerVars;
middleTable.vars = middleVars;
obsTable.vars = innerVars;
return obsTable;
}
private TableConfig makeSingle(NetcdfDataset ds, Dimension obsDim, Formatter errlog) throws IOException {
Table.Type obsTableType = Table.Type.Structure;
TableConfig obsTable = new TableConfig(obsTableType, "single");
obsTable.dimName = obsDim.getShortName();
obsTable.lat = matchAxisTypeAndDimension(ds, AxisType.Lat, obsDim);
obsTable.lon = matchAxisTypeAndDimension(ds, AxisType.Lon, obsDim);
obsTable.elev = matchAxisTypeAndDimension(ds, AxisType.Height, obsDim);
if (obsTable.elev == null) obsTable.elev = matchAxisTypeAndDimension(ds, AxisType.Pressure, obsDim);
if (obsTable.elev == null) obsTable.elev = matchAxisTypeAndDimension(ds, AxisType.GeoZ, obsDim);
obsTable.time = matchAxisTypeAndDimension(ds, AxisType.Time, obsDim);
makeStructureInfo(obsTable, ds, null, obsDim);
return obsTable;
}
private TableConfig makeMiddleTable(NetcdfDataset ds, TableConfig parentTable, Dimension obsDim, Formatter errlog) throws IOException {
throw new UnsupportedOperationException("CFpointObs: middleTable encoding");
}
// Adds check for dimensions against parent structure if applicable...
//
// Note to John. It may be that this implementation can be pushed into the super
// class, I don't understand enough of the code base to anticipate implementation artifacts.
protected String matchAxisTypeAndDimension(NetcdfDataset ds, AxisType type, final Dimension outer) {
Variable var = CoordSysEvaluator.findCoordByType(ds, type, new CoordSysEvaluator.Predicate() {
public boolean match(CoordinateAxis axis) {
if ((outer == null) && (axis.getRank() == 0))
return true;
if ((outer != null) && (axis.getRank() == 1) && (outer.equals(axis.getDimension(0))))
return true;
// if axis is structure member, try pulling dimension out of parent structure
if (axis.getParentStructure() != null) {
Structure parent = axis.getParentStructure();
if ((outer != null) && (parent.getRank() == 1) && (outer.equals(parent.getDimension(0))))
return true;
}
return false;
}
});
if (var == null) return null;
return var.getFullName();
}
}