diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5ffe34e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.15.2) + +# 设置工程名称和语言 +project(NETCDF_CXX) + +message(STATUS "Platform: " ${CMAKE_HOST_SYSTEM_NAME}) +set(CMAKE_INSTALL_PREFIX "D:/Library") + +# 添加源文件地址 +add_subdirectory(src/) \ No newline at end of file diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..d163863 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..5eb7632 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,61 @@ +# 设定源文件文件夹 +aux_source_directory(lib/ NETCDF_SRC) +# 设置netcdf c接口的库文件地址 +set(LIB_NETCDF_INC C:/Program\ Files/netCDF\ 4.8.0/include) +set(LIB_NETCDF_LIB C:/Program\ Files/netCDF\ 4.8.0/lib) +# netcdf安装后的动态库与静态库的地址不一样 需要手动在cmake生成的VS工程内添加下面的库地址 +# 方法如下:gctl->属性->链接器->附件库目录(选择编辑,并选择netcdf的可执行文件地址) +#set(LIB_NETCDF_BIN C:/Program\ Files/netCDF\ 4.8.0/bin) +include_directories(${LIB_NETCDF_INC}) + +# 设置编译选项 +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2") +# 设置库文件的输出地址 +set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) + +# 以下部分为库的编译 +# 注意目标名必须唯一 所以不能直接生成相同名称的动态库与静态库 +# 注意此处不必为目标名称添加lib前缀和相应后缀,cmake会自行添加 +add_library(netcdf_c++ SHARED ${NETCDF_SRC}) +# 首先添加静态库的生成命令 +add_library(netcdf_c++_static STATIC ${NETCDF_SRC}) +# 设置静态库的输出名称从而获得与动态库名称相同的静态库 +set_target_properties(netcdf_c++_static PROPERTIES OUTPUT_NAME "netcdf_c++") +# 设置输出目标属性以同时输出动态库与静态库 +set_target_properties(netcdf_c++ PROPERTIES CLEAN_DIRECT_OUTPUT 1) +set_target_properties(netcdf_c++_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) +# 设置动态库的版本号 +set_target_properties(netcdf_c++ PROPERTIES VERSION 1.0 SOVERSION 1.0) + +find_library(NETCDF_LIBRARY netcdf ${LIB_NETCDF_LIB}) +target_link_libraries(netcdf_c++ PUBLIC ${NETCDF_LIBRARY}) +target_link_libraries(netcdf_c++_static ${NETCDF_LIBRARY}) + +# 库的安装命令 +install(TARGETS netcdf_c++ DESTINATION lib) +install(TARGETS netcdf_c++_static DESTINATION lib) +# 头文件安装命令 +install(FILES lib/netcdfcpp.h DESTINATION include) +install(FILES lib/ncvalues.h DESTINATION include) + +# 以下部分为例子程序的编译 +# 设置可执行文件的输出地址 +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) + +# 例子的编译方法 +macro(add_sample name) + # 添加可执行文件 命令行 + add_executable(${name} example/${name}.cpp) + # 为安装文件添加动态库的搜索地址 在Windows下并没有什么用 直接忽略 + set_target_properties(${name} PROPERTIES INSTALL_RPATH D:/Library/lib) + # 链接动态库 + target_link_libraries(${name} PUBLIC netcdf_c++) +endmacro() + +add_sample(pres_temp_4D_rd) +add_sample(pres_temp_4D_wr) +add_sample(sfc_pres_temp_rd) +add_sample(sfc_pres_temp_wr) +add_sample(simple_xy_rd) +add_sample(simple_xy_wr) diff --git a/src/example/pres_temp_4D_rd.cpp b/src/example/pres_temp_4D_rd.cpp new file mode 100644 index 0000000..b4f327c --- /dev/null +++ b/src/example/pres_temp_4D_rd.cpp @@ -0,0 +1,126 @@ +/* This is part of the netCDF package. + Copyright 2006 University Corporation for Atmospheric Research/Unidata. + See COPYRIGHT file for conditions of use. + + This is an example which reads some 4D pressure and temperature + values. The data file read by this program is produced by the + companion program pres_temp_4D_wr.cpp. It is intended to illustrate + the use of the netCDF C++ API. + + This program is part of the netCDF tutorial: + http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-tutorial + + Full documentation of the netCDF C++ API can be found at: + http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-cxx + + $Id: pres_temp_4D_rd.cpp,v 1.13 2007/02/14 20:59:21 ed Exp $ +*/ + +#include +#include + +using namespace std; + +// We are writing 4D data, a 2 x 6 x 12 lvl-lat-lon grid, with 2 +// timesteps of data. +static const int NLVL = 2; +static const int NLAT = 6; +static const int NLON = 12; +static const int NREC = 2; + +// These are used to construct some example data. +static const float SAMPLE_PRESSURE = 900.0; +static const float SAMPLE_TEMP = 9.0; +static const float START_LAT = 25.0; +static const float START_LON = -125.0; + +// Return this code to the OS in case of failure. +static const int NC_ERR = 2; + +int main() +{ + // These arrays will store the latitude and longitude values. + float lats[NLAT], lons[NLON]; + + // These arrays will hold the data we will read in. We will only + // need enough space to hold one timestep of data; one record. + float pres_in[NLVL][NLAT][NLON]; + float temp_in[NLVL][NLAT][NLON]; + + // Change the error behavior of the netCDF C++ API by creating an + // NcError object. Until it is destroyed, this NcError object will + // ensure that the netCDF C++ API returns error codes on any + // failure, prints an error message, and leaves any other error + // handling to the calling program. In the case of this example, we + // just exit with an NC_ERR error code. + NcError err(NcError::verbose_nonfatal); + + // Open the file. + NcFile dataFile("pres_temp_4D.nc", NcFile::ReadOnly); + + // Check to see if the file was opened. + if(!dataFile.is_valid()) + return NC_ERR; + + // Get pointers to the latitude and longitude variables. + NcVar *latVar, *lonVar; + if (!(latVar = dataFile.get_var("latitude"))) + return NC_ERR; + if (!(lonVar = dataFile.get_var("longitude"))) + return NC_ERR; + + // Get the lat/lon data from the file. + if (!latVar->get(lats, NLAT)) + return NC_ERR; + if (!lonVar->get(lons, NLON)) + return NC_ERR; + + // Check the coordinate variable data. + for (int lat = 0; lat < NLAT; lat++) + if (lats[lat] != START_LAT + 5. * lat) + return NC_ERR; + for (int lon = 0; lon < NLON; lon++) + if (lons[lon] != START_LON + 5. * lon) + return NC_ERR; + + // Get pointers to the pressure and temperature variables. + NcVar *presVar, *tempVar; + if (!(presVar = dataFile.get_var("pressure"))) + return NC_ERR; + if (!(tempVar = dataFile.get_var("temperature"))) + return NC_ERR; + + // Read the data. Since we know the contents of the file we know + // that the data arrays in this program are the correct size to + // hold one timestep. + for (int rec = 0; rec < NREC; rec++) + { + // Read the data one record at a time. + if (!presVar->set_cur(rec, 0, 0, 0)) + return NC_ERR; + if (!tempVar->set_cur(rec, 0, 0, 0)) + return NC_ERR; + + // Get 1 record of NLVL by NLAT by NLON values for each variable. + if (!presVar->get(&pres_in[0][0][0], 1, NLVL, NLAT, NLON)) + return NC_ERR; + if (!tempVar->get(&temp_in[0][0][0], 1, NLVL, NLAT, NLON)) + return NC_ERR; + + // Check the data. + int i = 0; + for (int lvl = 0; lvl < NLVL; lvl++) + for (int lat = 0; lat < NLAT; lat++) + for (int lon = 0; lon < NLON; lon++) + if (pres_in[lvl][lat][lon] != SAMPLE_PRESSURE + i || + temp_in[lvl][lat][lon] != SAMPLE_TEMP + i++) + return NC_ERR; + } // next record + + // The file is automatically closed by the destructor. This frees + // up any internal netCDF resources associated with the file, and + // flushes any buffers. + + cout << "*** SUCCESS reading example file pres_temp_4D.nc!" << endl; + return 0; +} diff --git a/src/example/pres_temp_4D_wr.cpp b/src/example/pres_temp_4D_wr.cpp new file mode 100644 index 0000000..ce2e366 --- /dev/null +++ b/src/example/pres_temp_4D_wr.cpp @@ -0,0 +1,149 @@ +/* This is part of the netCDF package. + Copyright 2006 University Corporation for Atmospheric Research/Unidata. + See COPYRIGHT file for conditions of use. + + This is an example program which writes some 4D pressure and + temperatures. This example demonstrates the netCDF C++ API. + + This is part of the netCDF tutorial: + http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-tutorial + + Full documentation of the netCDF C++ API can be found at: + http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-cxx + + $Id: pres_temp_4D_wr.cpp,v 1.11 2007/01/19 12:52:13 ed Exp $ +*/ + +#include +#include + +using namespace std; + +// We are writing 4D data, a 2 x 6 x 12 lvl-lat-lon grid, with 2 +// timesteps of data. +static const int NLVL = 2; +static const int NLAT = 6; +static const int NLON = 12; +static const int NREC = 2; + +// These are used to construct some example data. +static const float SAMPLE_PRESSURE = 900.0; +static const float SAMPLE_TEMP = 9.0; +static const float START_LAT = 25.0; +static const float START_LON = -125.0; + +// Return this code to the OS in case of failure. +static const int NC_ERR = 2; + +int main() +{ + // These arrays will store the latitude and longitude values. + float lats[NLAT],lons[NLON]; + + // These arrays will hold the data we will write out. We will + // only need enough space to hold one timestep of data; one record. + float pres_out[NLVL][NLAT][NLON]; + float temp_out[NLVL][NLAT][NLON]; + + int i = 0; + + // Create some pretend data. If this wasn't an example program, we + // would have some real data to write for example, model output. + for (int lat = 0; lat < NLAT; lat++) + lats[lat] = START_LAT + 5. * lat; + for (int lon = 0; lon < NLON; lon++) + lons[lon] = START_LON + 5. * lon; + + for (int lvl = 0; lvl < NLVL; lvl++) + for (int lat = 0; lat < NLAT; lat++) + for (int lon = 0; lon < NLON; lon++) + { + pres_out[lvl][lat][lon] = SAMPLE_PRESSURE + i; + temp_out[lvl][lat][lon] = SAMPLE_TEMP + i++; + } + + // Change the error behavior of the netCDF C++ API by creating an + // NcError object. Until it is destroyed, this NcError object will + // ensure that the netCDF C++ API returns error codes on any + // failure, prints an error message, and leaves any other error + // handling to the calling program. In the case of this example, we + // just exit with an NC_ERR error code. + NcError err(NcError::verbose_nonfatal); + + // Create the file. + NcFile dataFile("pres_temp_4D.nc", NcFile::Replace); + + // Check to see if the file was created. + if(!dataFile.is_valid()) + return NC_ERR; + + // Define the dimensions. NetCDF will hand back an ncDim object for + // each. + NcDim *lvlDim, *latDim, *lonDim, *recDim; + if (!(lvlDim = dataFile.add_dim("level", NLVL))) + return NC_ERR; + if (!(latDim = dataFile.add_dim("latitude", NLAT))) + return NC_ERR; + if (!(lonDim = dataFile.add_dim("longitude", NLON))) + return NC_ERR; + // Add an unlimited dimension... + if (!(recDim = dataFile.add_dim("time"))) + return NC_ERR; + + // Define the coordinate variables. + NcVar *latVar, *lonVar; + if (!(latVar = dataFile.add_var("latitude", ncFloat, latDim))) + return NC_ERR; + if (!(lonVar = dataFile.add_var("longitude", ncFloat, lonDim))) + return NC_ERR; + + // Define units attributes for coordinate vars. This attaches a + // text attribute to each of the coordinate variables, containing + // the units. + if (!latVar->add_att("units", "degrees_north")) + return NC_ERR; + if (!lonVar->add_att("units", "degrees_east")) + return NC_ERR; + + // Define the netCDF variables for the pressure and temperature + // data. + NcVar *presVar, *tempVar; + if (!(presVar = dataFile.add_var("pressure", ncFloat, recDim, + lvlDim, latDim, lonDim))) + return NC_ERR; + if (!(tempVar = dataFile.add_var("temperature", ncFloat, recDim, + lvlDim, latDim, lonDim))) + return NC_ERR; + + // Define units attributes for data variables. + if (!presVar->add_att("units", "hPa")) + return NC_ERR; + if (!tempVar->add_att("units", "celsius")) + return NC_ERR; + + // Write the coordinate variable data to the file. + if (!latVar->put(lats, NLAT)) + return NC_ERR; + if (!lonVar->put(lons, NLON)) + return NC_ERR; + + // Write the pretend data. This will write our surface pressure and + // surface temperature data. The arrays only hold one timestep + // worth of data. We will just rewrite the same data for each + // timestep. In a real application, the data would change between + // timesteps. + for (int rec = 0; rec < NREC; rec++) + { + if (!presVar->put_rec(&pres_out[0][0][0], rec)) + return NC_ERR; + if (!tempVar->put_rec(&temp_out[0][0][0], rec)) + return NC_ERR; + } + + // The file is automatically closed by the destructor. This frees + // up any internal netCDF resources associated with the file, and + // flushes any buffers. + + cout << "*** SUCCESS writing example file pres_temp_4D.nc!" << endl; + return 0; +} diff --git a/src/example/sfc_pres_temp_rd.cpp b/src/example/sfc_pres_temp_rd.cpp new file mode 100644 index 0000000..9439b2e --- /dev/null +++ b/src/example/sfc_pres_temp_rd.cpp @@ -0,0 +1,161 @@ +/* This is part of the netCDF package. + Copyright 2006 University Corporation for Atmospheric Research/Unidata. + See COPYRIGHT file for conditions of use. + + This is an example which reads some surface pressure and + temperatures. The data file read by this program is produced + companion program sfc_pres_temp_wr.cxx. It is intended to + illustrate the use of the netCDF C++ API. + + This program is part of the netCDF tutorial: + http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-tutorial + + Full documentation of the netCDF C++ API can be found at: + http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-cxx + + $Id: sfc_pres_temp_rd.cpp,v 1.17 2008/05/16 13:42:28 ed Exp $ +*/ + +#include +#include +#include + +using namespace std; + +// We are reading 2D data, a 6 x 12 lat-lon grid. +static const int NLAT = 6; +static const int NLON = 12; + +// These are used to calculate the values we expect to find. +static const float SAMPLE_PRESSURE = 900; +static const float SAMPLE_TEMP = 9.0; +static const float START_LAT = 25.0; +static const float START_LON = -125.0; + +// Return this code to the OS in case of failure. +static const int NC_ERR = 2; + +int main(void) +{ + // These will hold our pressure and temperature data. + float presIn[NLAT][NLON]; + float tempIn[NLAT][NLON]; + + // These will hold our latitudes and longitudes. + float latsIn[NLAT]; + float lonsIn[NLON]; + + // Change the error behavior of the netCDF C++ API by creating an + // NcError object. Until it is destroyed, this NcError object will + // ensure that the netCDF C++ API silently returns error codes on + // any failure, and leaves any other error handling to the calling + // program. In the case of this example, we just exit with an + // NC_ERR error code. + NcError err(NcError::silent_nonfatal); + + // Open the file and check to make sure it's valid. + NcFile dataFile("sfc_pres_temp.nc", NcFile::ReadOnly); + if(!dataFile.is_valid()) + return NC_ERR; + + // There are a number of inquiry functions in netCDF which can be + // used to learn about an unknown netCDF file. In this case we know + // that there are 2 netCDF dimensions, 4 netCDF variables, no + // global attributes, and no unlimited dimension. + if (dataFile.num_dims() != 2 || dataFile.num_vars() != 4 || + dataFile.num_atts() != 0 || dataFile.rec_dim() != 0) + return NC_ERR; + + // We get back a pointer to each NcVar we request. Get the + // latitude and longitude coordinate variables. + NcVar *latVar, *lonVar; + if (!(latVar = dataFile.get_var("latitude"))) + return NC_ERR; + if (!(lonVar = dataFile.get_var("longitude"))) + return NC_ERR; + + // Read the latitude and longitude coordinate variables into arrays + // latsIn and lonsIn. + if (!latVar->get(latsIn, NLAT)) + return NC_ERR; + if (!lonVar->get(lonsIn, NLON)) + return NC_ERR; + + // Check the coordinate variable data. + for(int lat = 0; lat < NLAT; lat++) + if (latsIn[lat] != START_LAT + 5. * lat) + return NC_ERR; + + // Check longitude values. + for (int lon = 0; lon < NLON; lon++) + if (lonsIn[lon] != START_LON + 5. * lon) + return NC_ERR; + + // We get back a pointer to each NcVar we request. + NcVar *presVar, *tempVar; + if (!(presVar = dataFile.get_var("pressure"))) + return NC_ERR; + if (!(tempVar = dataFile.get_var("temperature"))) + return NC_ERR; + + // Read the data. Since we know the contents of the file we know + // that the data arrays in this program are the correct size to + // hold all the data. + if (!presVar->get(&presIn[0][0], NLAT, NLON)) + return NC_ERR; + if (!tempVar->get(&tempIn[0][0], NLAT, NLON)) + return NC_ERR; + + // Check the data. + for (int lat = 0; lat < NLAT; lat++) + for (int lon = 0; lon < NLON; lon++) + if (presIn[lat][lon] != SAMPLE_PRESSURE + (lon * NLAT + lat) + || tempIn[lat][lon] != SAMPLE_TEMP + .25 * (lon * NLAT + lat)) + return NC_ERR; + + // Each of the netCDF variables has a "units" attribute. Let's read + // them and check them. + NcAtt *att; + char *units; + + if (!(att = latVar->get_att("units"))) + return NC_ERR; + units = att->as_string(0); + if (strncmp(units, "degrees_north", strlen("degrees_north"))) + return NC_ERR; + // Attributes and attribute values should be deleted by the caller + // when no longer needed, to prevent memory leaks. + delete units; + delete att; + + if (!(att = lonVar->get_att("units"))) + return NC_ERR; + units = att->as_string(0); + if (strncmp(units, "degrees_east", strlen("degrees_east"))) + return NC_ERR; + delete units; + delete att; + + if (!(att = presVar->get_att("units"))) + return NC_ERR; + units = att->as_string(0); + if (strncmp(units, "hPa", strlen("hPa"))) + return NC_ERR; + delete units; + delete att; + + if (!(att = tempVar->get_att("units"))) + return NC_ERR; + units = att->as_string(0); + if (strncmp(units, "celsius", strlen("celsius"))) + return NC_ERR; + delete units; + delete att; + + // The file will be automatically closed by the destructor. This + // frees up any internal netCDF resources associated with the file, + // and flushes any buffers. + cout << "*** SUCCESS reading example file sfc_pres_temp.nc!" << endl; + + return 0; +} diff --git a/src/example/sfc_pres_temp_wr.cpp b/src/example/sfc_pres_temp_wr.cpp new file mode 100644 index 0000000..a931915 --- /dev/null +++ b/src/example/sfc_pres_temp_wr.cpp @@ -0,0 +1,142 @@ +/* This is part of the netCDF package. + Copyright 2006 University Corporation for Atmospheric Research/Unidata. + See COPYRIGHT file for conditions of use. + + This example writes some surface pressure and temperatures. It is + intended to illustrate the use of the netCDF C++ API. The companion + program sfc_pres_temp_rd.cpp shows how to read the netCDF data file + created by this program. + + This program is part of the netCDF tutorial: + http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-tutorial + + Full documentation of the netCDF C++ API can be found at: + http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-cxx + + $Id: sfc_pres_temp_wr.cpp,v 1.12 2007/01/19 12:52:13 ed Exp $ +*/ + +#include +#include + +using namespace std; + +// We are writing 2D data, a 6 x 12 lat-lon grid. We will need two +// netCDF dimensions. +static const int NLAT = 6; +static const int NLON = 12; + +// These are used to construct some example data. +static const float SAMPLE_PRESSURE = 900; +static const float SAMPLE_TEMP = 9.0; +static const float START_LAT = 25.0; +static const float START_LON = -125.0; + +// Return this to OS if there is a failure. +static const int NC_ERR = 2; + +int main(void) +{ + // These will hold our pressure and temperature data. + float presOut[NLAT][NLON]; + float tempOut[NLAT][NLON]; + + // These will hold our latitudes and longitudes. + float lats[NLAT]; + float lons[NLON]; + + // Create some pretend data. If this wasn't an example program, we + // would have some real data to write, for example, model + // output. + for(int lat = 0; lat < NLAT; lat++) + lats[lat] = START_LAT + 5. * lat; + + for(int lon = 0; lon < NLON; lon++) + lons[lon] = START_LON + 5. * lon; + + for (int lat = 0; lat < NLAT; lat++) + for(int lon = 0; lon < NLON; lon++) + { + presOut[lat][lon] = SAMPLE_PRESSURE + (lon * NLAT + lat); + tempOut[lat][lon] = SAMPLE_TEMP + .25 * (lon * NLAT + lat); + } + + // Change the error behavior of the netCDF C++ API by creating an + // NcError object. Until it is destroyed, this NcError object will + // ensure that the netCDF C++ API silently returns error codes + // on any failure, and leaves any other error handling to the + // calling program. In the case of this example, we just exit with + // an NC_ERR error code. + NcError err(NcError::silent_nonfatal); + + // Create the file. The Replace parameter tells netCDF to overwrite + // this file, if it already exists. + NcFile dataFile("sfc_pres_temp.nc", NcFile::Replace); + + // Check to see if the file was created. + if(!dataFile.is_valid()) + return NC_ERR; + + // Define the dimensions. NetCDF will hand back an ncDim object for + // each. + NcDim *latDim, *lonDim; + if (!(latDim = dataFile.add_dim("latitude", NLAT))) + return NC_ERR; + if (!(lonDim = dataFile.add_dim("longitude", NLON))) + return NC_ERR; + + // In addition to the latitude and longitude dimensions, we will + // also create latitude and longitude netCDF variables which will + // hold the actual latitudes and longitudes. Since they hold data + // about the coordinate system, the netCDF term for these is: + // "coordinate variables." + NcVar *latVar, *lonVar; + if (!(latVar = dataFile.add_var("latitude", ncFloat, latDim))) + return NC_ERR; + if (!(lonVar = dataFile.add_var("longitude", ncFloat, lonDim))) + return NC_ERR; + + // Define units attributes for coordinate vars. This attaches a + // text attribute to each of the coordinate variables, containing + // the units. + if (!lonVar->add_att("units", "degrees_east")) + return NC_ERR; + if (!latVar->add_att("units", "degrees_north")) + return NC_ERR; + + // Define the netCDF data variables. + NcVar *presVar, *tempVar; + if (!(presVar = dataFile.add_var("pressure", ncFloat, latDim, lonDim))) + return NC_ERR; + if (!(tempVar = dataFile.add_var("temperature", ncFloat, latDim, lonDim))) + return NC_ERR; + + // Define units attributes for variables. + if (!presVar->add_att("units", "hPa")) + return NC_ERR; + if (!tempVar->add_att("units", "celsius")) + return NC_ERR; + + // Write the coordinate variable data. This will put the latitudes + // and longitudes of our data grid into the netCDF file. + if (!latVar->put(lats, NLAT)) + return NC_ERR; + if (!lonVar->put(lons, NLON)) + return NC_ERR; + + // Write the pretend data. This will write our surface pressure and + // surface temperature data. The arrays of data are the same size + // as the netCDF variables we have defined, and below we write them + // each in one step. + if (!presVar->put(&presOut[0][0], NLAT, NLON)) + return NC_ERR; + if (!tempVar->put(&tempOut[0][0], NLAT, NLON)) + return NC_ERR; + + // The file is automatically closed by the destructor. This frees + // up any internal netCDF resources associated with the file, and + // flushes any buffers. + cout << "*** SUCCESS writing example file sfc_pres_temp.nc!" << endl; + + return 0; +} diff --git a/src/example/simple_xy_rd.cpp b/src/example/simple_xy_rd.cpp new file mode 100644 index 0000000..c1c0409 --- /dev/null +++ b/src/example/simple_xy_rd.cpp @@ -0,0 +1,67 @@ +/* This is part of the netCDF package. + Copyright 2006 University Corporation for Atmospheric Research/Unidata. + See COPYRIGHT file for conditions of use. + + This is a very simple example which reads a 2D array of + sample data produced by simple_xy_wr.cpp. + + This example is part of the netCDF tutorial: + http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-tutorial + + Full documentation of the netCDF C++ API can be found at: + http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-cxx + + $Id: simple_xy_rd.cpp,v 1.13 2007/01/19 12:52:13 ed Exp $ +*/ + +#include +#include + +using namespace std; + +// We are reading 2D data, a 6 x 12 grid. +static const int NX = 6; +static const int NY = 12; + +// Return this in event of a problem. +static const int NC_ERR = 2; + +int main(void) +{ + // This is the array we will read. + int dataIn[NX][NY]; + + // Open the file. The ReadOnly parameter tells netCDF we want + // read-only access to the file. + NcFile dataFile("simple_xy.nc", NcFile::ReadOnly); + + // You should always check whether a netCDF file open or creation + // constructor succeeded. + if (!dataFile.is_valid()) + { + cout << "Couldn't open file!\n"; + return NC_ERR; + } + + // For other method calls, the default behavior of the C++ API is + // to exit with a message if there is an error. If that behavior + // is OK, there is no need to check return values in simple cases + // like the following. + + // Retrieve the variable named "data" + NcVar *data = dataFile.get_var("data"); + + // Read all the values from the "data" variable into memory. + data->get(&dataIn[0][0], NX, NY); + + // Check the values. + for (int i = 0; i < NX; i++) + for (int j = 0; j < NY; j++) + if (dataIn[i][j] != i * NY + j) + return NC_ERR; + + // The netCDF file is automatically closed by the NcFile destructor + cout << "*** SUCCESS reading example file simple_xy.nc!" << endl; + + return 0; +} diff --git a/src/example/simple_xy_wr.cpp b/src/example/simple_xy_wr.cpp new file mode 100644 index 0000000..e959015 --- /dev/null +++ b/src/example/simple_xy_wr.cpp @@ -0,0 +1,80 @@ +/* This is part of the netCDF package. + Copyright 2006 University Corporation for Atmospheric Research/Unidata. + See COPYRIGHT file for conditions of use. + + This is a very simple example which writes a 2D array of + sample data. To handle this in netCDF we create two shared + dimensions, "x" and "y", and a netCDF variable, called "data". + + This example is part of the netCDF tutorial: + http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-tutorial + + Full documentation of the netCDF C++ API can be found at: + http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-cxx + + $Id: simple_xy_wr.cpp,v 1.15 2007/01/19 12:52:13 ed Exp $ +*/ + +#include +#include + +using namespace std; + +// We are writing 2D data, a 6 x 12 grid. +static const int NX = 6; +static const int NY = 12; + +// Return this in event of a problem. +static const int NC_ERR = 2; + +int +main(void) +{ + // This is the data array we will write. It will just be filled + // with a progression of numbers for this example. + int dataOut[NX][NY]; + + // Create some pretend data. If this wasn't an example program, we + // would have some real data to write, for example, model output. + for(int i = 0; i < NX; i++) + for(int j = 0; j < NY; j++) + dataOut[i][j] = i * NY + j; + + // Create the file. The Replace parameter tells netCDF to overwrite + // this file, if it already exists. + NcFile dataFile("simple_xy.nc", NcFile::Replace); + + // You should always check whether a netCDF file creation or open + // constructor succeeded. + if (!dataFile.is_valid()) + { + cout << "Couldn't open file!\n"; + return NC_ERR; + } + + // For other method calls, the default behavior of the C++ API is + // to exit with a message if there is an error. If that behavior + // is OK, there is no need to check return values in simple cases + // like the following. + + // When we create netCDF dimensions, we get back a pointer to an + // NcDim for each one. + NcDim* xDim = dataFile.add_dim("x", NX); + NcDim* yDim = dataFile.add_dim("y", NY); + + // Define a netCDF variable. The type of the variable in this case + // is ncInt (32-bit integer). + NcVar *data = dataFile.add_var("data", ncInt, xDim, yDim); + + // Write the pretend data to the file. Although netCDF supports + // reading and writing subsets of data, in this case we write all + // the data in one operation. + data->put(&dataOut[0][0], NX, NY); + + // The file will be automatically close when the NcFile object goes + // out of scope. This frees up any internal netCDF resources + // associated with the file, and flushes any buffers. + cout << "*** SUCCESS writing example file simple_xy.nc!" << endl; + + return 0; +} diff --git a/src/lib/ncvalues.cpp b/src/lib/ncvalues.cpp new file mode 100644 index 0000000..19948cc --- /dev/null +++ b/src/lib/ncvalues.cpp @@ -0,0 +1,332 @@ +/********************************************************************* + * Copyright 1992, University Corporation for Atmospheric Research + * See netcdf/README file for copying and redistribution conditions. + * + * Purpose: implementation of classes of typed arrays for netCDF + * + * $Header: /upc/share/CVS/netcdf-3/cxx/ncvalues.cpp,v 1.12 2008/03/05 16:45:32 russ Exp $ + *********************************************************************/ + +#include +#include +#include + +#include "netcdf_config.h" +#include "ncvalues.h" +#pragma warning(disable : 4996) + +NcValues::NcValues( void ) : the_type(ncNoType), the_number(0) +{} + +NcValues::NcValues(NcType type, long num) + : the_type(type), the_number(num) +{} + +NcValues::~NcValues( void ) +{} + +long NcValues::num( void ) +{ + return the_number; +} + +std::ostream& operator<< (std::ostream& os, const NcValues& vals) +{ + return vals.print(os); +} + +implement(NcValues,ncbyte) +implement(NcValues,char) +implement(NcValues,short) +implement(NcValues,int) +implement(NcValues,nclong) +implement(NcValues,long) +implement(NcValues,float) +implement(NcValues,double) + +Ncbytes_for_one_implement(ncbyte) +Ncbytes_for_one_implement(char) +Ncbytes_for_one_implement(short) +Ncbytes_for_one_implement(int) +Ncbytes_for_one_implement(nclong) +Ncbytes_for_one_implement(long) +Ncbytes_for_one_implement(float) +Ncbytes_for_one_implement(double) + +as_ncbyte_implement(short) +as_ncbyte_implement(int) +as_ncbyte_implement(nclong) +as_ncbyte_implement(long) +as_ncbyte_implement(float) +as_ncbyte_implement(double) + +inline ncbyte NcValues_char::as_ncbyte( long n ) const +{ + return the_values[n]; +} + +inline ncbyte NcValues_ncbyte::as_ncbyte( long n ) const +{ + return the_values[n]; +} + +as_char_implement(short) +as_char_implement(int) +as_char_implement(nclong) +as_char_implement(long) +as_char_implement(float) +as_char_implement(double) + +inline char NcValues_ncbyte::as_char( long n ) const +{ + return the_values[n] > CHAR_MAX ? ncBad_char : (char) the_values[n]; +} + +inline char NcValues_char::as_char( long n ) const +{ + return the_values[n]; +} + +as_short_implement(int) +as_short_implement(nclong) +as_short_implement(long) +as_short_implement(float) +as_short_implement(double) + +inline short NcValues_ncbyte::as_short( long n ) const +{ + return the_values[n]; +} + +inline short NcValues_char::as_short( long n ) const +{ + return the_values[n]; +} + +inline short NcValues_short::as_short( long n ) const +{ + return the_values[n]; +} + + +as_int_implement(float) +as_int_implement(double) + +inline int NcValues_ncbyte::as_int( long n ) const +{ + return the_values[n]; +} + +inline int NcValues_char::as_int( long n ) const +{ + return the_values[n]; +} + +inline int NcValues_short::as_int( long n ) const +{ + return the_values[n]; +} + +inline int NcValues_int::as_int( long n ) const +{ + return the_values[n]; +} + +inline int NcValues_nclong::as_int( long n ) const +{ + return the_values[n]; +} + +inline int NcValues_long::as_int( long n ) const +{ + return the_values[n]; +} + +as_nclong_implement(float) +as_nclong_implement(double) + +inline nclong NcValues_ncbyte::as_nclong( long n ) const +{ + return the_values[n]; +} + +inline nclong NcValues_char::as_nclong( long n ) const +{ + return the_values[n]; +} + +inline nclong NcValues_short::as_nclong( long n ) const +{ + return the_values[n]; +} + +inline nclong NcValues_int::as_nclong( long n ) const +{ + return the_values[n]; +} + +inline nclong NcValues_nclong::as_nclong( long n ) const +{ + return the_values[n]; +} + +inline nclong NcValues_long::as_nclong( long n ) const +{ + return the_values[n]; +} + +as_long_implement(float) +as_long_implement(double) + +inline long NcValues_ncbyte::as_long( long n ) const +{ + return the_values[n]; +} + +inline long NcValues_char::as_long( long n ) const +{ + return the_values[n]; +} + +inline long NcValues_short::as_long( long n ) const +{ + return the_values[n]; +} + +inline long NcValues_int::as_long( long n ) const +{ + return the_values[n]; +} + +inline long NcValues_nclong::as_long( long n ) const +{ + return the_values[n]; +} + +inline long NcValues_long::as_long( long n ) const +{ + return the_values[n]; +} + +as_float_implement(ncbyte) +as_float_implement(char) +as_float_implement(short) +as_float_implement(int) +as_float_implement(nclong) +as_float_implement(long) +as_float_implement(float) +as_float_implement(double) + +as_double_implement(ncbyte) +as_double_implement(char) +as_double_implement(short) +as_double_implement(int) +as_double_implement(nclong) +as_double_implement(long) +as_double_implement(float) +as_double_implement(double) + +as_string_implement(short) +as_string_implement(int) +as_string_implement(nclong) +as_string_implement(long) +as_string_implement(float) +as_string_implement(double) + +inline char* NcValues_ncbyte::as_string( long n ) const +{ + char* s = new char[the_number + 1]; + s[the_number] = '\0'; + strncpy(s, (const char*)the_values + n, (int)the_number); + return s; +} + +inline char* NcValues_char::as_string( long n ) const +{ + char* s = new char[the_number + 1]; + s[the_number] = '\0'; + strncpy(s, (const char*)the_values + n, (int)the_number); + return s; +} + +std::ostream& NcValues_short::print(std::ostream& os) const +{ + for(int i = 0; i < the_number - 1; i++) + os << the_values[i] << ", "; + if (the_number > 0) + os << the_values[the_number-1] ; + return os; +} + +std::ostream& NcValues_int::print(std::ostream& os) const +{ + for(int i = 0; i < the_number - 1; i++) + os << the_values[i] << ", "; + if (the_number > 0) + os << the_values[the_number-1] ; + return os; +} + +std::ostream& NcValues_nclong::print(std::ostream& os) const +{ + for(int i = 0; i < the_number - 1; i++) + os << the_values[i] << ", "; + if (the_number > 0) + os << the_values[the_number-1] ; + return os; +} + +std::ostream& NcValues_long::print(std::ostream& os) const +{ + for(int i = 0; i < the_number - 1; i++) + os << the_values[i] << ", "; + if (the_number > 0) + os << the_values[the_number-1] ; + return os; +} + +std::ostream& NcValues_ncbyte::print(std::ostream& os) const +{ + for(int i = 0; i < the_number - 1; i++) + os << the_values[i] << ", "; + if (the_number > 0) + os << the_values[the_number-1] ; + return os; +} + +std::ostream& NcValues_char::print(std::ostream& os) const +{ + os << '"'; + long len = the_number; + while (the_values[--len] == '\0') // don't output trailing null bytes + ; + for(int i = 0; i <= len; i++) + os << the_values[i] ; + os << '"'; + + return os; +} + +std::ostream& NcValues_float::print(std::ostream& os) const +{ + std::streamsize save=os.precision(); + os.precision(7); + for(int i = 0; i < the_number - 1; i++) + os << the_values[i] << ", "; + if (the_number > 0) + os << the_values[the_number-1] ; + os.precision(save); + return os; +} + +std::ostream& NcValues_double::print(std::ostream& os) const +{ + std::streamsize save=os.precision(); + os.precision(15); + for(int i = 0; i < the_number - 1; i++) + os << the_values[i] << ", "; + if (the_number > 0) + os << the_values[the_number-1]; + os.precision(save); + return os; +} diff --git a/src/lib/ncvalues.h b/src/lib/ncvalues.h new file mode 100644 index 0000000..e7655e4 --- /dev/null +++ b/src/lib/ncvalues.h @@ -0,0 +1,279 @@ +/********************************************************************* + * Copyright 1992, University Corporation for Atmospheric Research + * See netcdf/README file for copying and redistribution conditions. + * + * Purpose: interface for classes of typed arrays for netCDF + * + * $Header: /upc/share/CVS/netcdf-3/cxx/ncvalues.h,v 1.7 2006/07/26 21:12:06 russ Exp $ + *********************************************************************/ + +#ifndef Ncvalues_def +#define Ncvalues_def + +#include +#include +#include +#include "netcdf.h" + +// Documentation warned this might change and now it has, for +// consistency with C interface +typedef signed char ncbyte; + +#define NC_UNSPECIFIED ((nc_type)0) + +// C++ interface dates from before netcdf-3, still uses some netcdf-2 names +#ifdef NO_NETCDF_2 +#define NC_LONG NC_INT +#define FILL_LONG NC_FILL_INT +typedef int nclong; +#define NC_FATAL 1 +#define NC_VERBOSE 2 +#endif + +enum NcType +{ + ncNoType = NC_UNSPECIFIED, + ncByte = NC_BYTE, + ncChar = NC_CHAR, + ncShort = NC_SHORT, + ncInt = NC_INT, + ncLong = NC_LONG, // deprecated, someday want to use for 64-bit ints + ncFloat = NC_FLOAT, + ncDouble = NC_DOUBLE +}; + +#define ncBad_ncbyte ncBad_byte +static const ncbyte ncBad_byte = NC_FILL_BYTE; +static const char ncBad_char = NC_FILL_CHAR; +static const short ncBad_short = NC_FILL_SHORT; +static const nclong ncBad_nclong = FILL_LONG; // deprecated +static const int ncBad_int = NC_FILL_INT; +static const long ncBad_long = FILL_LONG; // deprecated +static const float ncBad_float = NC_FILL_FLOAT; +static const double ncBad_double = NC_FILL_DOUBLE; + +// macros to glue tokens together to form new names (used to be in generic.h) +#define name2(a,b) a ## b +#define declare(clas,t) name2(clas,declare)(t) +#define implement(clas,t) name2(clas,implement)(t) +// This is the same as the name2 macro, but we need to define our own +// version since rescanning something generated with the name2 macro +// won't necessarily cause name2 to be expanded again. +#define makename2(z, y) makename2_x(z, y) +#define makename2_x(z, y) z##y + +#define NcVal(TYPE) makename2(NcValues_,TYPE) + +#define NcValuesdeclare(TYPE) \ +class NcVal(TYPE) : public NcValues \ +{ \ + public: \ + NcVal(TYPE)( void ); \ + NcVal(TYPE)(long num); \ + NcVal(TYPE)(long num, const TYPE* vals); \ + NcVal(TYPE)(const NcVal(TYPE)&); \ + virtual NcVal(TYPE)& operator=(const NcVal(TYPE)&); \ + virtual ~NcVal(TYPE)( void ); \ + virtual void* base( void ) const; \ + virtual int bytes_for_one( void ) const; \ + virtual ncbyte as_ncbyte( long n ) const; \ + virtual char as_char( long n ) const; \ + virtual short as_short( long n ) const; \ + virtual int as_int( long n ) const; \ + virtual int as_nclong( long n ) const; \ + virtual long as_long( long n ) const; \ + virtual float as_float( long n ) const; \ + virtual double as_double( long n ) const; \ + virtual char* as_string( long n ) const; \ + virtual int invalid( void ) const; \ + private: \ + TYPE* the_values; \ + std::ostream& print(std::ostream&) const; \ +}; + +#define NcTypeEnum(TYPE) makename2(_nc__,TYPE) +#define _nc__ncbyte ncByte +#define _nc__char ncChar +#define _nc__short ncShort +#define _nc__int ncInt +#define _nc__nclong ncLong +#define _nc__long ncLong +#define _nc__float ncFloat +#define _nc__double ncDouble +#define NcValuesimplement(TYPE) \ +NcVal(TYPE)::NcVal(TYPE)( void ) \ + : NcValues(NcTypeEnum(TYPE), 0), the_values(0) \ +{} \ + \ +NcVal(TYPE)::NcVal(TYPE)(long num, const TYPE* vals) \ + : NcValues(NcTypeEnum(TYPE), num) \ +{ \ + the_values = new TYPE[num]; \ + for(int i = 0; i < num; i++) \ + the_values[i] = vals[i]; \ +} \ + \ +NcVal(TYPE)::NcVal(TYPE)(long num) \ + : NcValues(NcTypeEnum(TYPE), num), the_values(new TYPE[num]) \ +{} \ + \ +NcVal(TYPE)::NcVal(TYPE)(const NcVal(TYPE)& v) : \ + NcValues(v) \ +{ \ + delete[] the_values; \ + the_values = new TYPE[v.the_number]; \ + for(int i = 0; i < v.the_number; i++) \ + the_values[i] = v.the_values[i]; \ +} \ + \ +NcVal(TYPE)& NcVal(TYPE)::operator=(const NcVal(TYPE)& v) \ +{ \ + if ( &v != this) { \ + NcValues::operator=(v); \ + delete[] the_values; \ + the_values = new TYPE[v.the_number]; \ + for(int i = 0; i < v.the_number; i++) \ + the_values[i] = v.the_values[i]; \ + } \ + return *this; \ +} \ + \ +void* NcVal(TYPE)::base( void ) const \ +{ \ + return the_values; \ +} \ + \ +NcVal(TYPE)::~NcVal(TYPE)( void ) \ +{ \ + delete[] the_values; \ +} \ + \ +int NcVal(TYPE)::invalid( void ) const \ +{ \ + for(int i=0;i UCHAR_MAX) \ + return ncBad_byte; \ + return (ncbyte) the_values[n]; \ +} + +#define as_char_implement(TYPE) \ +char NcVal(TYPE)::as_char( long n ) const \ +{ \ + if (the_values[n] < CHAR_MIN || the_values[n] > CHAR_MAX) \ + return ncBad_char; \ + return (char) the_values[n]; \ +} + +#define as_short_implement(TYPE) \ +short NcVal(TYPE)::as_short( long n ) const \ +{ \ + if (the_values[n] < SHRT_MIN || the_values[n] > SHRT_MAX) \ + return ncBad_short; \ + return (short) the_values[n]; \ +} + +#define NCINT_MIN INT_MIN +#define NCINT_MAX INT_MAX +#define as_int_implement(TYPE) \ +int NcVal(TYPE)::as_int( long n ) const \ +{ \ + if (the_values[n] < NCINT_MIN || the_values[n] > NCINT_MAX) \ + return ncBad_int; \ + return (int) the_values[n]; \ +} + +#define NCLONG_MIN INT_MIN +#define NCLONG_MAX INT_MAX +#define as_nclong_implement(TYPE) \ +nclong NcVal(TYPE)::as_nclong( long n ) const \ +{ \ + if (the_values[n] < NCLONG_MIN || the_values[n] > NCLONG_MAX) \ + return ncBad_nclong; \ + return (nclong) the_values[n]; \ +} + +#define as_long_implement(TYPE) \ +long NcVal(TYPE)::as_long( long n ) const \ +{ \ + if (the_values[n] < LONG_MIN || the_values[n] > LONG_MAX) \ + return ncBad_long; \ + return (long) the_values[n]; \ +} + +#define as_float_implement(TYPE) \ +inline float NcVal(TYPE)::as_float( long n ) const \ +{ \ + return (float) the_values[n]; \ +} + +#define as_double_implement(TYPE) \ +inline double NcVal(TYPE)::as_double( long n ) const \ +{ \ + return (double) the_values[n]; \ +} + +#define as_string_implement(TYPE) \ +char* NcVal(TYPE)::as_string( long n ) const \ +{ \ + char* s = new char[32]; \ + std::ostringstream ostr; \ + ostr << the_values[n]; \ + ostr.str().copy(s, std::string::npos); \ + s[ostr.str().length()] = 0; \ + return s; \ +} + +class NcValues // ABC for value blocks +{ + public: + NcValues( void ); + NcValues(NcType, long); + virtual ~NcValues( void ); + virtual long num( void ); + virtual std::ostream& print(std::ostream&) const = 0; + virtual void* base( void ) const = 0; + virtual int bytes_for_one( void ) const = 0; + + // The following member functions provide conversions from the value + // type to a desired basic type. If the value is out of range, the + // default "fill-value" for the appropriate type is returned. + virtual ncbyte as_ncbyte( long n ) const = 0; // nth value as a byte + virtual char as_char( long n ) const = 0; // nth value as char + virtual short as_short( long n ) const = 0; // nth value as short + virtual int as_int( long n ) const = 0; // nth value as int + virtual int as_nclong( long n ) const = 0; // nth value as nclong + virtual long as_long( long n ) const = 0; // nth value as long + virtual float as_float( long n ) const = 0; // nth value as floating-point + virtual double as_double( long n ) const = 0; // nth value as double + virtual char* as_string( long n ) const = 0; // value as string + + protected: + NcType the_type; + long the_number; + friend std::ostream& operator<< (std::ostream&, const NcValues&); +}; + +declare(NcValues,ncbyte) +declare(NcValues,char) +declare(NcValues,short) +declare(NcValues,int) +declare(NcValues,nclong) +declare(NcValues,long) +declare(NcValues,float) +declare(NcValues,double) + +#endif diff --git a/src/lib/netcdf.cpp b/src/lib/netcdf.cpp new file mode 100644 index 0000000..de5c67f --- /dev/null +++ b/src/lib/netcdf.cpp @@ -0,0 +1,1660 @@ +/********************************************************************* + * Copyright 1992, University Corporation for Atmospheric Research + * See netcdf/README file for copying and redistribution conditions. + * + * Purpose: Implements class interface for netCDF over C interface + * + * $Header: /upc/share/CVS/netcdf-3/cxx/netcdf.cpp,v 1.18 2009/03/10 15:20:54 russ Exp $ + *********************************************************************/ + +#include +#include +#include + +#include "netcdf_config.h" +#include "netcdfcpp.h" +#pragma warning(disable : 4996) + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +static const int ncGlobal = NC_GLOBAL; // psuedo-variable for global attributes + +static const int ncBad = -1; // failure return for netCDF C interface + +NcFile::~NcFile( void ) +{ + (void) close(); +} + +NcBool NcFile::is_valid( void ) const +{ + return the_id != ncBad; +} + +int NcFile::num_dims( void ) const +{ + int num = 0; + if (is_valid()) + NcError::set_err( + nc_inq_ndims(the_id, &num) + ); + return num; +} + +int NcFile::num_vars( void ) const +{ + int num = 0; + if (is_valid()) + NcError::set_err( + nc_inq_nvars(the_id, &num) + ); + return num; +} + +int NcFile::num_atts( void ) const +{ + int num = 0; + if (is_valid()) + NcError::set_err( + nc_inq_natts(the_id, &num) + ); + return num; +} + +NcDim* NcFile::get_dim( NcToken name ) const +{ + int dimid; + if(NcError::set_err( + nc_inq_dimid(the_id, name, &dimid) + ) != NC_NOERR) + return 0; + return get_dim(dimid); +} + +NcVar* NcFile::get_var( NcToken name ) const +{ + int varid; + if(NcError::set_err( + nc_inq_varid(the_id, name, &varid) + ) != NC_NOERR) + return 0; + return get_var(varid); +} + +NcAtt* NcFile::get_att( NcToken aname ) const +{ + return is_valid() ? globalv->get_att(aname) : 0; +} + +NcDim* NcFile::get_dim( int i ) const +{ + if (! is_valid() || i < 0 || i >= num_dims()) + return 0; + return dimensions[i]; +} + +NcVar* NcFile::get_var( int i ) const +{ + if (! is_valid() || i < 0 || i >= num_vars()) + return 0; + return variables[i]; +} + +NcAtt* NcFile::get_att( int n ) const +{ + return is_valid() ? globalv->get_att(n) : 0; +} + +NcDim* NcFile::rec_dim( ) const +{ + if (! is_valid()) + return 0; + int recdim; + if(NcError::set_err( + nc_inq_unlimdim(the_id, &recdim) + ) != NC_NOERR) + return 0; + return get_dim(recdim); +} + +NcDim* NcFile::add_dim(NcToken name, long size) +{ + if (!is_valid() || !define_mode()) + return 0; + int n = num_dims(); + NcDim* dimp = new NcDim(this, name, size); + dimensions[n] = dimp; // for garbage collection on close() + return dimp; +} + +NcDim* NcFile::add_dim(NcToken name) +{ + return add_dim(name, NC_UNLIMITED); +} + +// To create scalar, 1-dimensional, ..., 5-dimensional variables, just supply +// as many dimension arguments as necessary + +NcVar* NcFile::add_var(NcToken name, NcType type, // scalar to 5D var + const NcDim* dim0, + const NcDim* dim1, + const NcDim* dim2, + const NcDim* dim3, + const NcDim* dim4) +{ + if (!is_valid() || !define_mode()) + return 0; + int dims[5]; + int ndims = 0; + if (dim0) { + ndims++; + dims[0] = dim0->id(); + if (dim1) { + ndims++; + dims[1] = dim1->id(); + if (dim2) { + ndims++; + dims[2] = dim2->id(); + if (dim3) { + ndims++; + dims[3] = dim3->id(); + if (dim4) { + ndims++; + dims[4] = dim4->id(); + } + } + } + } + } + int n = num_vars(); + int varid; + if(NcError::set_err( + nc_def_var(the_id, name, (nc_type) type, ndims, dims, &varid) + ) != NC_NOERR) + return 0; + NcVar* varp = + new NcVar(this, varid); + variables[n] = varp; + return varp; +} + +// For variables with more than 5 dimensions, use n-dimensional interface +// with vector of dimensions. + +NcVar* NcFile::add_var(NcToken name, NcType type, int ndims, const NcDim** dims) +{ + if (!is_valid() || !define_mode()) + return 0; + int* dimids = new int[ndims]; + for (int i=0; i < ndims; i++) + dimids[i] = dims[i]->id(); + int n = num_vars(); + int varid; + if(NcError::set_err( + nc_def_var(the_id, name, (nc_type) type, ndims, dimids, &varid) + ) != NC_NOERR) + return 0; + NcVar* varp = + new NcVar(this, varid); + variables[n] = varp; + delete [] dimids; + return varp; +} + +#define NcFile_add_scalar_att(TYPE) \ +NcBool NcFile::add_att(NcToken aname, TYPE val) \ +{ \ + return globalv->add_att(aname, val); \ +} + +NcFile_add_scalar_att(char) +NcFile_add_scalar_att(ncbyte) +NcFile_add_scalar_att(short) +NcFile_add_scalar_att(int) +NcFile_add_scalar_att(long) +NcFile_add_scalar_att(float) +NcFile_add_scalar_att(double) +NcFile_add_scalar_att(const char*) + +#define NcFile_add_vector_att(TYPE) \ +NcBool NcFile::add_att(NcToken aname, int n, const TYPE* val) \ +{ \ + return globalv->add_att(aname, n, val); \ +} + +NcFile_add_vector_att(char) +NcFile_add_vector_att(ncbyte) +NcFile_add_vector_att(short) +NcFile_add_vector_att(int) +NcFile_add_vector_att(long) +NcFile_add_vector_att(float) +NcFile_add_vector_att(double) + +NcBool NcFile::set_fill( FillMode a_mode ) +{ + int prev_mode; + if (NcError::set_err( + nc_set_fill(the_id, a_mode, &prev_mode) + ) == NC_NOERR) { + the_fill_mode = a_mode; + return TRUE; + } + return FALSE; +} + +NcFile::FillMode NcFile::get_fill( void ) const +{ + return the_fill_mode; +} + +NcFile::FileFormat NcFile::get_format( void ) const +{ + int the_format; + NcError::set_err( + nc_inq_format(the_id, &the_format) + ); + switch (the_format) { + case NC_FORMAT_CLASSIC: + return Classic; + case NC_FORMAT_64BIT: + return Offset64Bits; + case NC_FORMAT_NETCDF4: + return Netcdf4; + case NC_FORMAT_NETCDF4_CLASSIC: + return Netcdf4Classic; + default: + return BadFormat; + } +} + +NcBool NcFile::sync( void ) +{ + if (!data_mode()) + return 0; + if (NcError::set_err( + nc_sync(the_id) + ) != NC_NOERR) + return 0; + int i; + for (i = 0; i < num_dims(); i++) { + if (dimensions[i]->is_valid()) { + dimensions[i]->sync(); + } else { // someone else added a new dimension + dimensions[i] = new NcDim(this,i); + } + } + for (i = 0; i < num_vars(); i++) { + if (variables[i]->is_valid()) { + variables[i]->sync(); + } else { // someone else added a new variable + variables[i] = new NcVar(this,i); + } + } + return 1; +} + +NcBool NcFile::close( void ) +{ + int i; + + if (the_id == ncBad) + return 0; + for (i = 0; i < num_dims(); i++) + delete dimensions[i]; + for (i = 0; i < num_vars(); i++) + delete variables[i]; + delete [] dimensions; + delete [] variables; + delete globalv; + int old_id = the_id; + the_id = ncBad; + return NcError::set_err( + nc_close(old_id) + ) == NC_NOERR; +} + +NcBool NcFile::abort( void ) +{ + return NcError::set_err( + nc_abort(the_id) + ) == NC_NOERR; +} + +NcBool NcFile::define_mode( void ) +{ + if (! is_valid()) + return FALSE; + if (in_define_mode) + return TRUE; + if (NcError::set_err( + nc_redef(the_id) + ) != NC_NOERR) + return FALSE; + in_define_mode = 1; + return TRUE; +} + +NcBool NcFile::data_mode( void ) +{ + if (! is_valid()) + return FALSE; + if (! in_define_mode) + return TRUE; + if (NcError::set_err( + nc_enddef(the_id) + ) != NC_NOERR) + return FALSE; + in_define_mode = 0; + return TRUE; +} + +int NcFile::id( void ) const +{ + return the_id; +} + +NcFile::NcFile( const char* path, FileMode fmode, + size_t* bufrsizeptr, size_t initialsize, FileFormat fformat ) +{ + NcError err(NcError::silent_nonfatal); // constructor must not fail + + int mode = NC_NOWRITE; + the_fill_mode = Fill; + int status; + + // If the user wants a 64-bit offset format, set that flag. + if (fformat == Offset64Bits) + mode |= NC_64BIT_OFFSET; +#ifdef USE_NETCDF4 + else if (fformat == Netcdf4) + mode |= NC_NETCDF4; + else if (fformat == Netcdf4Classic) + mode |= NC_NETCDF4|NC_CLASSIC_MODEL; +#endif + + switch (fmode) { + case Write: + mode |= NC_WRITE; + /*FALLTHRU*/ + case ReadOnly: + // use netcdf-3 interface to permit specifying tuning parameter + status = NcError::set_err( + nc__open(path, mode, bufrsizeptr, &the_id) + ); + if(status != NC_NOERR) + { + NcError::set_err(status); + the_id = -1; + } + in_define_mode = 0; + break; + case New: + mode |= NC_NOCLOBBER; + /*FALLTHRU*/ + case Replace: + // use netcdf-3 interface to permit specifying tuning parameters + status = NcError::set_err( + nc__create(path, mode, initialsize, + bufrsizeptr, &the_id) + ); + if(status != NC_NOERR) + { + NcError::set_err(status); + the_id = -1; + } + in_define_mode = 1; + break; + default: + the_id = ncBad; + in_define_mode = 0; + break; + } + if (is_valid()) { + dimensions = new NcDim*[NC_MAX_DIMS]; + variables = new NcVar*[NC_MAX_VARS]; + int i; + for (i = 0; i < num_dims(); i++) + dimensions[i] = new NcDim(this, i); + for (i = 0; i < num_vars(); i++) + variables[i] = new NcVar(this, i); + globalv = new NcVar(this, ncGlobal); + } else { + dimensions = 0; + variables = 0; + globalv = 0; + } +} + +NcToken NcDim::name( void ) const +{ + return the_name; +} + +long NcDim::size( void ) const +{ + size_t sz = 0; + if (the_file) + NcError::set_err( + nc_inq_dimlen(the_file->id(), the_id, &sz) + ); + return sz; +} + +NcBool NcDim::is_valid( void ) const +{ + return the_file->is_valid() && the_id != ncBad; +} + +NcBool NcDim::is_unlimited( void ) const +{ + if (!the_file) + return FALSE; + int recdim; + NcError::set_err( + nc_inq_unlimdim(the_file->id(), &recdim) + ); + return the_id == recdim; +} + +NcBool NcDim::rename(NcToken newname) +{ + if (strlen(newname) > strlen(the_name)) { + if (! the_file->define_mode()) + return FALSE; + } + NcBool ret = NcError::set_err( + nc_rename_dim(the_file->id(), the_id, newname) + ) == NC_NOERR; + if (ret) { + delete [] the_name; + the_name = new char[1 + strlen(newname)]; + strcpy(the_name, newname); + } + return ret; +} + +int NcDim::id( void ) const +{ + return the_id; +} + +NcBool NcDim::sync(void) +{ + char nam[NC_MAX_NAME]; + if (the_name) { + delete [] the_name; + } + if (the_file && NcError::set_err( + nc_inq_dimname(the_file->id(), the_id, nam) + ) == NC_NOERR) { + the_name = new char[strlen(nam) + 1]; + strcpy(the_name, nam); + return TRUE; + } + the_name = 0; + return FALSE; +} + +NcDim::NcDim(NcFile* nc, int id) + : the_file(nc), the_id(id) +{ + char nam[NC_MAX_NAME]; + if (the_file && NcError::set_err( + nc_inq_dimname(the_file->id(), the_id, nam) + ) == NC_NOERR) { + the_name = new char[strlen(nam) + 1]; + strcpy(the_name, nam); + } else { + the_name = 0; + } +} + +NcDim::NcDim(NcFile* nc, NcToken name, long sz) + : the_file(nc) +{ + size_t dimlen = sz; + if(NcError::set_err( + nc_def_dim(the_file->id(), name, dimlen, &the_id) + ) == NC_NOERR) { + the_name = new char[strlen(name) + 1]; + strcpy(the_name, name); + } else { + the_name = 0; + } +} + +NcDim::~NcDim( void ) +{ + delete [] the_name; +} + +#define Nc_as(TYPE) name2(as_,TYPE) +#define NcTypedComponent_as(TYPE) \ +TYPE NcTypedComponent::Nc_as(TYPE)( long n ) const \ +{ \ + NcValues* tmp = values(); \ + TYPE rval = tmp->Nc_as(TYPE)(n); \ + delete tmp; \ + return rval; \ +} +NcTypedComponent_as(ncbyte) +NcTypedComponent_as(char) +NcTypedComponent_as(short) +NcTypedComponent_as(int) +NcTypedComponent_as(nclong) +NcTypedComponent_as(long) +NcTypedComponent_as(float) +NcTypedComponent_as(double) + +char* NcTypedComponent::as_string( long n ) const +{ + NcValues* tmp = values(); + char* rval = tmp->as_string(n); + delete tmp; + return rval; +} + +NcTypedComponent::NcTypedComponent ( NcFile* nc ) + : the_file(nc) +{} + +NcValues* NcTypedComponent::get_space( long numVals ) const +{ + NcValues* valp; + if (numVals < 1) + numVals = num_vals(); + switch (type()) { + case ncFloat: + valp = new NcValues_float(numVals); + break; + case ncDouble: + valp = new NcValues_double(numVals); + break; + case ncInt: + valp = new NcValues_int(numVals); + break; + case ncShort: + valp = new NcValues_short(numVals); + break; + case ncByte: + case ncChar: + valp = new NcValues_char(numVals); + break; + case ncNoType: + default: + valp = 0; + } + return valp; +} + +NcVar::~NcVar( void ) +{ + delete[] the_cur; + delete[] cur_rec; + delete[] the_name; +} + +NcToken NcVar::name( void ) const +{ + return the_name; +} + +NcType NcVar::type( void ) const +{ + nc_type typ; + NcError::set_err( + nc_inq_vartype(the_file->id(), the_id, &typ) + ); + return (NcType) typ; +} + +NcBool NcVar::is_valid( void ) const +{ + return the_file->is_valid() && the_id != ncBad; +} + +int NcVar::num_dims( void ) const +{ + int ndim; + NcError::set_err( + nc_inq_varndims(the_file->id(), the_id, &ndim) + ); + return ndim; +} + +// The i-th dimension for this variable +NcDim* NcVar::get_dim( int i ) const +{ + int ndim; + int dims[NC_MAX_DIMS]; + if(NcError::set_err( + nc_inq_var(the_file->id(), the_id, 0, 0, &ndim, dims, 0) + ) != NC_NOERR || + i < 0 || i >= ndim) + return 0; + return the_file->get_dim(dims[i]); +} + +long* NcVar::edges( void ) const // edge lengths (dimension sizes) +{ + long* evec = new long[num_dims()]; + for(int i=0; i < num_dims(); i++) + evec[i] = get_dim(i)->size(); + return evec; +} + +int NcVar::num_atts( void ) const // handles variable and global atts +{ + int natt = 0; + if (the_file->is_valid()) + if (the_id == ncGlobal) + natt = the_file->num_atts(); + else + NcError::set_err( + nc_inq_varnatts(the_file->id(), the_id, &natt) + ); + return natt; +} + +NcAtt* NcVar::get_att( NcToken aname ) const +{ + NcAtt* att = new NcAtt(the_file, this, aname); + if (! att->is_valid()) { + delete att; + return 0; + } + return att; +} + +NcAtt* NcVar::get_att( int n ) const +{ + if (n < 0 || n >= num_atts()) + return 0; + NcToken aname = attname(n); + NcAtt* ap = get_att(aname); + delete [] (char*)aname; + return ap; +} + +long NcVar::num_vals( void ) const +{ + long prod = 1; + for (int d = 0; d < num_dims(); d++) + prod *= get_dim(d)->size(); + return prod; +} + +NcValues* NcVar::values( void ) const +{ + int ndims = num_dims(); + size_t crnr[NC_MAX_DIMS]; + size_t edgs[NC_MAX_DIMS]; + for (int i = 0; i < ndims; i++) { + crnr[i] = 0; + edgs[i] = get_dim(i)->size(); + } + NcValues* valp = get_space(); + int status; + switch (type()) { + case ncFloat: + status = NcError::set_err( + nc_get_vara_float(the_file->id(), the_id, crnr, edgs, + (float *)valp->base()) + ); + break; + case ncDouble: + status = NcError::set_err( + nc_get_vara_double(the_file->id(), the_id, crnr, edgs, + (double *)valp->base()) + ); + break; + case ncInt: + status = NcError::set_err( + nc_get_vara_int(the_file->id(), the_id, crnr, edgs, + (int *)valp->base()) + ); + break; + case ncShort: + status = NcError::set_err( + nc_get_vara_short(the_file->id(), the_id, crnr, edgs, + (short *)valp->base()) + ); + break; + case ncByte: + status = NcError::set_err( + nc_get_vara_schar(the_file->id(), the_id, crnr, edgs, + (signed char *)valp->base()) + ); + break; + case ncChar: + status = NcError::set_err( + nc_get_vara_text(the_file->id(), the_id, crnr, edgs, + (char *)valp->base()) + ); + break; + case ncNoType: + default: + return 0; + } + if (status != NC_NOERR) + return 0; + return valp; +} + +int NcVar::dim_to_index(NcDim *rdim) +{ + for (int i=0; i < num_dims() ; i++) { + if (strcmp(get_dim(i)->name(),rdim->name()) == 0) { + return i; + } + } + // we should fail and gripe about it here.... + return -1; +} + +void NcVar::set_rec(NcDim *rdim, long slice) +{ + int i = dim_to_index(rdim); + // we should fail and gripe about it here.... + if (slice >= get_dim(i)->size() && ! get_dim(i)->is_unlimited()) + return; + cur_rec[i] = slice; + return; +} + +void NcVar::set_rec(long rec) +{ + // Since we can't ask for the record dimension here + // just assume [0] is it..... + set_rec(get_dim(0),rec); + return; +} + +NcValues* NcVar::get_rec(void) +{ + return get_rec(get_dim(0), cur_rec[0]); +} + +NcValues* NcVar::get_rec(long rec) +{ + return get_rec(get_dim(0), rec); +} + +NcValues* NcVar::get_rec(NcDim* rdim, long slice) +{ + int idx = dim_to_index(rdim); + long size = num_dims(); + size_t* start = new size_t[size]; + long* startl = new long[size]; + for (int i=1; i < size ; i++) { + start[i] = 0; + startl[i] = 0; + } + start[idx] = slice; + startl[idx] = slice; + NcBool result = set_cur(startl); + if (! result ) { + delete [] start; + delete [] startl; + return 0; + } + + long* edgel = edges(); + size_t* edge = new size_t[size]; + for (int i=1; i < size ; i++) { + edge[i] = edgel[i]; + } + edge[idx] = 1; + edgel[idx] = 1; + NcValues* valp = get_space(rec_size(rdim)); + int status; + switch (type()) { + case ncFloat: + status = NcError::set_err( + nc_get_vara_float(the_file->id(), the_id, start, edge, + (float *)valp->base()) + ); + break; + case ncDouble: + status = NcError::set_err( + nc_get_vara_double(the_file->id(), the_id, start, edge, + (double *)valp->base()) + ); + break; + case ncInt: + status = NcError::set_err( + nc_get_vara_int(the_file->id(), the_id, start, edge, + (int *)valp->base()) + ); + break; + case ncShort: + status = NcError::set_err( + nc_get_vara_short(the_file->id(), the_id, start, edge, + (short *)valp->base()) + ); + break; + case ncByte: + status = NcError::set_err( + nc_get_vara_schar(the_file->id(), the_id, start, edge, + (signed char *)valp->base()) + ); + break; + case ncChar: + status = NcError::set_err( + nc_get_vara_text(the_file->id(), the_id, start, edge, + (char *)valp->base()) + ); + break; + case ncNoType: + default: + return 0; + } + delete [] start; + delete [] startl; + delete [] edge; + delete [] edgel; + if (status != NC_NOERR) { + delete valp; + return 0; + } + return valp; +} + + +#define NcVar_put_rec(TYPE) \ +NcBool NcVar::put_rec( const TYPE* vals) \ +{ \ + return put_rec(get_dim(0), vals, cur_rec[0]); \ +} \ + \ +NcBool NcVar::put_rec( NcDim *rdim, const TYPE* vals) \ +{ \ + int idx = dim_to_index(rdim); \ + return put_rec(rdim, vals, cur_rec[idx]); \ +} \ + \ +NcBool NcVar::put_rec( const TYPE* vals, \ + long rec) \ +{ \ + return put_rec(get_dim(0), vals, rec); \ +} \ + \ +NcBool NcVar::put_rec( NcDim* rdim, const TYPE* vals, \ + long slice) \ +{ \ + int idx = dim_to_index(rdim); \ + long size = num_dims(); \ + long* start = new long[size]; \ + for (int i=1; i < size ; i++) start[i] = 0; \ + start[idx] = slice; \ + NcBool result = set_cur(start); \ + delete [] start; \ + if (! result ) \ + return FALSE; \ + \ + long* edge = edges(); \ + edge[idx] = 1; \ + result = put(vals, edge); \ + delete [] edge; \ + return result; \ +} + +NcVar_put_rec(ncbyte) +NcVar_put_rec(char) +NcVar_put_rec(short) +NcVar_put_rec(int) +NcVar_put_rec(long) +NcVar_put_rec(float) +NcVar_put_rec(double) + +long NcVar::rec_size(void) { + return rec_size(get_dim(0)); +} + +long NcVar::rec_size(NcDim *rdim) { + int idx = dim_to_index(rdim); + long size = 1; + long* edge = edges(); + for( int i = 0 ; idata_mode()) \ + return -1; \ +int idx = dim_to_index(rdim); \ +long maxrec = get_dim(idx)->size(); \ +long maxvals = rec_size(rdim); \ +NcValues* val; \ +int validx; \ +for (long j=0; jas_ ## TYPE(validx)) break; \ + } \ + delete val; \ + if (validx == maxvals) return j; \ + } \ +return -1; \ +} + + +NcVar_get_index(ncbyte) +NcVar_get_index(char) +NcVar_get_index(short) +NcVar_get_index(nclong) +NcVar_get_index(long) +NcVar_get_index(float) +NcVar_get_index(double) + +// Macros below work for short, nclong, long, float, and double, but for ncbyte +// and char, we must use corresponding schar, uchar, or text C functions, so in +// these cases macros are expanded manually. +#define NcVar_put_array(TYPE) \ +NcBool NcVar::put( const TYPE* vals, \ + long edge0, \ + long edge1, \ + long edge2, \ + long edge3, \ + long edge4) \ +{ \ + /* no need to check type() vs. TYPE, invoked C function will do that */ \ + if (! the_file->data_mode()) \ + return FALSE; \ + size_t count[5]; \ + count[0] = edge0; \ + count[1] = edge1; \ + count[2] = edge2; \ + count[3] = edge3; \ + count[4] = edge4; \ + for (int i = 0; i < 5; i++) { \ + if (count[i]) { \ + if (num_dims() < i) \ + return FALSE; \ + } else \ + break; \ + } \ + size_t start[5]; \ + for (int j = 0; j < 5; j++) { \ + start[j] = the_cur[j]; \ + } \ + return NcError::set_err( \ + makename2(nc_put_vara_,TYPE) (the_file->id(), the_id, start, count, vals) \ + ) == NC_NOERR; \ +} + +NcBool NcVar::put( const ncbyte* vals, + long edge0, + long edge1, + long edge2, + long edge3, + long edge4) +{ + /* no need to check type() vs. TYPE, invoked C function will do that */ + if (! the_file->data_mode()) + return FALSE; + size_t count[5]; + count[0] = edge0; + count[1] = edge1; + count[2] = edge2; + count[3] = edge3; + count[4] = edge4; + for (int i = 0; i < 5; i++) { + if (count[i]) { + if (num_dims() < i) + return FALSE; + } else + break; + } + size_t start[5]; + for (int j = 0; j < 5; j++) { + start[j] = the_cur[j]; + } + return NcError::set_err( + nc_put_vara_schar (the_file->id(), the_id, start, count, vals) + ) == NC_NOERR; +} + +NcBool NcVar::put( const char* vals, + long edge0, + long edge1, + long edge2, + long edge3, + long edge4) +{ + /* no need to check type() vs. TYPE, invoked C function will do that */ + if (! the_file->data_mode()) + return FALSE; + size_t count[5]; + count[0] = edge0; + count[1] = edge1; + count[2] = edge2; + count[3] = edge3; + count[4] = edge4; + for (int i = 0; i < 5; i++) { + if (count[i]) { + if (num_dims() < i) + return FALSE; + } else + break; + } + size_t start[5]; + for (int j = 0; j < 5; j++) { + start[j] = the_cur[j]; + } + return NcError::set_err( + nc_put_vara_text (the_file->id(), the_id, start, count, vals) + ) == NC_NOERR; +} + +NcVar_put_array(short) +NcVar_put_array(int) +NcVar_put_array(long) +NcVar_put_array(float) +NcVar_put_array(double) + +#define NcVar_put_nd_array(TYPE) \ +NcBool NcVar::put( const TYPE* vals, const long* count ) \ +{ \ + /* no need to check type() vs. TYPE, invoked C function will do that */ \ + if (! the_file->data_mode()) \ + return FALSE; \ + size_t start[NC_MAX_DIMS]; \ + for (int i = 0; i < num_dims(); i++) \ + start[i] = the_cur[i]; \ + return NcError::set_err( \ + makename2(nc_put_vara_,TYPE) (the_file->id(), the_id, start, (const size_t *) count, vals) \ + ) == NC_NOERR; \ +} + +NcBool NcVar::put( const ncbyte* vals, const long* count ) +{ + /* no need to check type() vs. TYPE, invoked C function will do that */ + if (! the_file->data_mode()) + return FALSE; + size_t start[NC_MAX_DIMS]; + for (int i = 0; i < num_dims(); i++) + start[i] = the_cur[i]; + return NcError::set_err( + nc_put_vara_schar (the_file->id(), the_id, start, (const size_t *)count, vals) + ) == NC_NOERR; +} + +NcBool NcVar::put( const char* vals, const long* count ) +{ + /* no need to check type() vs. TYPE, invoked C function will do that */ + if (! the_file->data_mode()) + return FALSE; + size_t start[NC_MAX_DIMS]; + for (int i = 0; i < num_dims(); i++) + start[i] = the_cur[i]; + return NcError::set_err( + nc_put_vara_text (the_file->id(), the_id, start, (const size_t *)count, vals) + ) == NC_NOERR; +} + +NcVar_put_nd_array(short) +NcVar_put_nd_array(int) +NcVar_put_nd_array(long) +NcVar_put_nd_array(float) +NcVar_put_nd_array(double) + +#define NcVar_get_array(TYPE) \ +NcBool NcVar::get( TYPE* vals, \ + long edge0, \ + long edge1, \ + long edge2, \ + long edge3, \ + long edge4) const \ +{ \ + if (! the_file->data_mode()) \ + return FALSE; \ + size_t count[5]; \ + count[0] = edge0; \ + count[1] = edge1; \ + count[2] = edge2; \ + count[3] = edge3; \ + count[4] = edge4; \ + for (int i = 0; i < 5; i++) { \ + if (count[i]) { \ + if (num_dims() < i) \ + return FALSE; \ + } else \ + break; \ + } \ + size_t start[5]; \ + for (int j = 0; j < 5; j++) { \ + start[j] = the_cur[j]; \ + } \ + return NcError::set_err( \ + makename2(nc_get_vara_,TYPE) (the_file->id(), the_id, start, count, vals) \ + ) == NC_NOERR; \ +} + +NcBool NcVar::get( ncbyte* vals, + long edge0, + long edge1, + long edge2, + long edge3, + long edge4) const +{ + if (! the_file->data_mode()) + return FALSE; + size_t count[5]; + count[0] = edge0; + count[1] = edge1; + count[2] = edge2; + count[3] = edge3; + count[4] = edge4; + for (int i = 0; i < 5; i++) { + if (count[i]) { + if (num_dims() < i) + return FALSE; + } else + break; + } + size_t start[5]; + for (int j = 0; j < 5; j++) { + start[j] = the_cur[j]; + } + return NcError::set_err( + nc_get_vara_schar (the_file->id(), the_id, start, count, vals) + ) == NC_NOERR; +} + +NcBool NcVar::get( char* vals, + long edge0, + long edge1, + long edge2, + long edge3, + long edge4) const +{ + if (! the_file->data_mode()) + return FALSE; + size_t count[5]; + count[0] = edge0; + count[1] = edge1; + count[2] = edge2; + count[3] = edge3; + count[4] = edge4; + for (int i = 0; i < 5; i++) { + if (count[i]) { + if (num_dims() < i) + return FALSE; + } else + break; + } + size_t start[5]; + for (int j = 0; j < 5; j++) { + start[j] = the_cur[j]; + } + return NcError::set_err( + nc_get_vara_text (the_file->id(), the_id, start, count, vals) + ) == NC_NOERR; +} + +NcVar_get_array(short) +NcVar_get_array(int) +NcVar_get_array(long) +NcVar_get_array(float) +NcVar_get_array(double) + +#define NcVar_get_nd_array(TYPE) \ +NcBool NcVar::get( TYPE* vals, const long* count ) const \ +{ \ + if (! the_file->data_mode()) \ + return FALSE; \ + size_t start[NC_MAX_DIMS]; \ + for (int i = 0; i < num_dims(); i++) \ + start[i] = the_cur[i]; \ + return NcError::set_err( \ + makename2(nc_get_vara_,TYPE) (the_file->id(), the_id, start, (const size_t *) count, vals) \ + ) == NC_NOERR; \ +} + +NcBool NcVar::get( ncbyte* vals, const long* count ) const +{ + if (! the_file->data_mode()) + return FALSE; + size_t start[NC_MAX_DIMS]; + for (int i = 0; i < num_dims(); i++) + start[i] = the_cur[i]; + return nc_get_vara_schar (the_file->id(), the_id, start, (const size_t *) count, vals) == NC_NOERR; +} + +NcBool NcVar::get( char* vals, const long* count ) const +{ + if (! the_file->data_mode()) + return FALSE; + size_t start[NC_MAX_DIMS]; + for (int i = 0; i < num_dims(); i++) + start[i] = the_cur[i]; + return nc_get_vara_text (the_file->id(), the_id, start, (const size_t*) count, vals) == NC_NOERR; +} + +NcVar_get_nd_array(short) +NcVar_get_nd_array(int) +NcVar_get_nd_array(long) +NcVar_get_nd_array(float) +NcVar_get_nd_array(double) + +// If no args, set cursor to all zeros. Else set initial elements of cursor +// to args provided, rest to zeros. +NcBool NcVar::set_cur(long c0, long c1, long c2, long c3, long c4) +{ + long t[6]; + t[0] = c0; + t[1] = c1; + t[2] = c2; + t[3] = c3; + t[4] = c4; + t[5] = -1; + for(int j = 0; j < 6; j++) { // find how many parameters were used + int i; + if (t[j] == -1) { + if (num_dims() < j) + return FALSE; // too many for variable's dimensionality + for (i = 0; i < j; i++) { + if (t[i] >= get_dim(i)->size() && ! get_dim(i)->is_unlimited()) + return FALSE; // too big for dimension + the_cur[i] = t[i]; + } + for(i = j; i < num_dims(); i++) + the_cur[i] = 0; + return TRUE; + } + } + return TRUE; +} + +NcBool NcVar::set_cur(long* cur) +{ + for(int i = 0; i < num_dims(); i++) { + if (cur[i] >= get_dim(i)->size() && ! get_dim(i)->is_unlimited()) + return FALSE; + the_cur[i] = cur[i]; + } + return TRUE; +} + +#define NcVar_add_scalar_att(TYPE) \ +NcBool NcVar::add_att(NcToken aname, TYPE val) \ +{ \ + if (! the_file->define_mode()) \ + return FALSE; \ + if (NcError::set_err( \ + makename2(nc_put_att_,TYPE) (the_file->id(), the_id, aname, (nc_type) NcTypeEnum(TYPE), \ + 1, &val) \ + ) != NC_NOERR) \ + return FALSE; \ + return TRUE; \ +} + +NcBool NcVar::add_att(NcToken aname, ncbyte val) +{ + if (! the_file->define_mode()) + return FALSE; + if (nc_put_att_schar (the_file->id(), the_id, aname, (nc_type) NcTypeEnum(ncbyte), + 1, &val) != NC_NOERR) + return FALSE; + return TRUE; +} + +NcBool NcVar::add_att(NcToken aname, char val) +{ + if (! the_file->define_mode()) + return FALSE; + if (nc_put_att_text (the_file->id(), the_id, aname, + 1, &val) != NC_NOERR) + return FALSE; + return TRUE; +} + +NcVar_add_scalar_att(short) +NcVar_add_scalar_att(int) +NcVar_add_scalar_att(long) +NcVar_add_scalar_att(double) + +NcBool NcVar::add_att(NcToken aname, float val) +{ + if (! the_file->define_mode()) + return FALSE; + float fval = (float) val; // workaround for bug, val passed as double?? + if (nc_put_att_float(the_file->id(), the_id, aname, (nc_type) ncFloat, + 1, &fval) != NC_NOERR) + return FALSE; + return TRUE; +} + +NcBool NcVar::add_att(NcToken aname, const char* val) +{ + if (! the_file->define_mode()) + return FALSE; + if (nc_put_att_text(the_file->id(), the_id, aname, + strlen(val), val) != NC_NOERR) + return FALSE; + return TRUE; +} + +#define NcVar_add_vector_att(TYPE) \ +NcBool NcVar::add_att(NcToken aname, int len, const TYPE* vals) \ +{ \ + if (! the_file->define_mode()) \ + return FALSE; \ + if (NcError::set_err( \ + makename2(nc_put_att_,TYPE) (the_file->id(), the_id, aname, (nc_type) NcTypeEnum(TYPE), \ + len, vals) \ + ) != NC_NOERR) \ + return FALSE; \ + return TRUE; \ +} + +NcBool NcVar::add_att(NcToken aname, int len, const ncbyte* vals) +{ + if (! the_file->define_mode()) + return FALSE; + if (NcError::set_err( + nc_put_att_schar (the_file->id(), the_id, aname, (nc_type) NcTypeEnum(ncbyte), + len, vals) + ) != NC_NOERR) + return FALSE; + return TRUE; +} + +NcBool NcVar::add_att(NcToken aname, int len, const char* vals) +{ + if (! the_file->define_mode()) + return FALSE; + if (NcError::set_err( + nc_put_att_text (the_file->id(), the_id, aname, + len, vals) + ) != NC_NOERR) + return FALSE; + return TRUE; +} + +NcVar_add_vector_att(short) +NcVar_add_vector_att(int) +NcVar_add_vector_att(long) +NcVar_add_vector_att(float) +NcVar_add_vector_att(double) + +NcBool NcVar::rename(NcToken newname) +{ + if (strlen(newname) > strlen(the_name)) { + if (! the_file->define_mode()) + return FALSE; + } + NcBool ret = NcError::set_err( + nc_rename_var(the_file->id(), the_id, newname) + ) == NC_NOERR; + if (ret) { + delete [] the_name; + the_name = new char [1 + strlen(newname)]; + strcpy(the_name, newname); + } + return ret; +} + +int NcVar::id( void ) const +{ + return the_id; +} + +NcBool NcVar::sync(void) +{ + if (the_name) { + delete [] the_name; + } + if (the_cur) { + delete [] the_cur; + } + if (cur_rec) { + delete [] cur_rec; + } + char nam[NC_MAX_NAME]; + if (the_file + && NcError::set_err( + nc_inq_varname(the_file->id(), the_id, nam) + ) == NC_NOERR) { + the_name = new char[1 + strlen(nam)]; + strcpy(the_name, nam); + } else { + the_name = 0; + return FALSE; + } + init_cur(); + return TRUE; +} + + +NcVar::NcVar(NcFile* nc, int id) + : NcTypedComponent(nc), the_id(id) +{ + char nam[NC_MAX_NAME]; + if (the_file + && NcError::set_err( + nc_inq_varname(the_file->id(), the_id, nam) + ) == NC_NOERR) { + the_name = new char[1 + strlen(nam)]; + strcpy(the_name, nam); + } else { + the_name = 0; + } + init_cur(); +} + +int NcVar::attnum( NcToken attrname ) const +{ + int num; + for(num=0; num < num_atts(); num++) { + char aname[NC_MAX_NAME]; + NcError::set_err( + nc_inq_attname(the_file->id(), the_id, num, aname) + ); + if (strcmp(aname, attrname) == 0) + break; + } + return num; // num_atts() if no such attribute +} + +NcToken NcVar::attname( int attnum ) const // caller must delete[] +{ + if (attnum < 0 || attnum >= num_atts()) + return 0; + char aname[NC_MAX_NAME]; + if (NcError::set_err( + nc_inq_attname(the_file->id(), the_id, attnum, aname) + ) != NC_NOERR) + return 0; + char* rname = new char[1 + strlen(aname)]; + strcpy(rname, aname); + return rname; +} + +void NcVar::init_cur( void ) +{ + the_cur = new long[NC_MAX_DIMS]; // *** don't know num_dims() yet? + cur_rec = new long[NC_MAX_DIMS]; // *** don't know num_dims() yet? + for(int i = 0; i < NC_MAX_DIMS; i++) { + the_cur[i] = 0; cur_rec[i] = 0; } +} + +NcAtt::NcAtt(NcFile* nc, const NcVar* var, NcToken name) + : NcTypedComponent(nc), the_variable(var) +{ + the_name = new char[1 + strlen(name)]; + strcpy(the_name, name); +} + +NcAtt::NcAtt(NcFile* nc, NcToken name) + : NcTypedComponent(nc), the_variable(NULL) +{ + the_name = new char[1 + strlen(name)]; + strcpy(the_name, name); +} + +NcAtt::~NcAtt( void ) +{ + delete [] the_name; +} + +NcToken NcAtt::name( void ) const +{ + return the_name; +} + +NcType NcAtt::type( void ) const +{ + nc_type typ; + NcError::set_err( + nc_inq_atttype(the_file->id(), the_variable->id(), the_name, &typ) + ); + return (NcType) typ; +} + +long NcAtt::num_vals( void ) const +{ + size_t len; + NcError::set_err( + nc_inq_attlen(the_file->id(), the_variable->id(), the_name, &len) + ); + return len; +} + +NcBool NcAtt::is_valid( void ) const +{ + int num; + return the_file->is_valid() && + (the_variable->id() == NC_GLOBAL || the_variable->is_valid()) && + NcError::set_err( + nc_inq_attid(the_file->id(), the_variable->id(), the_name, &num) + ) == NC_NOERR; +} + +NcValues* NcAtt::values( void ) const +{ + NcValues* valp = get_space(); + int status; + switch (type()) { + case ncFloat: + status = NcError::set_err( + nc_get_att_float(the_file->id(), the_variable->id(), the_name, + (float *)valp->base()) + ); + break; + case ncDouble: + status = NcError::set_err( + nc_get_att_double(the_file->id(), the_variable->id(), the_name, + (double *)valp->base()) + ); + break; + case ncInt: + status = NcError::set_err( + nc_get_att_int(the_file->id(), the_variable->id(), the_name, + (int *)valp->base()) + ); + break; + case ncShort: + status = NcError::set_err( + nc_get_att_short(the_file->id(), the_variable->id(), the_name, + (short *)valp->base()) + ); + break; + case ncByte: + status = NcError::set_err( + nc_get_att_schar(the_file->id(), the_variable->id(), the_name, + (signed char *)valp->base()) + ); + break; + case ncChar: + status = NcError::set_err( + nc_get_att_text(the_file->id(), the_variable->id(), the_name, + (char *)valp->base()) + ); + break; + case ncNoType: + default: + return 0; + } + if (status != NC_NOERR) { + delete valp; + return 0; + } + return valp; +} + +NcBool NcAtt::rename(NcToken newname) +{ + if (strlen(newname) > strlen(the_name)) { + if (! the_file->define_mode()) + return FALSE; + } + return NcError::set_err( + nc_rename_att(the_file->id(), the_variable->id(), + the_name, newname) + ) == NC_NOERR; +} + +NcBool NcAtt::remove( void ) +{ + if (! the_file->define_mode()) + return FALSE; + return NcError::set_err( + nc_del_att(the_file->id(), the_variable->id(), the_name) + ) == NC_NOERR; +} + +NcError::NcError( Behavior b ) +{ + the_old_state = ncopts; // global variable in version 2 C interface + the_old_err = ncerr; // global variable in version 2 C interface + ncopts = (int) b; +} + +NcError::~NcError( void ) +{ + ncopts = the_old_state; + ncerr = the_old_err; +} + +int NcError::get_err( void ) // returns most recent error +{ + return ncerr; +} + +int NcError::set_err (int err) +{ + ncerr = err; + // Check ncopts and handle appropriately + if(err != NC_NOERR) { + if(ncopts == verbose_nonfatal || ncopts == verbose_fatal) { + std::cout << nc_strerror(err) << std::endl; + } + if(ncopts == silent_fatal || ncopts == verbose_fatal) { + exit(ncopts); + } + } + return err; +} + +int NcError::ncerr = NC_NOERR; +int NcError::ncopts = NcError::verbose_fatal ; // for backward compatibility diff --git a/src/lib/netcdf_config.h b/src/lib/netcdf_config.h new file mode 100644 index 0000000..3139165 --- /dev/null +++ b/src/lib/netcdf_config.h @@ -0,0 +1,92 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* if true, run extra tests which may not work yet */ +#undef EXTRA_TESTS + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `nccreate' function. */ +#undef HAVE_NCCREATE + +/* Define to 1 if you have the `nc_def_opaque' function. */ +#undef HAVE_NC_DEF_OPAQUE + +/* Define to 1 if you have the `nc_set_log_level' function. */ +#undef HAVE_NC_SET_LOG_LEVEL + +/* Define to 1 if you have the `nc_use_parallel_enabled' function. */ +#undef HAVE_NC_USE_PARALLEL_ENABLED + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETCDF_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* do large file tests */ +#undef LARGE_FILE_TESTS + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Place to put very large netCDF test files. */ +#undef TEMP_LARGE + +/* Version number of package */ +#undef VERSION + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES diff --git a/src/lib/netcdfcpp.h b/src/lib/netcdfcpp.h new file mode 100644 index 0000000..2f828e0 --- /dev/null +++ b/src/lib/netcdfcpp.h @@ -0,0 +1,469 @@ +/********************************************************************* + * Copyright 1992, University Corporation for Atmospheric Research + * See netcdf/README file for copying and redistribution conditions. + * + * Purpose: C++ class interface for netCDF + * + * $Header: /upc/share/CVS/netcdf-3/cxx/netcdfcpp.h,v 1.15 2009/03/10 15:20:54 russ Exp $ + *********************************************************************/ + +#ifndef NETCDF_HH +#define NETCDF_HH + +#include "ncvalues.h" // arrays that know their element type + +typedef const char* NcToken; // names for netCDF objects +typedef unsigned int NcBool; // many members return 0 on failure + +class NcDim; // dimensions +class NcVar; // variables +class NcAtt; // attributes + +/* + * *********************************************************************** + * A netCDF file. + * *********************************************************************** + */ +class NcFile +{ + public: + + virtual ~NcFile( void ); + + enum FileMode { + ReadOnly, // file exists, open read-only + Write, // file exists, open for writing + Replace, // create new file, even if already exists + New // create new file, fail if already exists + }; + + enum FileFormat { + Classic, // netCDF classic format (i.e. version 1 format) + Offset64Bits, // netCDF 64-bit offset format + Netcdf4, // netCDF-4 using HDF5 format + Netcdf4Classic, // netCDF-4 using HDF5 format using only netCDF-3 calls + BadFormat + }; + + NcFile( const char * path, FileMode = ReadOnly , + size_t *bufrsizeptr = NULL, // optional tuning parameters + size_t initialsize = 0, + FileFormat = Classic ); + + NcBool is_valid( void ) const; // opened OK in ctr, still valid + + int num_dims( void ) const; // number of dimensions + int num_vars( void ) const; // number of variables + int num_atts( void ) const; // number of (global) attributes + + NcDim* get_dim( NcToken ) const; // dimension by name + NcVar* get_var( NcToken ) const; // variable by name + NcAtt* get_att( NcToken ) const; // global attribute by name + + NcDim* get_dim( int ) const; // n-th dimension + NcVar* get_var( int ) const; // n-th variable + NcAtt* get_att( int ) const; // n-th global attribute + NcDim* rec_dim( void ) const; // unlimited dimension, if any + + // Add new dimensions, variables, global attributes. + // These put the file in "define" mode, so could be expensive. + virtual NcDim* add_dim( NcToken dimname, long dimsize ); + virtual NcDim* add_dim( NcToken dimname ); // unlimited + + virtual NcVar* add_var( NcToken varname, NcType type, // scalar + const NcDim* dim0=0, // 1-dim + const NcDim* dim1=0, // 2-dim + const NcDim* dim2=0, // 3-dim + const NcDim* dim3=0, // 4-dim + const NcDim* dim4=0 ); // 5-dim + virtual NcVar* add_var( NcToken varname, NcType type, // n-dim + int ndims, const NcDim** dims ); + + NcBool add_att( NcToken attname, char ); // scalar attributes + NcBool add_att( NcToken attname, ncbyte ); + NcBool add_att( NcToken attname, short ); + NcBool add_att( NcToken attname, long ); + NcBool add_att( NcToken attname, int ); + NcBool add_att( NcToken attname, float ); + NcBool add_att( NcToken attname, double ); + NcBool add_att( NcToken attname, const char*); // string attribute + NcBool add_att( NcToken attname, int, const char* ); // vector attributes + NcBool add_att( NcToken attname, int, const ncbyte* ); + NcBool add_att( NcToken attname, int, const short* ); + NcBool add_att( NcToken attname, int, const long* ); + NcBool add_att( NcToken attname, int, const int* ); + NcBool add_att( NcToken attname, int, const float* ); + NcBool add_att( NcToken attname, int, const double* ); + + enum FillMode { + Fill = NC_FILL, // prefill (default) + NoFill = NC_NOFILL, // don't prefill + Bad + }; + + NcBool set_fill( FillMode = Fill ); // set fill-mode + FillMode get_fill( void ) const; // get fill-mode + FileFormat get_format( void ) const; // get format version + + NcBool sync( void ); // synchronize to disk + NcBool close( void ); // to close earlier than dtr + NcBool abort( void ); // back out of bad defines + + // Needed by other Nc classes, but users will not need them + NcBool define_mode( void ); // leaves in define mode, if possible + NcBool data_mode( void ); // leaves in data mode, if possible + int id( void ) const; // id used by C interface + + protected: + int the_id; + int in_define_mode; + FillMode the_fill_mode; + NcDim** dimensions; + NcVar** variables; + NcVar* globalv; // "variable" for global attributes +}; + +/* + * For backward compatibility. We used to derive NcOldFile and NcNewFile + * from NcFile, but that was over-zealous inheritance. + */ +#define NcOldFile NcFile +#define NcNewFile NcFile +#define Clobber Replace +#define NoClobber New + +/* + * ********************************************************************** + * A netCDF dimension, with a name and a size. These are only created + * by NcFile member functions, because they cannot exist independently + * of an open netCDF file. + * ********************************************************************** + */ +class NcDim +{ + public: + NcToken name( void ) const; + long size( void ) const; + NcBool is_valid( void ) const; + NcBool is_unlimited( void ) const; + NcBool rename( NcToken newname ); + int id( void ) const; + NcBool sync( void ); + + private: + NcFile *the_file; // not const because of rename + int the_id; + char *the_name; + + NcDim(NcFile*, int num); // existing dimension + NcDim(NcFile*, NcToken name, long sz); // defines a new dim + virtual ~NcDim( void ); + + // to construct dimensions, since constructor is private + friend class NcFile; +}; + + +/* + * ********************************************************************** + * Abstract base class for a netCDF variable or attribute, both of which + * have a name, a type, and associated values. These only exist as + * components of an open netCDF file. + * ********************************************************************** + */ +class NcTypedComponent +{ + public: + virtual ~NcTypedComponent( void ) {} + virtual NcToken name( void ) const = 0; + virtual NcType type( void ) const = 0; + virtual NcBool is_valid( void ) const = 0; + virtual long num_vals( void ) const = 0; + virtual NcBool rename( NcToken newname ) = 0; + virtual NcValues* values( void ) const = 0; // block of all values + + // The following member functions provide conversions from the value + // type to a desired basic type. If the value is out of range, + // the default "fill-value" for the appropriate type is returned. + + virtual ncbyte as_ncbyte( long n ) const; // nth value as an unsgnd char + virtual char as_char( long n ) const; // nth value as char + virtual short as_short( long n ) const; // nth value as short + virtual int as_int( long n ) const; // nth value as int + virtual int as_nclong( long n ) const; // nth value as nclong (deprecated) + virtual long as_long( long n ) const; // nth value as long + virtual float as_float( long n ) const; // nth value as floating-point + virtual double as_double( long n ) const; // nth value as double + virtual char* as_string( long n ) const; // nth value as string + + protected: + NcFile *the_file; + NcTypedComponent( NcFile* ); + virtual NcValues* get_space( long numVals = 0 ) const; // to hold values +}; + + +/* + * ********************************************************************** + * netCDF variables. In addition to a name and a type, these also have + * a shape, given by a list of dimensions + * ********************************************************************** + */ +class NcVar : public NcTypedComponent +{ + public: + virtual ~NcVar( void ); + NcToken name( void ) const; + NcType type( void ) const; + NcBool is_valid( void ) const; + int num_dims( void ) const; // dimensionality of variable + NcDim* get_dim( int ) const; // n-th dimension + long* edges( void ) const; // dimension sizes + int num_atts( void ) const; // number of attributes + NcAtt* get_att( NcToken ) const; // attribute by name + NcAtt* get_att( int ) const; // n-th attribute + long num_vals( void ) const; // product of dimension sizes + NcValues* values( void ) const; // all values + + // Put scalar or 1, ..., 5 dimensional arrays by providing enough + // arguments. Arguments are edge lengths, and their number must not + // exceed variable's dimensionality. Start corner is [0,0,..., 0] by + // default, but may be reset using the set_cur() member. FALSE is + // returned if type of values does not match type for variable. + NcBool put( const ncbyte* vals, + long c0=0, long c1=0, long c2=0, long c3=0, long c4=0 ); + NcBool put( const char* vals, + long c0=0, long c1=0, long c2=0, long c3=0, long c4=0 ); + NcBool put( const short* vals, + long c0=0, long c1=0, long c2=0, long c3=0, long c4=0 ); + NcBool put( const int* vals, + long c0=0, long c1=0, long c2=0, long c3=0, long c4=0 ); + NcBool put( const long* vals, + long c0=0, long c1=0, long c2=0, long c3=0, long c4=0 ); + NcBool put( const float* vals, + long c0=0, long c1=0, long c2=0, long c3=0, long c4=0 ); + NcBool put( const double* vals, + long c0=0, long c1=0, long c2=0, long c3=0, long c4=0 ); + + // Put n-dimensional arrays, starting at [0, 0, ..., 0] by default, + // may be reset with set_cur(). + NcBool put( const ncbyte* vals, const long* counts ); + NcBool put( const char* vals, const long* counts ); + NcBool put( const short* vals, const long* counts ); + NcBool put( const int* vals, const long* counts ); + NcBool put( const long* vals, const long* counts ); + NcBool put( const float* vals, const long* counts ); + NcBool put( const double* vals, const long* counts ); + + // Get scalar or 1, ..., 5 dimensional arrays by providing enough + // arguments. Arguments are edge lengths, and their number must not + // exceed variable's dimensionality. Start corner is [0,0,..., 0] by + // default, but may be reset using the set_cur() member. + NcBool get( ncbyte* vals, long c0=0, long c1=0, + long c2=0, long c3=0, long c4=0 ) const; + NcBool get( char* vals, long c0=0, long c1=0, + long c2=0, long c3=0, long c4=0 ) const; + NcBool get( short* vals, long c0=0, long c1=0, + long c2=0, long c3=0, long c4=0 ) const; + NcBool get( int* vals, long c0=0, long c1=0, + long c2=0, long c3=0, long c4=0 ) const; + NcBool get( long* vals, long c0=0, long c1=0, + long c2=0, long c3=0, long c4=0 ) const; + NcBool get( float* vals, long c0=0, long c1=0, + long c2=0, long c3=0, long c4=0 ) const; + NcBool get( double* vals, long c0=0, long c1=0, + long c2=0, long c3=0, long c4=0 ) const; + + // Get n-dimensional arrays, starting at [0, 0, ..., 0] by default, + // may be reset with set_cur(). + NcBool get( ncbyte* vals, const long* counts ) const; + NcBool get( char* vals, const long* counts ) const; + NcBool get( short* vals, const long* counts ) const; + NcBool get( int* vals, const long* counts ) const; + NcBool get( long* vals, const long* counts ) const; + NcBool get( float* vals, const long* counts ) const; + NcBool get( double* vals, const long* counts ) const; + + NcBool set_cur(long c0=-1, long c1=-1, long c2=-1, + long c3=-1, long c4=-1); + NcBool set_cur(long* cur); + + // these put file in define mode, so could be expensive + NcBool add_att( NcToken, char ); // add scalar attributes + NcBool add_att( NcToken, ncbyte ); + NcBool add_att( NcToken, short ); + NcBool add_att( NcToken, int ); + NcBool add_att( NcToken, long ); + NcBool add_att( NcToken, float ); + NcBool add_att( NcToken, double ); + NcBool add_att( NcToken, const char* ); // string attribute + NcBool add_att( NcToken, int, const char* ); // vector attributes + NcBool add_att( NcToken, int, const ncbyte* ); + NcBool add_att( NcToken, int, const short* ); + NcBool add_att( NcToken, int, const int* ); + NcBool add_att( NcToken, int, const long* ); + NcBool add_att( NcToken, int, const float* ); + NcBool add_att( NcToken, int, const double* ); + + NcBool rename( NcToken newname ); + + long rec_size ( void ); // number of values per record + long rec_size ( NcDim* ); // number of values per dimension slice + + // Though following are intended for record variables, they also work + // for other variables, using first dimension as record dimension. + + // Get a record's worth of data + NcValues *get_rec(void); // get current record + NcValues *get_rec(long rec); // get specified record + NcValues *get_rec(NcDim* d); // get current dimension slice + NcValues *get_rec(NcDim* d, long slice); // get specified dimension slice + + // Put a record's worth of data in current record + NcBool put_rec( const ncbyte* vals ); + NcBool put_rec( const char* vals ); + NcBool put_rec( const short* vals ); + NcBool put_rec( const int* vals ); + NcBool put_rec( const long* vals ); + NcBool put_rec( const float* vals ); + NcBool put_rec( const double* vals ); + + // Put a dimension slice worth of data in current dimension slice + NcBool put_rec( NcDim* d, const ncbyte* vals ); + NcBool put_rec( NcDim* d, const char* vals ); + NcBool put_rec( NcDim* d, const short* vals ); + NcBool put_rec( NcDim* d, const int* vals ); + NcBool put_rec( NcDim* d, const long* vals ); + NcBool put_rec( NcDim* d, const float* vals ); + NcBool put_rec( NcDim* d, const double* vals ); + + // Put a record's worth of data in specified record + NcBool put_rec( const ncbyte* vals, long rec ); + NcBool put_rec( const char* vals, long rec ); + NcBool put_rec( const short* vals, long rec ); + NcBool put_rec( const int* vals, long rec ); + NcBool put_rec( const long* vals, long rec ); + NcBool put_rec( const float* vals, long rec ); + NcBool put_rec( const double* vals, long rec ); + + // Put a dimension slice worth of data in specified dimension slice + NcBool put_rec( NcDim* d, const ncbyte* vals, long slice ); + NcBool put_rec( NcDim* d, const char* vals, long slice ); + NcBool put_rec( NcDim* d, const short* vals, long slice ); + NcBool put_rec( NcDim* d, const int* vals, long slice ); + NcBool put_rec( NcDim* d, const long* vals, long slice ); + NcBool put_rec( NcDim* d, const float* vals, long slice ); + NcBool put_rec( NcDim* d, const double* vals, long slice ); + + // Get first record index corresponding to specified key value(s) + long get_index( const ncbyte* vals ); + long get_index( const char* vals ); + long get_index( const short* vals ); + long get_index( const int* vals ); + long get_index( const long* vals ); + long get_index( const float* vals ); + long get_index( const double* vals ); + + // Get first index of specified dimension corresponding to key values + long get_index( NcDim* d, const ncbyte* vals ); + long get_index( NcDim* d, const char* vals ); + long get_index( NcDim* d, const short* vals ); + long get_index( NcDim* d, const int* vals ); + long get_index( NcDim* d, const long* vals ); + long get_index( NcDim* d, const float* vals ); + long get_index( NcDim* d, const double* vals ); + + // Set current record + void set_rec ( long rec ); + // Set current dimension slice + void set_rec ( NcDim* d, long slice ); + + int id( void ) const; // rarely needed, C interface id + NcBool sync( void ); + + private: + int dim_to_index(NcDim* rdim); + int the_id; + long* the_cur; + char* the_name; + long* cur_rec; + + // private constructors because only an NcFile creates these + NcVar( void ); + NcVar(NcFile*, int); + + int attnum( NcToken attname ) const; + NcToken attname( int attnum ) const; + void init_cur( void ); + + // to make variables, since constructor is private + friend class NcFile; +}; + + +/* + * ********************************************************************** + * netCDF attributes. In addition to a name and a type, these are each + * associated with a specific variable, or are global to the file. + * ********************************************************************** + */ +class NcAtt : public NcTypedComponent +{ + public: + virtual ~NcAtt( void ); + NcToken name( void ) const; + NcType type( void ) const; + NcBool is_valid( void ) const; + long num_vals( void ) const; + NcValues* values( void ) const; + NcBool rename( NcToken newname ); + NcBool remove( void ); + + private: + const NcVar* the_variable; + char* the_name; + // protected constructors because only NcVars and NcFiles create + // attributes + NcAtt( NcFile*, const NcVar*, NcToken); + NcAtt( NcFile*, NcToken); // global attribute + + // To make attributes, since constructor is private + friend class NcFile; + friend NcAtt* NcVar::get_att( NcToken ) const; +}; + + +/* + * ********************************************************************** + * To control error handling. Declaring an NcError object temporarily + * changes the error-handling behavior until the object is destroyed, at + * which time the previous error-handling behavior is restored. + * ********************************************************************** + */ +class NcError { + public: + enum Behavior { + silent_nonfatal = 0, + silent_fatal = 1, + verbose_nonfatal = 2, + verbose_fatal = 3 + }; + + // constructor saves previous error state, sets new state + NcError( Behavior b = verbose_fatal ); + + // destructor restores previous error state + virtual ~NcError( void ); + + int get_err( void ); // returns most recent error number + const char* get_errmsg( void ) {return nc_strerror(get_err());} + static int set_err( int err ); + + private: + int the_old_state; + int the_old_err; + static int ncopts; + static int ncerr; +}; + +#endif /* NETCDF_HH */