基于Java的NetCDF文件解析
近期在做的项目中,需要使用Java语言进行NetCDF文件的解析。
然而,当在寻找资料时,发现基于Java语言的资料相较于Python少了很多,而且现有的基于Java解析NetCDF文件到CSV的资料中,功能并不足以满足需要,代码注释也不太清晰,故而最终决定根据各类处理NetCDF文件的产品官方说明文档,自行进行所需功能的编写。
谨以此文,记录个人对于NetCDF文件的理解,和解析过程。
目录
NetCDF文件说明
什么是NetCDF数据?
如何查看NetCDF文件?
图形用户界面
命令行界面
NetCDF文件的数据组成结构
维度 Dimensions
变量 Variables
坐标变量 Coordinate Variavles
属性 Attributes
如何理解NetCDF文件中各数据组成部分?
NetCDF文件解析
解析语言及涉及依赖
解析过程说明
读取nc文件
解析各类变量
写入CSV
涉及到的函数体
其他需注意事项
相关测试结果记录
测试样例一:
测试样例二:
参考资料
NetCDF文件说明
什么是NetCDF数据?
NetCDF文件格式以栅格化形式存储数据,是大气和海洋科学中最常见的数据格式和文件类型,几乎所有天气、气象、海洋模型的数据都以NetCDF文件格式进行存储,卫星数据也常为NetCDF格式。
NetCDF格式文件(全称网络通用数据格式文件(Network Common Data Form))采用二进制格式存储数据,并以数值对的形式对其属性进行自描述。
如何查看NetCDF文件?
图形用户界面
- Ncview:NetCDF可视化浏览器,允许用户可视化地查看NetCDF数据文件。
- IDL NetCDF Reader:IDL中的NetCDF浏览器。
- Panoply:NASA开发的用于查看NetCDF文件的Java应用程序。
- NcBrowse:提供灵活的交互式图形显示数据及其属性的Java应用程序。
命令行界面
- Ncdump:NetCDF文件读取器,与Unidata的NetCDF产品捆绑。
- NetCDF Operators(NCO):操作符程序,其中每个操作符为一个在UNIX命令提示符下执行的独立命令行程序。
- Climate data operatores(CDO):命令行操作符,用于操作和分析NetCDF数据。
NetCDF文件的数据组成结构
维度 Dimensions
NetCDF维度是用于指定NetCDF文件中包含的一个或多个多维变量形状(shape)的命名整数,可以用来表示真实的物理维度,例如时间、纬度、经度或高度;或者更抽象的数量,如车站或模型运行ID。
每个NetCDF维度都有名称和大小,且区分大小写。
- 维度名称:以字母开头的包含字母或数字字符(以及下划线字符和连字符)的任意序列。
- 维度大小:一个正整数,但是netCDF文件中的一个维度的大小可以是UNLIMITED。这种维度称为无限维度或记录维度。具有无限维度的变量可以沿该维度增长到任意长度。
变量 Variables
NetCDF文件中,实际存储数据的是变量。变量的名称、数据类型和形状(shape)由创建变量时指定的维度列表描述。维度的数量就是秩(也称为维),标量变量的秩是0,向量的秩是1,矩阵的秩是2。变量还可以具有关联的属性,可以在创建变量后添加、删除或更改这些属性。
变量的例子有:温度、盐度、氧气等。
坐标变量 Coordinate Variavles
与维度名称相同的一维变量是坐标变量,与一个或多个数据变量的维度相关联,通常定义与该维度对应的物理坐标。二维坐标域将不会被定义为维度。
坐标变量对于NetCDF库没有特殊的意义,但是,使用这个库的软件应该以专门的方式处理坐标变量。
属性 Attributes
NetCDF属性用于存储辅助数据或元数据,提供关于特定变量的信息。
提供关于整个NetCDF文件信息的属性是全局属性,可通过属性名与空白变量名或特殊的空变量ID识别。
注:以上为Introduction to netCDF – Data Carpentry for Oceanographers中的内容,显然,单看解释是很绕的,下面我们来说人话。
如何理解NetCDF文件中各数据组成部分?
下图为NetCDF-Java Online Tutorial | netCDF-Java Documentation中关于坐标的框架图,虽然是与Unidata的产品绑定的,但对于各类数据的理解很有帮助。
从图中,我们可以知道,变量(Variable)是基于坐标轴(Axis)变量的,而坐标轴变量的类型很多,图中列出的有Time、Lat、Lon、Height、Pressure、GeoX、GeoY、GeoZ、RedialAzimuth、RedialElevation、RedialDistance、RunTimely、Ensemble。
在测试样例中,用到的坐标轴变量为Time、Lat、Lon、GeoX、GeoY,在此对这五个变量进行一个说明:
Time:时间,在NetCDF中为time。
Lat:纬度,在NetCDF中为lat。
Lon:经度,在NetCDF中为lon。
GeoX:网格横轴,在NetCDF中为grid_xt。
GeoY:网格横轴,在NetCDF中为grid_yt。
其中GeoX与GeoY都是用来描述网格大小的变量,而Lat和Lon才是真实的物理地址,即经纬度是实际数据所对应的地理坐标,而GeoX和GeoY是数据在图纸上的坐标,同理可以猜测海拔(Height)是与GeoZ相对应的三维坐标系下的z轴。
至于时间纬度,则是一个固定的值或某个范围,可参考Introduction to netCDF – Data Carpentry for Oceanographers中的这张图来理解:
此图所展示的是随时间变化的区域内的温度,故可以这样理解:在这种情况下,温度是唯一含有我们所关心的数据的变量,而经纬度、时间则是坐标变量,每一张表的行列分别为经度纬度,它们各自有固定的时间,随时间变化的区域内温度则表示为一组二维表格。
如果这时,加入了海拔这一坐标变量(如上图所示),则随时间和海拔变化的区域内温度表示为一组三维表格。
以上例子,都只是关于温度这一变量的情况,但实际上的NetCDF文件,通常是由一组涵盖各方面参数的变量组成的,因此,NetCDF文件根据时间、经纬度、海拔,表示为关于多个变量的多个表格。
正是因为其维度的复杂性,当将NetCDF文件解析为CSV文件后,要对所得到的关于各个变量的一组表格进行解读,则需要同时参照时间CSV文件、经度CSV文件、纬度CSV文件及该变量的自身的表格,对格点数据一一对应来解释该变量在所处条件下的值。
举个例子:
time.csv
lon.csv
lat.csv
dlwrf.csv
以上数据均来源于测试样例一,在此文件中,时间为固定的单值变量,故下面将不再提及time变量。
当我们要解读dlwrf变量的D5格点中的值时,需参照坐标变量进行读取,在上述例子中,即是dlwrf在经度为0.703125,纬度为88.88682时,值为210.4392。
同理,dlwrf变量的F10格点可解释为,在经度为1.171875,纬度为87.71603时,dlwrf值为208.5743。
对于该文件中的其余变量,也以如上方式进行解读即可。
NetCDF文件解析
解析语言及涉及依赖
因为项目要求,因此,在此用Java语言对NetCDF进行解析,根据NetCDF文件的自身特性考虑,将其解析为CSV文件是有助于提高可读性的,下面为将NetCDF文件解析为CSV文件时所涉及到的依赖:
<!-- nc格式解析包 -->
<dependency><groupId>edu.ucar</groupId><artifactId>netcdf4</artifactId><version>4.5.5</version>
</dependency><!-- csv操作包 -->
<dependency><groupId>com.opencsv</groupId><artifactId>opencsv</artifactId><version>4.6</version>
</dependency>
解析过程说明
首先,要明确解析过程需要进行哪几个步骤,在此处,由于要将nc文件解析为CSV文件,故应该分为如下几步:
- 读取nc文件
- 将nc文件解析为可读的结构化形式(此处为了后续便于写入CSV,将其解析为String数组)
- 将读取到的数据写入CSV
其中具体要做的各步骤可归纳为如下流程图:
下面将对各步骤进行说明。
读取nc文件
在读取文件时,同时获取其中的所有变量
// NC文件路径
String ncPath = "这里填nc文件的路径";// 建立nc文件对象
NetcdfFile ncFile = NetcdfFile.open(ncPath);
// 获取所有的变量
List<Variable> variables = ncFile.getVariables();
解析各类变量
根据NetCDF文件中变量的特性,可以分为三类:time、grid_xt、grid_yt等一维变量,经、纬度等不受其他变量影响的以二维表格形式展示的地理位置变量,以及测量所得变量。
因此,对于上述变量,要采用不同的方式进行解析:
- time、grid_xt、grid_yt变量
这类变量本身就是一维的,因此可以直接读取,不需要进行额外的操作。
// 读取变量,并储存到String数组中Array value = variable.read();String[] strings = new String[(int) value.getSize()];for (int i = 0; i < value.getSize(); i++) {Double temp = value.getDouble(i);strings[i] = temp.toString();}
- 经、纬度变量
这类变量虽然表示为二维表格的形式,但本质上还是一维的,但会根据绘制图形的坐标变量grid_xt和grid_yt而进行行列上的重复,故而要对其样式进行整合。
// 读取经度,用grid_xt的大小来控制每行数据量,并将读取到的数据储存到String数组中写入CSV
Array value = variable.read();long num = ncFile.findVariable("grid_xt").getSize();
String[] strings = new String[(int) num];
for (int i = 0; i < variable.getSize(); i++) {Double temp = value.getDouble(i);int index = (int) (i % num);strings[index] = temp.toString();// 整合后写入CSVif((i + 1) % num == 0) ; // 此处填写写入csv文件的指令
}// 读取纬度,用grid_xt的大小来控制每行数据量,并将读取到的数据储存到String数组中写入CSV
Array value = variable.read();long num = ncFile.findVariable("grid_xt").getSize();
String[] strings = new String[(int) num];
for (int i = 0; i < variable.getSize(); i++) {Double temp = value.getDouble(i);int index = (int) (i % num);strings[index] = temp.toString();// 整合后写入CSVif((i + 1) % num == 0); // 此处填写写入csv文件的指令
}
- 测量所得变量
这类型的变量是nc文件中,我们最为关心的数据,但由于其是根据时间、经度、纬度构建的,因此需要对数组进行降维。
// 读取nc数据到数组
Array data = v.read();// 获取参数和索引,其中shape的前三个参数分别是时间、纬度、经度
int[] shape = data.getShape();
Index index = data.getIndex();// 将三维数组降维,并用String数组提取数据
// 按时间
for (int i = 0; i < shape[0]; i++) {// 按维度for (int j = 0; j < shape[1]; j++) {String[] strings = new String[shape[2]];// 按经度for (int k = 0; k < shape[2]; k++) {// 按照对应索引获取数据并转换为string类型添加到数组中Float dval = data.getFloat(index.set(i, j, k));strings[k] = dval.toString();}; // 此处填写写入csv文件的指令}
}
写入CSV
在写入CSV时,需要构建CSV,并根据其输出路径和相关参数进行设置,从而按行将数据输入到CSV中。
注:之所以不按列,是因为openCSV中没有按列写入的操作,如果要按列输入,需要自行对二维数组进行转置,随后再按行输入,但不建议这样做,因为nc文件很大,这样的操作相当消耗内存,容易出现内存溢出的错误。
// 创建CSV
FileOutputStream fileOutputStream = new FileOutputStream(outPath);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);
CSVWriter csvWriter = new CSVWriter(outputStreamWriter, CSVWriter.DEFAULT_SEPARATOR, CSVWriter.NO_QUOTE_CHARACTER, CSVWriter.DEFAULT_ESCAPE_CHARACTER, CSVWriter.DEFAULT_LINE_END);// 将string数组写入CSV
csvWriter.writeNext(strings);// 刷新、关闭CSV文件
csvWriter.flush();
csvWriter.close();
outputStreamWriter.close();
fileOutputStream.close();
涉及到的函数体
此处将解析变量以及写入CSV文件的操作写成了相应函数,其代码如下:
// 读取时间、grid_xt、grid_yt的值,并将其存放在一维数组中
public static void writeDimensionCSV(String outPath, Variable variable) throws IOException, InvalidRangeException {// 创建CSVFileOutputStream fileOutputStream = new FileOutputStream(outPath);OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);CSVWriter csvWriter = new CSVWriter(outputStreamWriter, CSVWriter.DEFAULT_SEPARATOR, CSVWriter.NO_QUOTE_CHARACTER, CSVWriter.DEFAULT_ESCAPE_CHARACTER, CSVWriter.DEFAULT_LINE_END);// 读取维度变量,并储存到String数组中Array value = variable.read();String[] strings = new String[(int) value.getSize()];for (int i = 0; i < value.getSize(); i++) {Double temp = value.getDouble(i);strings[i] = temp.toString();}csvWriter.writeNext(strings);// 刷新、关闭csvWriter.flush();csvWriter.close();outputStreamWriter.close();fileOutputStream.close();}// 读取经度的值,并将其存放在一维数组中
public static void writeLonCSV(NetcdfFile ncFile, String outPath, Variable variable) throws IOException, InvalidRangeException {// 创建CSVFileOutputStream fileOutputStream = new FileOutputStream(outPath);OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);CSVWriter csvWriter = new CSVWriter(outputStreamWriter, CSVWriter.DEFAULT_SEPARATOR, CSVWriter.NO_QUOTE_CHARACTER, CSVWriter.DEFAULT_ESCAPE_CHARACTER, CSVWriter.DEFAULT_LINE_END);// 读取经度,用grid_xt的大小来控制每行数据量,并将读取到的数据储存到String数组中写入CSVArray value = variable.read();long num = ncFile.findVariable("grid_xt").getSize();String[] strings = new String[(int) num];for (int i = 0; i < variable.getSize(); i++) {Double temp = value.getDouble(i);int index = (int) (i % num);strings[index] = temp.toString();// 整合后写入CSVif((i + 1) % num == 0)csvWriter.writeNext(strings);}// 刷新、关闭csvWriter.flush();csvWriter.close();outputStreamWriter.close();fileOutputStream.close();}// 读取纬度的值,并将其存放在一维数组中
public static void writeLatCSV(NetcdfFile ncFile, String outPath, Variable variable) throws IOException, InvalidRangeException {// 创建CSVFileOutputStream fileOutputStream = new FileOutputStream(outPath);OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);CSVWriter csvWriter = new CSVWriter(outputStreamWriter, CSVWriter.DEFAULT_SEPARATOR, CSVWriter.NO_QUOTE_CHARACTER, CSVWriter.DEFAULT_ESCAPE_CHARACTER, CSVWriter.DEFAULT_LINE_END);// 读取纬度,用grid_xt的大小来控制每行数据量,并将读取到的数据储存到String数组中写入CSVArray value = variable.read();long num = ncFile.findVariable("grid_xt").getSize();String[] strings = new String[(int) num];for (int i = 0; i < variable.getSize(); i++) {Double temp = value.getDouble(i);int index = (int) (i % num);strings[index] = temp.toString();// 整合后写入CSVif((i + 1) % num == 0)csvWriter.writeNext(strings);}// 刷新、关闭csvWriter.flush();csvWriter.close();outputStreamWriter.close();fileOutputStream.close();}// 读取变量在任一时间、经度、纬度下的值,并将其存放在二维数组中
public static void writeVariableCSV(String outPath, Variable v) throws IOException, InvalidRangeException {// 读取nc数据到数组Array data = v.read();// 获取参数和索引,其中shape的前三个参数分别是时间、纬度、经度int[] shape = data.getShape();Index index = data.getIndex();// 创建CSVFileOutputStream fileOutputStream = new FileOutputStream(outPath);OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);CSVWriter csvWriter = new CSVWriter(outputStreamWriter, CSVWriter.DEFAULT_SEPARATOR, CSVWriter.NO_QUOTE_CHARACTER, CSVWriter.DEFAULT_ESCAPE_CHARACTER, CSVWriter.DEFAULT_LINE_END);// 将三维数组降维,并用String数组提取数据,将其按行导出到CSV文件// 按时间for (int i = 0; i < shape[0]; i++) {// 按维度for (int j = 0; j < shape[1]; j++) {String[] strings = new String[shape[2]];// 按经度for (int k = 0; k < shape[2]; k++) {// 按照对应索引获取数据并转换为string类型添加到数组中Float dval = data.getFloat(index.set(i, j, k));strings[k] = dval.toString();}csvWriter.writeNext(strings);}}// 刷新、关闭csvWriter.flush();csvWriter.close();outputStreamWriter.close();fileOutputStream.close();}
其他需注意事项
由于在NetCDF文件中,变量的前五项是时间、经度、纬度、grid_xt、grid_yt,因而在对变量进行解析导出时,需要根据其索引判断调用能正确解析相应变量的函数。
// 由于变量的前五项是时间、经度、纬度、grid_xt、grid_yt,都不是二维数组,故要用一维数组的方式进行导出
for (int i = 0; i < 5; i++) {// 得到该索性对应的变量名以形成导出的CSV文件名称String variableName = variables.get(i).getName();String outPath = "D:\\test0\\" + variableName + ".csv";// 根据时间、经纬度、grid_xt/yt的性质,将其按照相应方式写入CSV// 维度变量在nc文件中的顺序为grid_xt、lon、grid_yt、lat、timeswitch (i){case 1:writeLonCSV(ncFile, outPath, variables.get(i));break;case 3:writeLatCSV(ncFile, outPath, variables.get(i));break;default:writeDimensionCSV(outPath, variables.get(i));break;}}// 由于变量的前五项是时间、经度、维度、grid_xt、grid_yt,都不是二维数组,故此处从第六个变量开始写入CSV
for (int i = 5; i < variables.size(); i++) {// 得到该索性对应的变量名以形成导出的CSV文件名称String variableName = variables.get(i).getName();String outPath = "D:\\test0\\" + variableName + ".csv";// 将该变量写入CSVwriteVariableCSV(outPath, variables.get(i));
}
当然,最后需要记得关闭nc文件。
// 关闭nc文件
ncFile.close();
相关测试结果记录
以下测试数据均来源自美国National Centers for Environmental Information (NCEI)的开源数据,为了简化,文件名进行了删减,读者可自行到网站上去下载所需的nc文件。
测试样例一:
文件名:sfcf003.nc
文件大小:262M
导出用时:19.08s
解析后大小:1.26G
测试样例二:
文件名:sfcf000.nc
文件大小:0.98G
导出用时:77.10s
解析后大小:5.08G
参考资料
Introduction to netCDF – Data Carpentry for Oceanographers
NetCDF-Java Online Tutorial | netCDF-Java Documentatid
National Centers for Environmental Information (NCEI)
基于Java的NetCDF文件解析相关推荐
- dom4j工具类_基于DOM4J的XML文件解析类
XML文件解析分四类方式:DOM解析:SAX解析:JDOM解析:DOM4J解析.其中前两种属于基础方法,是官方提供的平台无关的解析方式:后两种属于扩展方法,它们是在基础的方法上扩展出来的,只适用于ja ...
- java实现Excel文件解析---apache POI以及把汉字转化为拼音
java实现Excel文件解析----apache POI以及把汉字转化为拼音 1.POI简介 Apache POI是Apache软件基金会的开放源码函式库,POI提供给Java程序对Microso ...
- java怎么xml文件解析_Java对Xml文件解析
JAVA 解析 XML 通常有两种方式,DOM 和 SAX. DOM 虽然是 W3C 的标准,提供了标准的解析方式,但它的解析效率一直不尽如人意,因为使用DOM解析XML时,解析器读入整个文档并构建一 ...
- java中 Excel文件解析及超大Excel文件读写
本文主要对Excel中数据的解析和生成进行总结 前言 在应用程序的开发过程中,我们经常要用到Excel进行数据的导入或导出.所以,在通过Java语言实现此类需求时,通常会对Excel文件进行解析或生成 ...
- Java中的文件解析——Excel解析
在日常的开发过程中,经常会使用到excel工作簿进行数据的保存,那么在java中,通常会使用第三方提供的技术来进行excel文件的解析,比如:Apache POI.JXL.Alibaba EasyEx ...
- 基于c的xml文件解析(转)
libxml(一) 摘要 Libxml是个有免费许可的用于处理XML.能轻松跨越多个平台的C语言库.这个指南提供他的基本函数的例子. 绪论 Libxml是个实现读.创建及操纵XML数据功能的C语言库. ...
- 【E文件解析】Java实现E文件解析为对象
参照一位大佬贡献的代码修改的(扩展了对象解析,修改了一些bug,比如流异常未关闭) E语言解析包_e文件-Java代码类资源-CSDN下载 封装好了,三行代码解析为对象 github: https: ...
- 基于java的excel_基于Java的Excel文件操作
电脑知识与技术 本栏目责任编辑: 王力计算机教育 1 引言在 Web 应用日益盛行的今天, 通过 Web 来操作 Excel 文件的需求越来越强烈, 目前较为流行的操作是在 JSP 或 Servlet ...
- java ios乱码_相同的后台java代码,txt文件解析,安卓解析正常,IOS却是乱码,PC解析也正常.......
中文乱码,之所以出现这种现象,根本原因是解析和编码所按照的字符集不 同,而字符集是什么呢? ``` 字符集(Character set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同, ...
最新文章
- 机器学习-线性回归LinearRegression
- CART决策树(分类回归树)分析及应用建模
- java与c/c++进行socket通信的一些问题
- linux 每周日执行脚本,Linux任务计划和周期性任务执行
- width:100vh与min-height:calc(100vh + 51px)
- 【BZOJ - 1059】矩阵游戏(二分图匹配,建图,最小边覆盖)
- 【转】VS2017的VSIX插件开发
- 众专家推荐《移动微技(Mobile Widget)应用开发权威指南》
- 中国书法之美 No。2017-08-30
- PostgreSQL column cryptographic use pgcrypto extension and optional openssl lib
- deficit记忆_英语单词分类记忆 高效快速的记忆法
- 柴静:我只是讨厌屈服
- html 设计尺寸,多少像素才合适 网页设计标准尺寸大讲解
- 计算机磁盘管理没有打开方式,Win10系统双击磁盘和文件夹打不开提示没有与之关联的程序怎么办...
- idea上传写好的springboot项目到码市coding
- (三)Linux vi 文本编辑器,用户管理,组管理,Sudo命令,时间管理
- 圣诞音乐贺卡beepMusic_v6d;--铃儿响叮当;
- firefox插件推荐
- 语音计算机打字教程,win7电脑打字使用语音输入法打字的超详细教程
- java之getResource方法