cfvariable.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. #ifdef HAVE_NETCDF
  2. #include "cfvariable.h"
  3. #include "cf.h"
  4. #include "guessnc.h"
  5. #include <netcdf.h>
  6. #include <string.h>
  7. #include <sstream>
  8. #include <algorithm>
  9. #include <numeric>
  10. // todo:
  11. // Find gridcell by lat/lon (with epsilon), range search?
  12. // Get data by DateTime (closest following?)
  13. // Getting sizes of spatial dimensions
  14. namespace GuessNC {
  15. /// Check whether a data type is one of the NetCDF classic numeric data types
  16. bool classic_numeric_type(nc_type type) {
  17. return
  18. type == NC_BYTE ||
  19. type == NC_SHORT ||
  20. type == NC_INT ||
  21. type == NC_FLOAT ||
  22. type == NC_DOUBLE;
  23. }
  24. namespace CF {
  25. GridcellOrderedVariable::
  26. GridcellOrderedVariable(const char* filename,
  27. const char* variable) {
  28. // Constructor - opens the file and figures out how time works
  29. // Open the file
  30. ncid_file = open_ncdf(filename);
  31. // Get a handle to the main variable
  32. int status = nc_inq_varid(ncid_file, variable, &ncid_var);
  33. handle_error(status, std::string("Failed to get variable ") + variable);
  34. variable_name = GuessNC::get_variable_name(ncid_file, ncid_var);
  35. // Verify datatype - we require one of the NetCDF classic numeric types,
  36. // even though that's not a CF requirement. The 64 bit integral types in
  37. // NetCDF4 would require special care since we can't represent that with
  38. // a double.
  39. nc_type type;
  40. status = nc_inq_vartype(ncid_file, ncid_var, &type);
  41. handle_error(status,
  42. std::string("Failed to get type of main variable: ") +
  43. variable_name);
  44. if (!classic_numeric_type(type)) {
  45. throw CFError(variable, "Main variable must be a NetCDF classic numeric type");
  46. }
  47. // Figure out how time works in this file
  48. find_time_dimension();
  49. // Figure out how the horizontal coordinate system works in this file
  50. find_spatial_dimensions();
  51. // Verify that we haven't identified any dimension in two different roles
  52. if (reduced) {
  53. if (t_dimension_index == landid_dimension_index ||
  54. ncid_t_dimension == ncid_landid_dimension) {
  55. throw CFError(variable, "Ambiguous land/time dimensions");
  56. }
  57. }
  58. else {
  59. if (x_dimension_index == y_dimension_index ||
  60. x_dimension_index == t_dimension_index ||
  61. y_dimension_index == t_dimension_index ||
  62. ncid_x_dimension == ncid_y_dimension ||
  63. ncid_x_dimension == ncid_t_dimension ||
  64. ncid_y_dimension == ncid_t_dimension) {
  65. throw CFError(variable, "Ambiguous x/y/time dimensions");
  66. }
  67. }
  68. // Figure out if there is an extra dimension
  69. find_extra_dimension();
  70. // Verify that the variable has no dimensions apart from those
  71. // which we have already identified and know how to interpret
  72. std::vector<int> dims;
  73. get_dimensions(ncid_file, ncid_var, dims);
  74. for (size_t d = 0; d < dims.size(); ++d) {
  75. int dim = dims[d];
  76. if (dim != ncid_x_dimension &&
  77. dim != ncid_y_dimension &&
  78. dim != ncid_landid_dimension &&
  79. dim != ncid_t_dimension &&
  80. dim != ncid_extra_dimension) {
  81. throw CFError(variable, "Unsupported number of dimensions for variable");
  82. }
  83. }
  84. }
  85. void GridcellOrderedVariable::find_time_dimension() {
  86. // Get the dimensions of the main variable
  87. std::vector<int> ncid_dims;
  88. get_dimensions(ncid_file, ncid_var, ncid_dims);
  89. bool foundit = false;
  90. int ncid_timecoord;
  91. int status;
  92. // For each dimension, check the corresponding coordinate variable
  93. for (size_t d = 0; d < ncid_dims.size(); ++d) {
  94. // Get the corresponding coordinate variable
  95. int ncid_coord_var;
  96. if (!get_coordinate_variable(ncid_file, ncid_dims[d], ncid_coord_var)) {
  97. continue;
  98. }
  99. // According to CF spec, time coordinate variables must have a units attribute
  100. std::string units;
  101. if (!get_attribute(ncid_file, ncid_coord_var, "units", units)) {
  102. continue;
  103. }
  104. // Is the units string a valid time spec?
  105. try {
  106. TimeUnitSpecification tus(units.c_str());
  107. }
  108. catch (const CFError&) {
  109. continue;
  110. }
  111. // Verify datatype - we require one of the NetCDF classic numeric types,
  112. // even though that's not a CF requirement. The 64 bit integral types in
  113. // NetCDF4 would require special care since we can't represent that with
  114. // a double.
  115. nc_type type;
  116. status = nc_inq_vartype(ncid_file, ncid_coord_var, &type);
  117. handle_error(status,
  118. std::string("Failed to get type of time variable: ") +
  119. GuessNC::get_variable_name(ncid_file, ncid_coord_var));
  120. if (!classic_numeric_type(type)) {
  121. throw CFError(variable_name, "Time coordinates must be a NetCDF classic numeric type");
  122. }
  123. // Make sure there's only one time dimension associated with the main variable
  124. if (foundit) {
  125. throw CFError(variable_name, "More than one time dimension associated with main variable");
  126. }
  127. else {
  128. foundit = true;
  129. ncid_t_dimension = ncid_dims[d];
  130. ncid_timecoord = ncid_coord_var;
  131. t_dimension_index = d;
  132. }
  133. }
  134. if (!foundit) {
  135. throw CFError(variable_name, "No proper time dimension found");
  136. }
  137. // We've now found the time dimension and coordinate variable
  138. // read in time data and store the time unit specification
  139. size_t nbr_timesteps;
  140. status = nc_inq_dimlen(ncid_file, ncid_t_dimension, &nbr_timesteps);
  141. // Read in the time variable
  142. time.resize(nbr_timesteps);
  143. status = nc_get_var_double(ncid_file, ncid_timecoord, &time.front());
  144. // Get information about calendar and time unit
  145. std::string calendar_attribute;
  146. if (!get_attribute(ncid_file, ncid_timecoord, "calendar", calendar_attribute)) {
  147. throw CFError(variable_name, "Time coordinate variable didn't have a proper calendar attribute");
  148. }
  149. calendar = parse_calendar(calendar_attribute.c_str());
  150. std::string units_attribute;
  151. if (!get_attribute(ncid_file, ncid_timecoord, "units", units_attribute)) {
  152. throw CFError(variable_name, "Time coordinate variable didn't have a proper units attribute");
  153. }
  154. time_spec = TimeUnitSpecification(units_attribute.c_str());
  155. // Try getting the reference time with the given calendar.
  156. // If the standard/gregorian calendar is used, we only allow dates
  157. // after the Julian/Gregorian switch, time_spec will throw an
  158. // exception if a year before 1583 is used with a standard/gregorian
  159. // calendar.
  160. time_spec.get_date_time(0, calendar);
  161. }
  162. bool is_longitude_coordinate_variable(const std::string& units,
  163. const std::string& standard_name) {
  164. static const char* acceptable_units[] = {
  165. "degree_east",
  166. "degrees_east",
  167. "degree_E",
  168. "degrees_E",
  169. 0 };
  170. if (standard_name == "longitude") {
  171. for (int i = 0; acceptable_units[i] != 0; ++i) {
  172. if (units == acceptable_units[i]) {
  173. return true;
  174. }
  175. }
  176. }
  177. return false;
  178. }
  179. bool is_latitude_coordinate_variable(const std::string& units,
  180. const std::string& standard_name) {
  181. static const char* acceptable_units[] = {
  182. "degree_north",
  183. "degrees_north",
  184. "degree_N",
  185. "degrees_N",
  186. 0 };
  187. if (standard_name == "latitude") {
  188. for (int i = 0; acceptable_units[i] != 0; ++i) {
  189. if (units == acceptable_units[i]) {
  190. return true;
  191. }
  192. }
  193. }
  194. return false;
  195. }
  196. void GridcellOrderedVariable::find_spatial_dimensions() {
  197. // -1 is an invalid variable and dimension id
  198. ncid_lon = -1;
  199. ncid_lat = -1;
  200. ncid_x_dimension = -1;
  201. ncid_y_dimension = -1;
  202. ncid_landid_dimension = -1;
  203. // Loop over main variable's dimensions, try to find out
  204. // which corresponds to x and y
  205. std::vector<int> ncid_dims;
  206. get_dimensions(ncid_file, ncid_var, ncid_dims);
  207. for (size_t d = 0; d < ncid_dims.size(); ++d) {
  208. if (ncid_dims[d] == ncid_t_dimension) {
  209. continue;
  210. }
  211. // Get corresponding coordinate variable
  212. int ncid_coord_var;
  213. if (!get_coordinate_variable(ncid_file, ncid_dims[d], ncid_coord_var)) {
  214. continue;
  215. }
  216. // Check units and standard_name first to see if it's a regular lon/lat
  217. std::string units, standard_name;
  218. if (get_attribute(ncid_file, ncid_coord_var, "units", units) &&
  219. get_attribute(ncid_file, ncid_coord_var, "standard_name", standard_name)) {
  220. if (is_longitude_coordinate_variable(units, standard_name)) {
  221. // This dimension should be the x axis, and it has a corresponding
  222. // coordinate variable for getting longitudes
  223. x_dimension_index = d;
  224. ncid_x_dimension = ncid_dims[d];
  225. ncid_lon = ncid_coord_var;
  226. continue;
  227. }
  228. else if (is_latitude_coordinate_variable(units, standard_name)) {
  229. // This dimension should be the y axis, and it has a corresponding
  230. // coordinate variable for getting latitudes
  231. y_dimension_index = d;
  232. ncid_y_dimension = ncid_dims[d];
  233. ncid_lat = ncid_coord_var;
  234. continue;
  235. }
  236. }
  237. // Otherwise, check if it has an axis attribute indicating X or Y
  238. std::string axis;
  239. if (get_attribute(ncid_file, ncid_coord_var, "axis", axis)) {
  240. if (axis == "X") {
  241. // Found an x axis, but will need to get a longitude (auxiliary)
  242. // coordinate variable via the 'coordinates' attribute
  243. x_dimension_index = d;
  244. ncid_x_dimension = ncid_dims[d];
  245. }
  246. else if (axis == "Y") {
  247. // Found a y axis, but will need to get a latitude (auxiliary)
  248. // coordinate variable via the 'coordinates' attribute
  249. y_dimension_index = d;
  250. ncid_y_dimension = ncid_dims[d];
  251. }
  252. }
  253. }
  254. // If we didn't find lat or lon coordinate variables by now, require that main
  255. // variable has a 'coordinates' attribute, use that to get longitude and
  256. // latitude (auxiliary) coordinate variables
  257. if (ncid_lon == -1 && ncid_lat == -1) {
  258. std::string coordinates;
  259. if (!get_attribute(ncid_file, ncid_var, "coordinates", coordinates)) {
  260. throw CFError(variable_name + " isn't indexed by lat/lon and"\
  261. " doesn't have a 'coordinates' attribute");
  262. }
  263. std::istringstream is(coordinates);
  264. std::string coordinate;
  265. while (is >> coordinate) {
  266. int ncid_coord_var;
  267. int status = nc_inq_varid(ncid_file, coordinate.c_str(), &ncid_coord_var);
  268. handle_error(status, "Failed getting coordinate variable specified in " +
  269. variable_name + ":coordinates");
  270. std::string units, standard_name;
  271. if (get_attribute(ncid_file, ncid_coord_var, "units", units) &&
  272. get_attribute(ncid_file, ncid_coord_var, "standard_name", standard_name)) {
  273. if (is_longitude_coordinate_variable(units, standard_name)) {
  274. ncid_lon = ncid_coord_var;
  275. }
  276. else if (is_latitude_coordinate_variable(units, standard_name)) {
  277. ncid_lat = ncid_coord_var;
  278. }
  279. }
  280. }
  281. }
  282. if (ncid_lon == -1 || ncid_lat == -1) {
  283. throw CFError(variable_name, "Couldn't find coordinate variables for lat/lon");
  284. }
  285. // If we only found one of x or y, something is wrong
  286. if ( (ncid_x_dimension >= 0) != (ncid_y_dimension >= 0) ) {
  287. throw CFError(variable_name, "Found one spatial dimension, but not the other");
  288. }
  289. // If neither, see if main variable has a dimension which also is the one
  290. // and only dimension for the lon/lat auxiliary coordinate variables
  291. std::vector<int> ncid_lon_dims, ncid_lat_dims;
  292. get_dimensions(ncid_file, ncid_lon, ncid_lon_dims);
  293. get_dimensions(ncid_file, ncid_lat, ncid_lat_dims);
  294. if (ncid_lon_dims.size() == 1 &&
  295. ncid_lat_dims.size() == 1 &&
  296. ncid_lon_dims.front() == ncid_lat_dims.front()) {
  297. std::vector<int>::iterator itr =
  298. std::find(ncid_dims.begin(), ncid_dims.end(), ncid_lon_dims.front());
  299. if (itr != ncid_dims.end()) {
  300. landid_dimension_index = itr-ncid_dims.begin();
  301. ncid_landid_dimension = ncid_lon_dims.front();
  302. }
  303. else {
  304. throw CFError(variable_name, "Failed to find x or y axis, or reduced horizontal grid");
  305. }
  306. }
  307. reduced = ncid_landid_dimension != -1;
  308. }
  309. void GridcellOrderedVariable::find_extra_dimension() {
  310. ncid_extra_dimension = -1;
  311. extra_dimension_size = 1;
  312. // Go through all dimensions, if we find one that isn't already
  313. // identified as a time or spatial dimension, we'll assume
  314. // that it's an extra dimension.
  315. std::vector<int> ncid_dims;
  316. get_dimensions(ncid_file, ncid_var, ncid_dims);
  317. for (size_t d = 0; d < ncid_dims.size(); ++d) {
  318. int dim = ncid_dims[d];
  319. if (dim != ncid_t_dimension &&
  320. dim != ncid_x_dimension &&
  321. dim != ncid_y_dimension &&
  322. dim != ncid_landid_dimension) {
  323. // Found an unidentified dimension
  324. extra_dimension_index = d;
  325. ncid_extra_dimension = dim;
  326. int status = nc_inq_dimlen(ncid_file, ncid_extra_dimension, &extra_dimension_size);
  327. handle_error(status, "Failed to get size of extra dimension for " + variable_name);
  328. break;
  329. }
  330. }
  331. }
  332. bool GridcellOrderedVariable::load_data_for(size_t x, size_t y) {
  333. data.resize(get_timesteps() * extra_dimension_size);
  334. if (!location_exists(x, y)) {
  335. return false;
  336. }
  337. size_t start[4];
  338. start[x_dimension_index] = x;
  339. start[y_dimension_index] = y;
  340. start[t_dimension_index] = 0;
  341. size_t count[4];
  342. count[x_dimension_index] = 1;
  343. count[y_dimension_index] = 1;
  344. count[t_dimension_index] = get_timesteps();
  345. ptrdiff_t imap[4];
  346. imap[x_dimension_index] = 1;
  347. imap[y_dimension_index] = 1;
  348. imap[t_dimension_index] = extra_dimension_size;
  349. if (ncid_extra_dimension != -1) {
  350. start[extra_dimension_index] = 0;
  351. count[extra_dimension_index] = extra_dimension_size;
  352. imap[extra_dimension_index] = 1;
  353. }
  354. int status = nc_get_varm_double(ncid_file, ncid_var, start, count, 0, imap, &data.front());
  355. handle_error(status,
  356. std::string("Failed to read data from variable ") + variable_name);
  357. // Check if the data for this location contains a missing value
  358. double missing_value;
  359. if (get_attribute(ncid_file, ncid_var, "missing_value", missing_value)) {
  360. if (std::find(data.begin(), data.end(), missing_value) != data.end()) {
  361. return false;
  362. }
  363. }
  364. unpack_data();
  365. return true;
  366. }
  367. bool GridcellOrderedVariable::load_data_for(size_t landid) {
  368. data.resize(get_timesteps());
  369. if (!location_exists(landid)) {
  370. return false;
  371. }
  372. size_t start[3];
  373. start[landid_dimension_index] = landid;
  374. start[t_dimension_index] = 0;
  375. size_t count[3];
  376. count[landid_dimension_index] = 1;
  377. count[t_dimension_index] = get_timesteps();
  378. ptrdiff_t imap[3];
  379. imap[landid_dimension_index] = 1;
  380. imap[t_dimension_index] = extra_dimension_size;
  381. if (ncid_extra_dimension != -1) {
  382. start[extra_dimension_index] = 0;
  383. count[extra_dimension_index] = extra_dimension_size;
  384. imap[extra_dimension_index] = 1;
  385. }
  386. int status = nc_get_varm_double(ncid_file, ncid_var, start, count, 0, imap, &data.front());
  387. handle_error(status,
  388. std::string("Failed to read data from variable ") + variable_name);
  389. // Check if the data for this location contains a missing value
  390. double missing_value;
  391. if (get_attribute(ncid_file, ncid_var, "missing_value", missing_value)) {
  392. if (std::find(data.begin(), data.end(), missing_value) != data.end()) {
  393. return false;
  394. }
  395. }
  396. unpack_data();
  397. return true;
  398. }
  399. bool GridcellOrderedVariable::is_reduced() const {
  400. return reduced;
  401. }
  402. GridcellOrderedVariable::~GridcellOrderedVariable() {
  403. close_ncdf(ncid_file);
  404. }
  405. void GridcellOrderedVariable::get_coords_for(size_t x, size_t y,
  406. double& lon, double& lat) const {
  407. if (!location_exists(x, y)) {
  408. std::ostringstream os;
  409. os << "Attempted to get coordinates for invalid location: " << x << ", " << y;
  410. throw CFError(variable_name, os.str());
  411. }
  412. size_t lon_index[2];
  413. // Get the dimensions of the lon variable
  414. std::vector<int> londims;
  415. get_dimensions(ncid_file, ncid_lon, londims);
  416. // Loop over the dimensions to set up lon_index correctly with values x and y
  417. for (size_t d = 0; d < londims.size(); ++d) {
  418. if (londims[d] == ncid_x_dimension) {
  419. lon_index[d] = x;
  420. }
  421. else if (londims[d] == ncid_y_dimension) {
  422. lon_index[d] = y;
  423. }
  424. else {
  425. throw CFError(variable_name, "Unexpected dimension found in longitude coordinate variable");
  426. }
  427. }
  428. int status = nc_get_var1_double(ncid_file, ncid_lon, lon_index, &lon);
  429. handle_error(status, "Failed lon indexing");
  430. size_t lat_index[2];
  431. // Get the dimensions of the lat variable
  432. std::vector<int> latdims;
  433. get_dimensions(ncid_file, ncid_lat, latdims);
  434. // Loop over the dimensions to set up lat_index correctly with values x and y
  435. for (size_t d = 0; d < latdims.size(); ++d) {
  436. if (latdims[d] == ncid_x_dimension) {
  437. lat_index[d] = x;
  438. }
  439. else if (latdims[d] == ncid_y_dimension) {
  440. lat_index[d] = y;
  441. }
  442. else {
  443. throw CFError(variable_name, "Unexpected dimension found in latitude coordinate variable");
  444. }
  445. }
  446. status = nc_get_var1_double(ncid_file, ncid_lat, lat_index, &lat);
  447. handle_error(status, "Failed lat indexing");
  448. }
  449. void GridcellOrderedVariable::get_coords_for(size_t landid,
  450. double& lon, double& lat) const {
  451. if (!location_exists(landid)) {
  452. std::ostringstream os;
  453. os << "Attempted to get coordinates for invalid location: " << landid;
  454. throw CFError(variable_name, os.str());
  455. }
  456. const size_t index[] = { landid };
  457. int status = nc_get_var1_double(ncid_file, ncid_lon, index, &lon);
  458. handle_error(status, "Failed lon indexing");
  459. status = nc_get_var1_double(ncid_file, ncid_lat, index, &lat);
  460. handle_error(status, "Failed lat indexing");
  461. }
  462. int GridcellOrderedVariable::get_timesteps() const {
  463. return time.size();
  464. }
  465. double GridcellOrderedVariable::get_value(int timestep) const {
  466. return data[timestep * extra_dimension_size];
  467. }
  468. void GridcellOrderedVariable::get_values(int timestep, std::vector<double>& values) const {
  469. if (values.size() < extra_dimension_size) {
  470. values.resize(extra_dimension_size);
  471. }
  472. for (size_t i = 0; i < extra_dimension_size; ++i) {
  473. values[i] = data[timestep * extra_dimension_size + i];
  474. }
  475. }
  476. DateTime GridcellOrderedVariable::get_date_time(int timestep) const {
  477. return time_spec.get_date_time(time[timestep], calendar);
  478. }
  479. bool GridcellOrderedVariable::location_exists(size_t x, size_t y) const {
  480. if (reduced) {
  481. throw CFError(variable_name, "Attempted to identify location with (x,y)-pair"\
  482. " in file with reduced grid");
  483. }
  484. size_t x_len, y_len;
  485. int status = nc_inq_dimlen(ncid_file, ncid_x_dimension, &x_len);
  486. handle_error(status, "Failed to get x-dimension length");
  487. status = nc_inq_dimlen(ncid_file, ncid_y_dimension, &y_len);
  488. handle_error(status, "Failed to get y-dimension length");
  489. return x < x_len && y < y_len;
  490. }
  491. bool GridcellOrderedVariable::location_exists(size_t landid) const {
  492. if (!reduced) {
  493. throw CFError(variable_name, "Attempted to identify a location with a single index"\
  494. " in a file with (x,y)-coordinate system");
  495. }
  496. size_t land_len;
  497. int status = nc_inq_dimlen(ncid_file, ncid_landid_dimension, &land_len);
  498. handle_error(status, "Failed to get land dimension length");
  499. return landid < land_len;
  500. }
  501. std::string GridcellOrderedVariable::get_units() const {
  502. std::string result;
  503. if (get_attribute(ncid_file, ncid_var, "units", result)) {
  504. return result;
  505. }
  506. else {
  507. // The units attribute isn't required by the CF standard
  508. return "";
  509. }
  510. }
  511. std::string GridcellOrderedVariable::get_variable_name() const {
  512. return variable_name;
  513. }
  514. std::string GridcellOrderedVariable::get_standard_name() const {
  515. std::string attribute;
  516. if (get_attribute(ncid_file, ncid_var, "standard_name", attribute)) {
  517. // Get the first token from the string, ignoring "Standard Name Modifier"
  518. // if there happens to be one
  519. std::string result;
  520. std::istringstream is(attribute);
  521. is >> result;
  522. return result;
  523. }
  524. else {
  525. // The standard_name attribute isn't required by the CF standard
  526. return "";
  527. }
  528. }
  529. std::string GridcellOrderedVariable::get_long_name() const {
  530. std::string result;
  531. if (get_attribute(ncid_file, ncid_var, "long_name", result)) {
  532. return result;
  533. }
  534. else {
  535. // The long_name attribute isn't required by the CF standard
  536. return "";
  537. }
  538. }
  539. void GridcellOrderedVariable::
  540. get_extra_dimension(std::vector<double>& coordinate_values) const {
  541. if (ncid_extra_dimension == -1) {
  542. coordinate_values.resize(0);
  543. }
  544. else {
  545. // Find the corresponding coordinate variable
  546. int ncid_coord;
  547. if (!get_coordinate_variable(ncid_file, ncid_extra_dimension, ncid_coord)) {
  548. throw CFError(variable_name, "Extra dimension has no coordinate variable");
  549. }
  550. size_t start[] = { 0 };
  551. size_t count[] = { extra_dimension_size };
  552. if (coordinate_values.size() < extra_dimension_size) {
  553. coordinate_values.resize(extra_dimension_size);
  554. }
  555. int status = nc_get_vara_double(ncid_file, ncid_coord, start, count, &coordinate_values.front());
  556. handle_error(status, "Failed to read values from extra dimension coordinate variable");
  557. }
  558. }
  559. CalendarType GridcellOrderedVariable::get_calendar_type() const {
  560. return calendar;
  561. }
  562. bool GridcellOrderedVariable::same_spatial_coordinates(int ncid_my_coordvar,
  563. const GridcellOrderedVariable& other,
  564. int ncid_other_coordvar) const {
  565. // Compare dimensions
  566. std::vector<int> mydims;
  567. get_dimensions(ncid_file, ncid_my_coordvar, mydims);
  568. std::vector<int> otherdims;
  569. get_dimensions(other.ncid_file, ncid_other_coordvar, otherdims);
  570. if (mydims.size() != otherdims.size()) {
  571. return false;
  572. }
  573. std::vector<size_t> dim_sizes(mydims.size());
  574. for (size_t i = 0; i < mydims.size(); ++i) {
  575. size_t mydimlen, otherdimlen;
  576. const std::string error_message("Failed to get dimension length of coordinate variable associated with variable ");
  577. int status = nc_inq_dimlen(ncid_file, mydims[i], &mydimlen);
  578. handle_error(status, error_message + variable_name);
  579. status = nc_inq_dimlen(other.ncid_file, otherdims[i], &otherdimlen);
  580. handle_error(status, error_message + other.variable_name);
  581. if (mydimlen != otherdimlen) {
  582. return false;
  583. }
  584. dim_sizes[i] = mydimlen;
  585. }
  586. // Compare data for the coordinate variables
  587. size_t total_size = std::accumulate(dim_sizes.begin(), dim_sizes.end(), 1, std::multiplies<size_t>());
  588. std::vector<double> mydata(total_size), otherdata(total_size);
  589. std::vector<size_t> start(mydims.size(), 0);
  590. const std::string error_message("Failed to get data for coordinate variable associated with variable ");
  591. int status = nc_get_vara_double(ncid_file, ncid_my_coordvar, &start[0], &dim_sizes[0], &mydata[0]);
  592. handle_error(status, error_message + variable_name);
  593. status = nc_get_vara_double(other.ncid_file, ncid_other_coordvar, &start[0], &dim_sizes[0], &otherdata[0]);
  594. handle_error(status, error_message + variable_name);
  595. return mydata == otherdata;
  596. }
  597. bool GridcellOrderedVariable::same_spatial_domain(const GridcellOrderedVariable& other) const {
  598. if (reduced != other.reduced) {
  599. return false;
  600. }
  601. return same_spatial_coordinates(ncid_lat, other, other.ncid_lat) &&
  602. same_spatial_coordinates(ncid_lon, other, other.ncid_lon);
  603. }
  604. #ifdef NC_STRING
  605. void GridcellOrderedVariable::
  606. get_extra_dimension(std::vector<std::string>& coordinate_values) const {
  607. }
  608. #endif
  609. void GridcellOrderedVariable::unpack_data() {
  610. // First, multiply all data by scale_factor (if present)
  611. double factor;
  612. if (get_attribute(ncid_file, ncid_var, "scale_factor", factor)) {
  613. for (size_t i = 0; i < data.size(); ++i) {
  614. data[i] *= factor;
  615. }
  616. }
  617. // Then add add_offset (if present)
  618. double offset;
  619. if (get_attribute(ncid_file, ncid_var, "add_offset", offset)) {
  620. for (size_t i = 0; i < data.size(); ++i) {
  621. data[i] += offset;
  622. }
  623. }
  624. }
  625. } // namespace CF
  626. } // namespace GuessNC
  627. #endif // #ifdef HAVE_NETCDF