近期在做的项目中,需要使用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文件解析相关推荐

  1. dom4j工具类_基于DOM4J的XML文件解析类

    XML文件解析分四类方式:DOM解析:SAX解析:JDOM解析:DOM4J解析.其中前两种属于基础方法,是官方提供的平台无关的解析方式:后两种属于扩展方法,它们是在基础的方法上扩展出来的,只适用于ja ...

  2. java实现Excel文件解析---apache POI以及把汉字转化为拼音

    java实现Excel文件解析----apache  POI以及把汉字转化为拼音 1.POI简介 Apache POI是Apache软件基金会的开放源码函式库,POI提供给Java程序对Microso ...

  3. java怎么xml文件解析_Java对Xml文件解析

    JAVA 解析 XML 通常有两种方式,DOM 和 SAX. DOM 虽然是 W3C 的标准,提供了标准的解析方式,但它的解析效率一直不尽如人意,因为使用DOM解析XML时,解析器读入整个文档并构建一 ...

  4. java中 Excel文件解析及超大Excel文件读写

    本文主要对Excel中数据的解析和生成进行总结 前言 在应用程序的开发过程中,我们经常要用到Excel进行数据的导入或导出.所以,在通过Java语言实现此类需求时,通常会对Excel文件进行解析或生成 ...

  5. Java中的文件解析——Excel解析

    在日常的开发过程中,经常会使用到excel工作簿进行数据的保存,那么在java中,通常会使用第三方提供的技术来进行excel文件的解析,比如:Apache POI.JXL.Alibaba EasyEx ...

  6. 基于c的xml文件解析(转)

    libxml(一) 摘要 Libxml是个有免费许可的用于处理XML.能轻松跨越多个平台的C语言库.这个指南提供他的基本函数的例子. 绪论 Libxml是个实现读.创建及操纵XML数据功能的C语言库. ...

  7. 【E文件解析】Java实现E文件解析为对象

    参照一位大佬贡献的代码修改的(扩展了对象解析,修改了一些bug,比如流异常未关闭) E语言解析包_e文件-Java代码类资源-CSDN下载 封装好了,三行代码解析为对象 github:  https: ...

  8. 基于java的excel_基于Java的Excel文件操作

    电脑知识与技术 本栏目责任编辑: 王力计算机教育 1 引言在 Web 应用日益盛行的今天, 通过 Web 来操作 Excel 文件的需求越来越强烈, 目前较为流行的操作是在 JSP 或 Servlet ...

  9. java ios乱码_相同的后台java代码,txt文件解析,安卓解析正常,IOS却是乱码,PC解析也正常.......

    中文乱码,之所以出现这种现象,根本原因是解析和编码所按照的字符集不 同,而字符集是什么呢? ``` 字符集(Character set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同, ...

最新文章

  1. 机器学习-线性回归LinearRegression
  2. CART决策树(分类回归树)分析及应用建模
  3. java与c/c++进行socket通信的一些问题
  4. linux 每周日执行脚本,Linux任务计划和周期性任务执行
  5. width:100vh与min-height:calc(100vh + 51px)
  6. 【BZOJ - 1059】矩阵游戏(二分图匹配,建图,最小边覆盖)
  7. 【转】VS2017的VSIX插件开发
  8. 众专家推荐《移动微技(Mobile Widget)应用开发权威指南》
  9. 中国书法之美 No。2017-08-30
  10. PostgreSQL column cryptographic use pgcrypto extension and optional openssl lib
  11. deficit记忆_英语单词分类记忆 高效快速的记忆法
  12. 柴静:我只是讨厌屈服
  13. html 设计尺寸,多少像素才合适 网页设计标准尺寸大讲解
  14. 计算机磁盘管理没有打开方式,Win10系统双击磁盘和文件夹打不开提示没有与之关联的程序怎么办...
  15. idea上传写好的springboot项目到码市coding
  16. (三)Linux vi 文本编辑器,用户管理,组管理,Sudo命令,时间管理
  17. 圣诞音乐贺卡beepMusic_v6d;--铃儿响叮当;
  18. firefox插件推荐
  19. 语音计算机打字教程,win7电脑打字使用语音输入法打字的超详细教程
  20. java之getResource方法

热门文章

  1. Markdown——图片、文字显示居中的一种方法
  2. 手机号 mysql 索引_mysql索引以及优化
  3. 靶场练习第十四天~vulnhub靶场之dc-6
  4. 人工智能+区块链+云协同赋能工业制造
  5. openstack 网络更改版
  6. 神奇宝贝五分类:数据预处理,可以推广到任意图片集
  7. 用python爬取今日头条上的图片_Python爬虫:抓取今日头条图集
  8. 心率检测实现报告(二)
  9. android视频播放边播边缓存
  10. 人工神经网络神经元模型,人工神经元算法机制图