使用 NetCDF-java API 读取 HDF5 的有效方法是什么,给出由以下形式的结构组成的栅格变量?
Structure {
float depth;
float uncertainty;
} values(2115, 1635);
:_ChunkSizes = 67U, 103U; // uint
过去,我很幸运地使用 NetCDF-java API 来处理简单的栅格变量(请参阅读取 NetCDF 文件)。但是当我尝试访问结构类型时,我的代码运行速度非常慢。 处理上述结构的数据需要运行 36 分钟。使用JHDF Java HDF 库处理相同的文件只需要大约 2 秒。 显然,我没有按照作者的意图使用 NetCDF-java API。不幸的是,我找不到任何处理结构变量的好例子。我确实阅读了项目中的 Javadoc 和 Junit 测试用例,但我能弄清楚的最好的代码是下面所示的代码。
我正在使用 NetCDF-java 的当前版本,版本 5.6.0。我测试的示例文件是 IHO S-102 格式 (HDF5) 文件,给出港口的底部深度。样本文件可在NOAA S-102 测深表面数据 下载。我测试了许多具有类似结果的文件(上面显示的变量来自102US00_US4NJ1FH.h5)。尽管下面的代码在行和列上循环,但我还尝试了一种基于块大小方案访问网格单元的变体。 查看代码,很明显该循环创建了大量的短期持久对象,但我认为真正的问题在于底层方法。 我非常确定代码会为其检索的每个数据值执行不同的文件访问操作。但我一直没能找到更有效的方法来使用 API。
NetcdfFile ncfile = NetcdfFiles.open(targetFilePath);
Variable v = ncfile.findVariable(targetVariableName);
System.out.println(""+v.toString());
Structure s = (Structure) v;
v.setCaching(true); // does not seem to make a difference
int[] shape = s.getShape();
StructureData sd = s.readStructure(0);
Member m = sd.findMember("depth");
double sumValid = 0;
int nValid = 0;
int nNoData = 0;
for (int i = 0; i < shape[0]; i++) {
for (int j = 0; j < shape[1]; j++) {
int index = i * shape[1] + j;
sd = s.readStructure(index);
float[] f = sd.getJavaArrayFloat(m);
if (f[0] == 1000000) {
nNoData++;
} else {
sumValid += f[0];
nValid++;
}
}
}
System.out.println("nValid: " + nValid);
System.out.println("nNoData: " + nNoData);
System.out.println("Mean: " + (sumValid / nValid));
预先感谢您的帮助。
我还不确定使用您提供的代码导致速度缓慢的原因,但在
readStructure
调用中有些东西似乎不太正确。似乎每次它从结构中读取单个值时,都会从磁盘加载整个块,解压缩它,读取单个值,然后重复自身(因此 3,458,025 次加载块/解压缩/获取单浮点迭代。 ..不好)。
如果您知道要读取结构变量给定成员的所有数据,则可以采用其他几种方法,这两种方法都快得多。请注意,在这些示例中,我使用此文件 (102US00_US4NJ1FH.h5) 和
targetVariableName = "BathymetryCoverage/BathymetryCoverage.01/Group_001/values"
。
首先,您可以使用结构迭代器:
try (NetcdfFile ncfile = NetcdfFiles.open(targetFilePath)) {
Variable v = ncfile.findVariable(targetVariableName);
Structure s = (Structure) v;
double sumValid = 0;
int nValid = 0;
int nNoData = 0;
try (StructureDataIterator siter = s.getStructureIterator()) {
while (siter.hasNext()) {
StructureData sd = siter.next();
float f = sd.getScalarFloat("depth");
if (f == 1000000) {
nNoData++;
} else {
sumValid += f;
nValid++;
}
}
}
System.out.println("nValid: " + nValid);
System.out.println("nNoData: " + nNoData);
System.out.println("Mean: " + (sumValid / nValid));
}
这在我的机器上只需要不到 4 秒。
其次,您可以一起跳过结构特定的 API,并将成员读取为
Variable
,方法是将 .<memberName>
附加到结构变量名称,例如:
try (NetcdfFile ncfile = NetcdfFiles.open(targetFilePath)) {
Variable v = ncfile.findVariable(targetVariableName + ".depth");
double sumValid = 0;
int nValid = 0;
int nNoData = 0;
Array memberArray = v.read();
Index index = memberArray.getIndex();
int[] shape = memberArray.getShape();
for (int i = 0; i < shape[0]; i++) {
for (int j = 0; j < shape[1]; j++) {
float f = memberArray.getFloat(index.set(i, j));
if (f == 1000000) {
nNoData++;
} else {
sumValid += f;
nValid++;
}
}
}
System.out.println("nValid: " + nValid);
System.out.println("nNoData: " + nNoData);
System.out.println("Mean: " + (sumValid / nValid));
}
这在我的机器上大约需要 500 毫秒,尽管需要将整个成员数据数组加载到内存中。