C++读取netcdf文件

引自:https://blog.csdn.net/laomai/article/details/1740747
https://blog.csdn.net/qq_37962333/article/details/104369898
基础知识:
一、概述
NetCDF全称为network Common Data Format,中文译法为“网络通用数据格式”,
对程序员来说,它和zip、jpeg、bmp文件格式类似,都是一种文件格式的标准。netcdf
文件开始的目的是用于存储气象科学中的数据,现在已经成为许多数据采集软件的生成文件
的格式。
从数学上来说,netcdf存储的数据就是一个多自变量的单值函数。用公式来说就是
f(x,y,z,…)=value, 函数的自变量x,y,z等在netcdf中叫做维(dimension)
或坐标轴(axix),函数值value在netcdf中叫做变量(Variables).而自变量和函数值
在物理学上的一些性质,比如计量单位(量纲)、物理学名称等等
在netcdf中就叫属性(Attributes).

二、netcdf的下载
netcdf的是官方网站为http://www.unidata.ucar.edu/software/netcdf/。
在本文中,我们主要讨论在windows平台上使用netcdf软件库。我们将要从这个网站上
下载如下资源
⑴netcdf的源代码,目前的地址为
ftp://ftp.unidata.ucar.edu/pub/netcdf/netcdf-4/netcdf-beta.tar.gz
⑵netcdf的在windows平台预编译好的dll,地址为
ftp://ftp.unidata.ucar.edu/pub/netcdf/contrib/win32/netcdf-3.6.1-win32.zip
解压后里面有如下东西
netcdf.dll 为编译好的dll
ncgen.exe 为生成netcdf文件的工具
ncdump.exe 为读取netcdf文件的工具
netcdf.lib 和 netcdf.exp在编程时会用到,后面会讲。
⑶netcdf的相关文档,包括
①netcdf的用户手册,下载地址为http://www.unidata.ucar.edu/software/netcdf/docs/netcdf.pdf
②netcdf的入门教程, 下载地址为http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-tutorial.pdf
③netcdf的c接口api手册,下载地址为http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-c.pdf
下面我们来看netcdf文件的具体内容。

三、netcdf文件的内容
一个netcdf文件的结构包括以下对象:
1、变量(Variables)
变量对应着真实的物理数据。比如我们家里的电表,每个时刻显示的读数表示用户的到该时刻的耗电量。
这个读数值就可以用netcdf里的变量来表示。它是一个以时间为自变量(或者说自变量个数为一维)的单值
函数。再比如在气象学中要作出一个气压图,就是“东经xx度,北纬yy度的点的大气压值为多少帕”,这是
一个二维单值函数,两维分别是经度和纬度。函数值为大气压。
从上面的例子可以看出,netcdf中的变量就是一个N维数组,数组的维数就是实际问题中的自变量个数,
数组的值就是观测得到的物理值。变量(数组值)在netcdf中的存储类型有六种,ascii字符(char) ,字节(byte), 短整型(short), 整型(int), 浮点(float), 双精度(double). 显然这些类型和c中的类型一致,搞C的朋友应该很快就能明白。
2、维(dimension)
一个维对应着函数中的某个自变量,或者说函数图象中的一个坐标轴,在线性代数中就是一个N维向量
的一个分量(这也是维这个名称的由来)。在netcdf中,一个维具有一个名字和范围(或者说长度,也就
是数学上所说的定义域,可以是离散的点集合或者连续的区间)。在netcdf中,维的长度基本都是有限的,
最多只能有一个具有无限长度的维。
3、属性(Attribute)
属性对变量值和维的具体物理含义的注释或者说解释。因为变量和维在netcdf中都只是无量纲的数字,
要想让人们明白这些数字的具体含义,就得靠属性这个对象了。
在netcdf中,属性由一个属性名和一个属性值(一般为字符串)组成。比如,在某个cdl文件
(cdl文件的具体格式在下一节中讲述)中有这样的代码段
temperature:units = “celsius” ;
前面的temperature是一个已经定义好的变量(Variable),即温度,冒号后面的units就是属性名,
表示物理单位,=后面的就是units这个属性的值,为“celsius” ,即摄氏度,整个一行代码的意思就是
温度这个物理量的单位为celsius,很好理解。

三、CDL结构
CDL全称为network Common data form Description Language,它是用来描述netcdf文件
的结构的一种语法格式。它包括前面所说的三种netcdf对象(变量、维、属性)的具体定义。
看一个具体例子(这个例子cdl文件是从netcdf教程中的2.1 节The simple xy Example摘出来的)
netcdf simple_xy {
dimensions:
x = 6 ;
y = 12 ;
variables:
int data(x, y) ;
data:
data =
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71 ;
}
上面的代码定义了一个符合netcdf格式的结构simple_xy,
这个结构包括三个部分
1、维的定义,以dimensions:关键字开头
dimensions:
x = 6 ;
y = 12 ;
定义了两个轴(或者说两维),名字分别为x和y,x轴的长度(准确的说是坐标点的个数)为6,
y轴的长度为12。
2、变量的定义:以variables:开头
variables:
int data(x, y);
定义了一个以x轴和y轴为自变量的函数data,数学公式就是f(x,y)=data;
注意维出现的顺序是有序的,它决定data段中的具体赋值结果.
3、数据的定义,以data:开头
data:
data =
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71 ;
这个段数据用数学的函数公式f(x,y)=data来看,
就是 x=0,y=0时,data = 0;
x=0,y=1时,data = 1;

 x=5,y=11是,data=71;

要注意的是,
1、赋值顺序:
我们给出的是c格式的cdl文件,因此这里的赋值顺序和c语言中的一致,也就是通常所说的“行式赋值”,
而fortran语言中则是“列式赋值”,因此在fortran格式的cdl文件中,data段的数值顺序和这里正好
行列互换。
2、自变量的默认取值和坐标变量
如果只给出维的长度,那么维的值默认从0开始,然后自动加1,到(长度-1)停止,
很多情况下我们要自己给出每个点的坐标值,这时就需要用到netcdf里的坐标变量
“coordinate varibles”:增加一个和只和维相关的一元函数(自变量)并给出它的取值范围。
比如下面的cdl文件(摘自netcdf教程中的2.2 The sfc pres temp Example)
netcdf sfc_pres_temp {
dimensions:
latitude = 6 ; //纬度轴
longitude = 12 ; //经度轴
variables:
float latitude(latitude) ; //坐标变量,存储具体纬度
latitude:units = “degrees_north” ;
float longitude(longitude) ; //坐标变量,存储具体纬度
longitude:units = “degrees_east” ;
float pressure(latitude, longitude) ; //某个点(经度和纬度的交点)的大气压值
pressure:units = “hPa” ; //大气压的单位为
float temperature(latitude, longitude) ; //某个点(经度和纬度的交点)的温度值
temperature:units = “celsius” ; //温度的单位为
data:
latitude = 25, 30, 35, 40, 45, 50 ;
longitude = -125, -120, -115, -110, -105, -100, -95, -90, -85, -80, -75, -70 ;
pressure =
900, 906, 912, 918, 924, 930, 936, 942, 948, 954, 960, 966,
901, 907, 913, 919, 925, 931, 937, 943, 949, 955, 961, 967,
902, 908, 914, 920, 926, 932, 938, 944, 950, 956, 962, 968,
903, 909, 915, 921, 927, 933, 939, 945, 951, 957, 963, 969,
904, 910, 916, 922, 928, 934, 940, 946, 952, 958, 964, 970,
905, 911, 917, 923, 929, 935, 941, 947, 953, 959, 965, 971 ;
temperature =
9, 10.5, 12, 13.5, 15, 16.5, 18, 19.5, 21, 22.5, 24, 25.5,
9.25, 10.75, 12.25, 13.75, 15.25, 16.75, 18.25, 19.75, 21.25, 22.75, 24.25,
25.75,
9.5, 11, 12.5, 14, 15.5, 17, 18.5, 20, 21.5, 23, 24.5, 26,
9.75, 11.25, 12.75, 14.25, 15.75, 17.25, 18.75, 20.25, 21.75, 23.25, 24.75,
26.25,
10, 11.5, 13, 14.5, 16, 17.5, 19, 20.5, 22, 23.5, 25, 26.5,
10.25, 11.75, 13.25, 14.75, 16.25, 17.75, 19.25, 20.75, 22.25, 23.75,
25.25
对于上面的数据,就是
latitude = 25,longitude = -125时,pressure = 900,temperature = 9;
latitude = 25,longitude = -120时,pressure = 906,temperature = 10.5;
以此类推。

文件读写方法:

1.下载对应的NetCDF-C和C++库:官网下载
在这里插入图片描述

选择64位的NetCDF4安装版(没有DAP的)
在这里插入图片描述

下载4.3.1的版本
2.项目属性配置
(1)安装exe及解压压缩文件,得到两个文件夹
(2)把netcdf-cxx4-4.3.1–>cxx4文件中的netcdf改名为netcdfcpp.h
(3)VS配置管理器设置为Release和x64
(4)项目–>属性–>VC++目录–>包含目录 添加netcdf-cxx4-4.3.1中的cxx4文件夹和netCDF 4.7.3中的include文件夹
(5)项目–>属性–>VC++目录–>库目录 添加netCDF 4.7.3中的lib文件夹
(6)项目–>属性–>链接器–>常规–>附加库目录 添加netCDF 4.7.3中的bin文件夹
(7)项目–>属性–>链接器–>输入–>附加依赖项 输入netcdf.lib
(8)项目–>属性–>C/C+±->预编译头–>不使用预编译头

3.把cxx4文件中的xxx.h xxx.cpp复制到项目头文件和源文件中(test_xxx是测试都不要)
4.测试代码:
写入:

#include <iostream>
#include "netcdfcpp.h"
#include <vector>
using namespace std;
using namespace netCDF;
using namespace netCDF::exceptions;

static const int NX = 6;
static const int NY = 12;

static const int NC_ERR = 2;

int main()
{
	int dataOut[NX][NY];

	for (int i = 0; i < NX; i++)
		for (int j = 0; j < NY; j++)
			dataOut[i][j] = i * NY + j;

	try
	{
		NcFile dataFile("simple_xy.nc", NcFile::replace);

		NcDim xDim = dataFile.addDim("x", NX);
		NcDim yDim = dataFile.addDim("y", NY);

		vector<NcDim> dims;
		dims.push_back(xDim);
		dims.push_back(yDim);
		NcVar data = dataFile.addVar("data", ncInt, dims);

		data.putVar(dataOut);

		return 0;
	}
	catch (NcException& e)
	{
		e.what();
		return NC_ERR;
	}
}

读取:

#include <iostream>
#include "netcdfcpp.h"
using namespace std;
using namespace netCDF;
using namespace netCDF::exceptions;

static const int NX = 6;
static const int NY = 12;

static const int NC_ERR = 2;

int main()
{
	try
	{
		int dataIn[NX][NY];

		NcFile dataFile("simple_xy.nc", NcFile::read);

		NcVar data = dataFile.getVar("data");
		if (data.isNull()) return NC_ERR;
		data.getVar(dataIn);

		for (int i = 0; i < NX; i++)
			for (int j = 0; j < NY; j++)
			{
				if (dataIn[i][j] != i * NY + j)
					return NC_ERR;
				else
				{
					cout << dataIn[i][j] << endl;
				}

			}
		return 0;
	}
	catch (NcException& e)
	{
		e.what();
		cout << "FAILURE*************************************" << endl;
		return NC_ERR;
	}
}

可能遇到的问题:
1.NcFile dataFile(“nc文件的路径”, NcFile::read);文件路径中的反斜杠都要换成正斜杠,"\"也不行的
2.同样的配置方法在低版本VS中可能不能成功(2015可以一试)
3.缺少xxx.dll:在文件夹里找到对应文件复制到C:\Windows\system32下
4.error C4996: ‘strcpy’: This function or variable may be unsafe. Consider using strcpy_s instead.:项目–>属性–>C/C++>预处理器–>预处理器定义 添加_CRT_SECURE_NO_WARNINGS

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值