用NetCDF建立和讀取NC檔案
參考網站: 1.https://www.unidata.ucar.edu/ 2.http://crawler.iteye.com/blog/1059995 3.https://www.unidata.ucar.edu/software/netcdf/examples/programs/ NetCDF版本:netcdf-4.6.6.jar JDK:1.8 一、NetCDF簡介 NetCDF全稱為network Common Data Format( “網路通用資料格式”),是一個軟體庫與機器無關的資料格式,支援建立,訪問基於陣列的科研資料。分別提供了對Java和C / C++ / Fortran語言。 對程式設計師來說,它和zip、jpeg、bmp檔案格式類似,都是一種檔案格式的標準。netcdf檔案開始的目的是用於儲存氣象科學中的資料,現在已經成為許多資料採集軟體的生成檔案的格式。 從數學上來說,netcdf儲存的資料就是一個多自變數的單值函式。用公式來說就是f(x,y,z,…)=value, 函式的自變數x,y,z等在netcdf中叫做維(dimension)或座標軸(axix),函式值value在netcdf中叫做變數(Variables)。而自變數和函式值在物理學上的一些性質,比如計量單位(量綱)、物理學名稱在netcdf中就叫屬性(Attributes)。
NetCDF可處理的資料種類如下: Available Data Types:(現有資料型別) Forecast Model Output:出型預報 Satellite Data:衛星資料 Radar Data:雷達資料 Lightning Data:閃電資料 Wind Profiler Data:風分析的資料 Aircraft-Borne (ACARS):航空傳播資料 GPS Meteo. (SuomiNet):GPS資料
二、NetCDF檔案結構 1. 變數(Variables) 變數對應著真實的物理資料。比如我們家裡的電錶,每個時刻顯示的讀數表示使用者的到該時刻的耗電量。這個讀數值就可以用netcdf裡的變數來表示。它是一個以時間為自變數(或者說自變數個數為一維)的單值函式。再比如在氣象學中要作出一個氣壓圖,就是“東經xx度,北緯yy度的點的大氣壓值為多少帕”,這是一個二維單值函式,兩維分別是經度和緯度。函式值為大氣壓。 從上面的例子可以看出,netcdf中的變數就是一個N維陣列,陣列的維數就是實際問題中的自變數個數,陣列的值就是觀測得到的物理值。變數(陣列值)在netcdf中的儲存型別有六種,ascii字元(char) ,位元組(byte), 短整型(short), 整型(int), 浮點(float), 雙精度(double)。 2. 維(dimension)
三、NetCDF樣例檔案 1.網上檔案(模擬4維資料)
netcdf pres_temp_4D {
dimensions:
level = 2 ;
latitude = 6 ;
longitude = 12 ;
time = UNLIMITED ; // (2 currently)
variables:
float latitude(latitude) ;
latitude:units = "degrees_north" ;
float longitude(longitude) ;
longitude:units = "degrees_east" ;
float pressure(time, level, latitude, longitude) ;
pressure:units = "hPa" ;
float temperature(time, level, 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, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911,
912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923,
924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935,
936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947,
948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959,
960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971,
972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983,
984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995,
996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007,
1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019,
1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031,
1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043,
900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911,
912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923,
924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935,
936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947,
948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959,
960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971,
972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983,
984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995,
996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007,
1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019,
1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031,
1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043 ;
temperature =
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, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
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, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152 ;
}
2.實際工作中用到的是全球模式EC的10U,10V風場資料(真實資料),資料格式如下:
四、NetCDF檔案建立和寫入 以上述的模擬4維資料為例:
package examples;
import java.io.IOException;
import java.util.ArrayList;
import ucar.ma2.ArrayFloat;
import ucar.ma2.DataType;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.Attribute;
import ucar.nc2.Dimension;
import ucar.nc2.NetcdfFileWriter;
import ucar.nc2.Variable;
public class Pres_temp_4D_wr {
public static void main(String[] args) {
final int NLVL = 2;
final int NLAT = 6;
final int NLON = 12;
final int NREC = 2;
final float SAMPLE_PRESSURE = 900.0f;
final float SAMPLE_TEMP = 9.0f;
final float START_LAT = 25.0f;
final float START_LON = -125.0f;
// 定義檔名
String filename = "pres_temp_4D.nc";
NetcdfFileWriter dataFile = null;
try {
//建立netcdf3檔案
dataFile = NetcdfFileWriter.createNew(NetcdfFileWriter.Version.netcdf3, filename);
//定義維度(dimensions)
Dimension lvlDim = dataFile.addDimension(null, "level", NLVL);
Dimension latDim = dataFile.addDimension(null, "latitude", NLAT);
Dimension lonDim = dataFile.addDimension(null, "longitude", NLON);
Dimension timeDim = dataFile.addUnlimitedDimension("time");
// 定義座標變數(Variable)
Variable latVar = dataFile.addVariable(null, "latitude", DataType.FLOAT, "latitude");
Variable lonVar = dataFile.addVariable(null, "longitude", DataType.FLOAT, "longitude");
// 定義變數的屬性(Attribute)
dataFile.addVariableAttribute(latVar, new Attribute("units", "degrees_north"));
dataFile.addVariableAttribute(lonVar, new Attribute("units", "degrees_east"));
//定義資料變數:溫度和氣壓
// ArrayList<Dimension> dims = new ArrayList<Dimension>();
// dims.add(lvlDim);
// dims.add(latDim);
// dims.add(lonDim);
String dims = "time level latitude longitude";
Variable presVar = dataFile.addVariable(null, "pressure", DataType.FLOAT, dims);
Variable tempVar = dataFile.addVariable(null, "temperature", DataType.FLOAT, dims);
// 定義資料屬性(Attribute)
dataFile.addVariableAttribute(presVar, new Attribute("units", "hPa"));
dataFile.addVariableAttribute(tempVar, new Attribute("units", "celsius"));
//定義一維陣列(為座標設定數值)
ArrayFloat.D1 lats = new ArrayFloat.D1(latDim.getLength());
ArrayFloat.D1 lons = new ArrayFloat.D1(lonDim.getLength());
int i, j;
for (i = 0; i < latDim.getLength(); i++) {
lats.set(i, START_LAT + 5.f * i);
}
for (j = 0; j < lonDim.getLength(); j++) {
lons.set(j, START_LON + 5.f * j);
}
// 定義4維陣列(為資料設定值)
ArrayFloat.D4 dataTemp = new ArrayFloat.D4(NREC, lvlDim.getLength(), latDim.getLength(), lonDim.getLength());
ArrayFloat.D4 dataPres = new ArrayFloat.D4(NREC, lvlDim.getLength(), latDim.getLength(), lonDim.getLength());
for (int record = 0; record < NREC; record++) {
i = 0;
for (int lvl = 0; lvl < NLVL; lvl++)
for (int lat = 0; lat < NLAT; lat++)
for (int lon = 0; lon < NLON; lon++) {
dataPres.set(record, lvl, lat, lon, SAMPLE_PRESSURE + i);
dataTemp.set(record, lvl, lat, lon, SAMPLE_TEMP + i++);
}
}
//建立NC檔案
dataFile.create();
// A newly created Java integer array to be initialized to zeros.
int[] origin = new int[4];
//資料寫入NC檔案
dataFile.write(latVar, lats);
dataFile.write(lonVar, lons);
dataFile.write(presVar, origin, dataPres);
dataFile.write(tempVar, origin,dataTemp);
} catch (IOException e) {
e.printStackTrace(System.err);
} catch (InvalidRangeException e) {
e.printStackTrace(System.err);
} finally {
if (dataFile != null)
try {
dataFile.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
System.out.println("*** SUCCESS writing example file " + filename);
}
}
程式執行結果:
五、NetCDF檔案讀取
package examples;
import java.io.IOException;
import java.util.List;
import ucar.ma2.ArrayFloat;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.Dimension;
import ucar.nc2.NCdumpW;
import ucar.nc2.NetcdfFile;
import ucar.nc2.Variable;
public class Pres_temp_4D_rd {
public static void main(String[] args) {
// TODO Auto-generated method stub
final int NLVL = 2;
final int NLAT = 6;
final int NLON = 12;
// These are used to construct some example data.
final float SAMPLE_PRESSURE = 900.0f;
final float SAMPLE_TEMP = 9.0f;
final float START_LAT = 25.0f;
final float START_LON = -125.0f;
String filename = "pres_temp_4D.nc";
NetcdfFile dataFile = null;
try {
dataFile = NetcdfFile.open(filename, null);
List<Dimension> dimensions = dataFile.getDimensions();
System.out.println("dimension:");
for(Dimension dimension:dimensions){
System.out.println(dimension);
}
// Get the latitude and longitude Variables.
Variable latVar = dataFile.findVariable("latitude");
if (latVar == null) {
System.out.println("Cant find Variable latitude");
return;
}
System.out.println();
Variable lonVar = dataFile.findVariable("longitude");
if (lonVar == null) {
System.out.println("Cant find Variable longitude");
return;
}
System.out.println(NCdumpW.printVariableData(latVar, null));
System.out.println(NCdumpW.printVariableData(lonVar, null));
// Get the lat/lon data from the file.
ArrayFloat.D1 latArray;
ArrayFloat.D1 lonArray;
latArray = (ArrayFloat.D1) latVar.read();
lonArray = (ArrayFloat.D1) lonVar.read();
// System.out.println(NCdumpW.printArray(latArray, "lat", null));
// System.out.println(NCdumpW.printArray(lonArray, "lon", null));
// Get the pressure and temperature variables.
Variable presVar = dataFile.findVariable("pressure");
if (presVar == null) {
System.out.println("Cant find Variable pressure");
return;
}
Variable tempVar = dataFile.findVariable("temperature");
if (lonVar == null) {
System.out.println("Cant find Variable temperature");
return;
}
int[] shape = presVar.getShape();
int recLen = shape[0]; // number of times
int[] origin = new int[4];
shape[0] = 1; // only one rec per read
// loop over the rec dimension
for (int rec = 0; rec < recLen; rec++) {
origin[0] = rec; // read this index
// read 3D array for that index
ArrayFloat.D3 presArray, tempArray;
presArray = (ArrayFloat.D3) (presVar.read(origin, shape).reduce());
tempArray = (ArrayFloat.D3) (tempVar.read(origin, shape).reduce());
System.out.println(NCdumpW.printArray(presArray, "presure", null));
System.out.println(NCdumpW.printArray(tempArray, "temperature", null));
// now checking the value
int count = 0;
for (int lvl = 0; lvl < NLVL; lvl++)
for (int lat = 0; lat < NLAT; lat++)
for (int lon = 0; lon < NLON; lon++) {
if ((presArray.get(lvl, lat, lon) != SAMPLE_PRESSURE + count) ||
(tempArray.get(lvl, lat, lon) != SAMPLE_TEMP + count))
System.err.println("ERROR incorrect value in variable pressure or temperature");
count++;
}
}
// The file is closed no matter what by putting inside a try/catch block.
} catch (java.io.IOException e) {
e.printStackTrace();
return;
} catch (InvalidRangeException e) {
e.printStackTrace();
return;
} finally {
if (dataFile != null)
try {
dataFile.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
System.out.println("*** SUCCESS reading example file " + filename);
}
}
執行結果:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
dimension:
level = 2;
latitude = 6;
longitude = 12;
time = UNLIMITED; // (2 currently)
latitude =
{25.0, 30.0, 35.0, 40.0, 45.0, 50.0}
longitude =
{-125.0, -120.0, -115.0, -110.0, -105.0, -100.0, -95.0, -90.0, -85.0, -80.0, -75.0, -70.0}
presure =
{
{
{900.0, 901.0, 902.0, 903.0, 904.0, 905.0, 906.0, 907.0, 908.0, 909.0, 910.0, 911.0},
{912.0, 913.0, 914.0, 915.0, 916.0, 917.0, 918.0, 919.0, 920.0, 921.0, 922.0, 923.0},
{924.0, 925.0, 926.0, 927.0, 928.0, 929.0, 930.0, 931.0, 932.0, 933.0, 934.0, 935.0},
{936.0, 937.0, 938.0, 939.0, 940.0, 941.0, 942.0, 943.0, 944.0, 945.0, 946.0, 947.0},
{948.0, 949.0, 950.0, 951.0, 952.0, 953.0, 954.0, 955.0, 956.0, 957.0, 958.0, 959.0},
{960.0, 961.0, 962.0, 963.0, 964.0, 965.0, 966.0, 967.0, 968.0, 969.0, 970.0, 971.0}
},
{
{972.0, 973.0, 974.0, 975.0, 976.0, 977.0, 978.0, 979.0, 980.0, 981.0, 982.0, 983.0},
{984.0, 985.0, 986.0, 987.0, 988.0, 989.0, 990.0, 991.0, 992.0, 993.0, 994.0, 995.0},
{996.0, 997.0, 998.0, 999.0, 1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0, 1006.0, 1007.0},
{1008.0, 1009.0, 1010.0, 1011.0, 1012.0, 1013.0, 1014.0, 1015.0, 1016.0, 1017.0, 1018.0, 1019.0},
{1020.0, 1021.0, 1022.0, 1023.0, 1024.0, 1025.0, 1026.0, 1027.0, 1028.0, 1029.0, 1030.0, 1031.0},
{1032.0, 1033.0, 1034.0, 1035.0, 1036.0, 1037.0, 1038.0, 1039.0, 1040.0, 1041.0, 1042.0, 1043.0}
}
}
temperature =
{
{
{9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0},
{21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0},
{33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 40.0, 41.0, 42.0, 43.0, 44.0},
{45.0, 46.0, 47.0, 48.0, 49.0, 50.0, 51.0, 52.0, 53.0, 54.0, 55.0, 56.0},
{57.0, 58.0, 59.0, 60.0, 61.0, 62.0, 63.0, 64.0, 65.0, 66.0, 67.0, 68.0},
{69.0, 70.0, 71.0, 72.0, 73.0, 74.0, 75.0, 76.0, 77.0, 78.0, 79.0, 80.0}
},
{
{81.0, 82.0, 83.0, 84.0, 85.0, 86.0, 87.0, 88.0, 89.0, 90.0, 91.0, 92.0},
{93.0, 94.0, 95.0, 96.0, 97.0, 98.0, 99.0, 100.0, 101.0, 102.0, 103.0, 104.0},
{105.0, 106.0, 107.0, 108.0, 109.0, 110.0, 111.0, 112.0, 113.0, 114.0, 115.0, 116.0},
{117.0, 118.0, 119.0, 120.0, 121.0, 122.0, 123.0, 124.0, 125.0, 126.0, 127.0, 128.0},
{129.0, 130.0, 131.0, 132.0, 133.0, 134.0, 135.0, 136.0, 137.0, 138.0, 139.0, 140.0},
{141.0, 142.0, 143.0, 144.0, 145.0, 146.0, 147.0, 148.0, 149.0, 150.0, 151.0, 152.0}
}
}
presure =
{
{
{900.0, 901.0, 902.0, 903.0, 904.0, 905.0, 906.0, 907.0, 908.0, 909.0, 910.0, 911.0},
{912.0, 913.0, 914.0, 915.0, 916.0, 917.0, 918.0, 919.0, 920.0, 921.0, 922.0, 923.0},
{924.0, 925.0, 926.0, 927.0, 928.0, 929.0, 930.0, 931.0, 932.0, 933.0, 934.0, 935.0},
{936.0, 937.0, 938.0, 939.0, 940.0, 941.0, 942.0, 943.0, 944.0, 945.0, 946.0, 947.0},
{948.0, 949.0, 950.0, 951.0, 952.0, 953.0, 954.0, 955.0, 956.0, 957.0, 958.0, 959.0},
{960.0, 961.0, 962.0, 963.0, 964.0, 965.0, 966.0, 967.0, 968.0, 969.0, 970.0, 971.0}
},
{
{972.0, 973.0, 974.0, 975.0, 976.0, 977.0, 978.0, 979.0, 980.0, 981.0, 982.0, 983.0},
{984.0, 985.0, 986.0, 987.0, 988.0, 989.0, 990.0, 991.0, 992.0, 993.0, 994.0, 995.0},
{996.0, 997.0, 998.0, 999.0, 1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0, 1006.0, 1007.0},
{1008.0, 1009.0, 1010.0, 1011.0, 1012.0, 1013.0, 1014.0, 1015.0, 1016.0, 1017.0, 1018.0, 1019.0},
{1020.0, 1021.0, 1022.0, 1023.0, 1024.0, 1025.0, 1026.0, 1027.0, 1028.0, 1029.0, 1030.0, 1031.0},
{1032.0, 1033.0, 1034.0, 1035.0, 1036.0, 1037.0, 1038.0, 1039.0, 1040.0, 1041.0, 1042.0, 1043.0}
}
}
temperature =
{
{
{9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0},
{21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0},
{33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 40.0, 41.0, 42.0, 43.0, 44.0},
{45.0, 46.0, 47.0, 48.0, 49.0, 50.0, 51.0, 52.0, 53.0, 54.0, 55.0, 56.0},
{57.0, 58.0, 59.0, 60.0, 61.0, 62.0, 63.0, 64.0, 65.0, 66.0, 67.0, 68.0},
{69.0, 70.0, 71.0, 72.0, 73.0, 74.0, 75.0, 76.0, 77.0, 78.0, 79.0, 80.0}
},
{
{81.0, 82.0, 83.0, 84.0, 85.0, 86.0, 87.0, 88.0, 89.0, 90.0, 91.0, 92.0},
{93.0, 94.0, 95.0, 96.0, 97.0, 98.0, 99.0, 100.0, 101.0, 102.0, 103.0, 104.0},
{105.0, 106.0, 107.0, 108.0, 109.0, 110.0, 111.0, 112.0, 113.0, 114.0, 115.0, 116.0},
{117.0, 118.0, 119.0, 120.0, 121.0, 122.0, 123.0, 124.0, 125.0, 126.0, 127.0, 128.0},
{129.0, 130.0, 131.0, 132.0, 133.0, 134.0, 135.0, 136.0, 137.0, 138.0, 139.0, 140.0},
{141.0, 142.0, 143.0, 144.0, 145.0, 146.0, 147.0, 148.0, 149.0, 150.0, 151.0, 152.0}
}
}
*** SUCCESS reading example file pres_temp_4D.nc
讀資料時注意幾點: 1.Variable. read的使用
常用的方法為: (1) read(); 讀取所有資料 (2) read(Section section) (非此文章的資料例項) (3)read(int[] origin,int[] shape) (非此文章的資料例項)
(4)把多維矩陣轉換為java的1維陣列 (非此文章的資料例項)