NetCDF数据结构与解析

什么是NetCDF

NetCDF(网络公用数据格式)是一种用来存储温度、湿度、气压、风速和风向等多维科学数据(变量)的文件格式。
NetCDF包含以下特性

  • 自描述性,即 netCDF 文件包括关于其中所含数据的信息,如捕获数据元素的时间以及使用的测量单位。
  • 可移植性,或称跨平台性,即在一种操作系统上创建的 netCDF 文件通常可被其他操作系统上的软件读取。
  • 可扩展性,即可有效地读取一个大 netCDF 文件的一个小子集,而无需读取整个文件。
    在ArcGis中可以使用Netcdf数据。

NETCDF的基本结构

  • 维度(Dimensions)

    NetCDF 维度包含名称和大小。维度大小是一个任意的正整数。每个 NetCDF 文件中只有一个维度的大小是“无限制”的。这类维度是无限维度或记录维度。无限维度的变量可以沿着该维度增加到任意长度。

  • 变量(Variables)

    变量代表相同类型的值数组。变量用来存储 NetCDF 文件中的大部分数据。变量具有名称、数据类型以及在创建该变量时指定的维度列表所描述的 shape。维度数称为秩(或维数)。标量变量的秩为 0,矢量的秩为 1,矩阵的秩为 2。变量还可以具有能够在变量创建后进行添加、删除或更改的相关属性。

  • 坐标变量()

    与维度同名的一维变量称为坐标变量。坐标变量与一个或多个数据变量的维度相关,通常用来定义与该维度相对应的物理坐标。

    坐标变量对于 NetCDF 库来说没有特殊意义。但使用这个库的软件会采用一种特殊的方式来处理坐标变量。

  • 属性(Attributes)

    NetCDF 的属性用于存储辅助数据或元数据。大部分属性提供有关特定变量的信息。这些属性由变量名称与属性名称共同标识。

  • 约定(Conventions)

    约定用来定义为每个变量中的数据提供确切描述的元数据及其空间和时态属性。约定有助于使用不同数据源的用户确定哪些量具有可比性。约定名称在 NetCDF 文件中以全局属性的形式表示。

    目前,ArcGIS 支持气候和预测 (CF) 以及海洋/大气合作研究数据服务 (COARDS) 约定。然而,它可能也支持采用其他约定创建的 NetCDF 文件。

  • 存储结构如下图所示

    3d
    4d

  • 采用 CDL(网络公用数据格式语言)表示法来描述的 NetCDF 文件结构

    4d

如何创建或读取netcdf数据

参考官方文档https://www.unidata.ucar.edu/software/netcdf/docs/index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//定义维度 当len为0L时,创建不定长度维度,每个变量只允许有一个。
public static extern int nc_def_dim(int ncid, string name, int len, out int dimidp);
//定义变量
public static extern int nc_def_var(int ncid, string name, NcType xtype, int ndims, int[] dimids, out int varidp);
//整体写入变量
public static extern int nc_put_var_int(int ncid, int varid, int[] data);
public static extern int nc_put_var_int(int ncid, int varid, int[,] data);
//写入单条数据
public static extern int nc_put_var1_int(int ncid, int varid, int[] indexp, int data);
//写入部分数据
public static extern int nc_put_vara_int(int ncid, int varid, int[] startp, int[] countp, int[] data);
//未知
public static extern int nc_put_varm_int(int ncid, int varid, int[] startp, int[] );
public static extern int nc_put_vars_int(int ncid, int varid, int[] startp, int[] countp, int[] stridep, int[] data);
///压缩算法,shuffle辅助压缩算法,defalte是否压缩deflate_level压缩等级
public static extern int nc_def_var_deflate(int ncid, int varid, int shuffle, int deflate, int deflate_level);
//加入属性 当varid为-1时,将加入全局属性
public static extern int nc_put_att_text(int ncid, int varid, string name, int len, string tp);

如何创建CF COARDS约定的数据集

按照以下格式创建的NC为CF1.0约定的数据集文件

cf-1.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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
public bool TifToNC3DSingleVarByYear(string TifPath, string NcPath, int year)
{
bool flag = false;
try
{
List<String> tifList = Directory.GetFiles(TifPath, "*.tif").ToList();
Dataset dataset = Gdal.Open(tifList[0], Access.GA_ReadOnly);
int width = dataset.RasterXSize;
int heigh = dataset.RasterYSize;
int timeCount = tifList.Count;
//创建nc文件
int ncid;
int result = NetCDF.nc_create(NcPath, NetCDF.CreateMode.NC_NETCDF4, out ncid);
//创建维度
int lonDimidp;
result = NetCDF.nc_def_dim(ncid, "longitude", width, out lonDimidp);
int latDimidp;
result = NetCDF.nc_def_dim(ncid, "latitude", heigh, out latDimidp);
result = NetCDF.nc_def_var(ncid, "longitude", NetCDF.NcType.NC_FLOAT, 1, new int[] { lonDimidp }, out int lonVaridp);
result = NetCDF.nc_def_var(ncid, "latitude", NetCDF.NcType.NC_FLOAT, 1, new int[] { latDimidp }, out int latVaridp);
float[] lon = new float[7200];
for (int i = 0; i < 7200; i++)
{
lon[i] = -180 + 0.05f * i;
}
float[] lat = new float[3600];
for (int i = 0; i < 3600; i++)
{
lat[i] = 90 - 0.05f * i;
}
NetCDF.nc_put_var_float(ncid, lonVaridp, lon);
NetCDF.nc_put_att_text(ncid, lonVaridp, "units", "degrees_east".Length, "degrees_east");
NetCDF.nc_put_att_text(ncid, lonVaridp, "long_name", "longitude".Length, "longitude");
NetCDF.nc_put_var_float(ncid, latVaridp, lat);
NetCDF.nc_put_att_text(ncid, latVaridp, "units", "degrees_north".Length, "degrees_north");
NetCDF.nc_put_att_text(ncid, latVaridp, "long_name", "latitude".Length, "latitude");
result = put_var_year("time", "snw", tifList, ref dataset, ncid, lonDimidp, latDimidp);
result = NetCDF.nc_close(ncid);
flag = true;
}
catch (Exception ex)
{
flag = false;
}
return flag;
}

private int put_var_year(string dimname, string varname, List<string> tifList, ref Dataset dataset, int ncid, int lonDimidp, int latDimidp)
{
tifList = tifList.OrderBy(p => p).ToList();
int timeCount = tifList.Count;
int[] time = new int[timeCount];
string timeStr = Path.GetFileName(tifList[0]).Split("_")[3];
DateTime firstDay = DateTime.ParseExact(timeStr, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture);
int firstDaysInt = (firstDay - new DateTime(1900, 1, 1)).Days;
for (int i = 0; i < timeCount; i++)
{
time[i] = i + firstDaysInt;
}
int result;
int timeDimidp;
result = NetCDF.nc_def_dim(ncid, dimname, timeCount, out timeDimidp);
result = NetCDF.nc_def_var(ncid, dimname, NetCDF.NcType.NC_INT, 1, new int[] { timeDimidp }, out int timeVaridp);
NetCDF.nc_put_var_int(ncid, timeVaridp, time);
NetCDF.nc_put_att_text(ncid, timeVaridp, "units", "days since 1900-01-01 00:00:0.0".Length, "days since 1900-01-01 00:00:0.0");
NetCDF.nc_put_att_text(ncid, timeVaridp, "long_name", dimname.Length, dimname);
NetCDF.nc_put_att_text(ncid, timeVaridp, "calendar", "gregorian".Length, "gregorian");
int varidp;
int[] dimids = new int[3];
dimids[0] = timeDimidp;
dimids[1] = latDimidp;
dimids[2] = lonDimidp;
result = NetCDF.nc_def_var(ncid, varname, NetCDF.NcType.NC_BYTE, 3, dimids, out varidp);
//加入压缩算法
result = NetCDF.nc_def_var_deflate(ncid, varidp, 0, 1, 5);
result = NetCDF.nc_enddef(ncid);
LongArray<byte> buffer = new LongArray<byte>(timeCount * 3600L * 7200L);
for (int i = 0; i < timeCount; i++)
{
dataset = Gdal.OpenShared(tifList[i], Access.GA_ReadOnly);
Band srcBand = dataset.GetRasterBand(1);
//读取数据为数组的数组
int[,] writeBuffer = ReadBandToArray<int>(srcBand);
//值转换
for (int m = 0; m < 3600; m++)
{
for (int n = 0; n < 7200; n++)
{
if (writeBuffer[m, n] == 200)
buffer[i*3600L*7200L+m*7200L+n] = 1;
}
}
}
result = NetCDF.nc_put_var_ubyte(ncid, varidp, buffer._getAddress(0));
buffer.Dispose();
return result;
}