bes  Updated for version 3.20.13
NCArray.cc
1 // -*- mode: c++; c-basic-offset:4 -*-
2 
3 // This file is part of nc_handler, a data handler for the OPeNDAP data
4 // server.
5 
6 // Copyright (c) 2002,2003 OPeNDAP, Inc.
7 // Author: James Gallagher <jgallagher@opendap.org>
8 //
9 // This is free software; you can redistribute it and/or modify it under the
10 // terms of the GNU Lesser General Public License as published by the Free
11 // Software Foundation; either version 2.1 of the License, or (at your
12 // option) any later version.
13 //
14 // This software is distributed in the hope that it will be useful, but
15 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17 // License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 //
23 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24 
25 // (c) COPYRIGHT URI/MIT 1994-1999
26 // Please read the full copyright statement in the file COPYRIGHT.
27 //
28 // Authors:
29 // reza Reza Nekovei (rnekovei@ieee.org)
30 
31 // netCDF sub-class implementation for NCByte,...NCGrid.
32 // The files are patterned after the subcalssing examples
33 // Test<type>.c,h files.
34 //
35 // ReZa 1/12/95
36 
37 #include "config_nc.h"
38 
39 #include <cstring>
40 #include <iostream>
41 #include <sstream>
42 #include <algorithm>
43 
44 #include <netcdf.h>
45 
46 // #define DODS_DEBUG 1
47 
48 #include <libdap/BaseType.h>
49 #include <libdap/Error.h>
50 #include <libdap/InternalErr.h>
51 #include <libdap/util.h>
52 #include <libdap/debug.h>
53 
54 #include <BESDebug.h>
55 
56 #include "NCRequestHandler.h"
57 #include "NCArray.h"
58 #include "NCStructure.h"
59 // #include "nc_util.h"
60 
61 #define MODULE "nc"
62 #define prolog std::string("NCArray::").append(__func__).append("() - ")
63 
64 BaseType *
65 NCArray::ptr_duplicate()
66 {
67  return new NCArray(*this);
68 }
69 
77 NCArray::NCArray(const string &n, const string &d, BaseType *v)
78  : Array(n, d, v)
79 {}
80 
81 NCArray::NCArray(const NCArray &rhs) : Array(rhs)
82 {}
83 
84 NCArray::~NCArray()
85 {
86 }
87 
88 NCArray &
89 NCArray::operator=(const NCArray &rhs)
90 {
91  if (this == &rhs)
92  return *this;
93 
94  dynamic_cast<Array &>(*this) = rhs;
95 
96  return *this;
97 }
98 
99 
100 // Should this be a private method? jhrg 11/3/04
114 long
115 NCArray::format_constraint(size_t *cor, ptrdiff_t *step, size_t *edg,
116  bool *has_stride)
117 {
118  int start, stride, stop;
119  int id = 0;
120  long nels = 1;
121 
122  *has_stride = false;
123 
124  for (Dim_iter p = dim_begin(); p != dim_end(); ++p) {
125  start = dimension_start(p, true);
126  stride = dimension_stride(p, true);
127  stop = dimension_stop(p, true);
128 #if 0
129  // Check for an empty constraint and use the whole dimension if so.
130  if (start + stop + stride == 0) {
131  start = dimension_start(p, false);
132  stride = dimension_stride(p, false);
133  stop = dimension_stop(p, false);
134  }
135 #endif
136  cor[id] = start;
137  step[id] = stride;
138  edg[id] = ((stop - start) / stride) + 1; // count of elements
139  nels *= edg[id++]; // total number of values for variable
140 
141  if (stride != 1)
142  *has_stride = true;
143  }
144 
145  return nels;
146 }
147 
148 void NCArray::do_cardinal_array_read(int ncid, int varid, nc_type datatype,
149  vector<char> &values, bool has_values, int values_offset,
150  int nels, size_t cor[], size_t edg[], ptrdiff_t step[], bool has_stride)
151 {
152  size_t size;
153  int errstat;
154 #if NETCDF_VERSION >= 4
155  errstat = nc_inq_type(ncid, datatype, 0, &size);
156  if (errstat != NC_NOERR)
157  throw Error(errstat, "Could not get the size for the type.");
158 #else
159  size = nctypelen(datatype);
160 #endif
161 
162  BESDEBUG( MODULE, prolog << "size = " << size << endl);
163  switch (datatype) {
164  case NC_FLOAT:
165  case NC_DOUBLE:
166  case NC_SHORT:
167  case NC_INT:
168 #if NETCDF_VERSION >= 4
169  case NC_USHORT:
170  case NC_UINT:
171  case NC_UBYTE:
172 #endif
173  {
174  if (!has_values) {
175  values.resize(nels * size);
176  if (has_stride)
177  errstat = nc_get_vars(ncid, varid, cor, edg, step, values.data());
178  else
179  errstat = nc_get_vara(ncid, varid, cor, edg, values.data());
180  if (errstat != NC_NOERR){
181  ostringstream oss;
182  oss << prolog << "Could not get the value for Array variable '" << name() << "'.";
183  oss << " dimensions: " << dimensions(true);
184  oss << " nc_get_vara() errstat: " << errstat;
185  throw Error(errstat, oss.str());
186  }
187  // Do not set has_values to true here because the 'true' state
188  // indicates that the values for an entire compound have been
189  // read.
190  }
191 
192  val2buf(values.data() + values_offset);
193  set_read_p(true);
194  break;
195  }
196 
197  case NC_BYTE:{
198  if (!has_values) {
199  values.resize(nels * size);
200  if (has_stride)
201  errstat = nc_get_vars(ncid, varid, cor, edg, step, values.data());
202  else
203  errstat = nc_get_vara(ncid, varid, cor, edg, values.data());
204  if (errstat != NC_NOERR)
205  throw Error(errstat, prolog + "Could not get the value for variable '" + name() + string("' (NCArray::do_cardinal_array_read)"));
206  }
207  if (NCRequestHandler::get_promote_byte_to_short()) {
208  // the data set's signed byte data are going to be stored in a short
209  // not an unsigned byte array. But double check that the template
210  // data type is Int16.
211  if (var()->type() != libdap::dods_int16_c) {
212  throw Error(string("NC.PromoteByteToShort is set but the underlying array type is still a Byte: ") + name() + string("."));
213  }
214  // temporary vector for short (int16) data
215  vector<short int> tmp(nels);
216 
217  // Pointer into the byte data. These values might be part of a compound and
218  // thus might have been read by a previous call (has_values is true in that
219  // case).
220  const char *raw_byte_data = values.data() + values_offset;
221  for (int i = 0; i < nels; ++i)
222  tmp[i] = *raw_byte_data++;
223  val2buf(tmp.data());
224  set_read_p(true);
225  }
226  else {
227  val2buf(values.data() + values_offset);
228  set_read_p(true);
229  }
230  break;
231  }
232 
233  case NC_CHAR: {
234  // Use the dimension info from netcdf since that's the place where
235  // this variable has N-dims. In the DAP representation it's a N-1
236  // dimensional variable.
237  int num_dim; // number of dim. in variable
238  int vdimids[MAX_VAR_DIMS]; // variable dimension ids
239  errstat = nc_inq_var(ncid, varid, (char *)0, (nc_type*)0, &num_dim, vdimids, (int *)0);
240  if (errstat != NC_NOERR)
241  throw Error(errstat, string("Could not read information about the variable `") + name() + string("'."));
242  if (num_dim < 2) // one-dim --> DAP String and we should not be here
243  throw Error(string("A one-dimensional NC_CHAR array should now map to a DAP string: '") + name() + string("'."));
244 
245  size_t vdims[MAX_VAR_DIMS]; // variable dimension sizes
246  for (int i = 0; i < num_dim; ++i)
247  if ((errstat = nc_inq_dimlen(ncid, vdimids[i], &vdims[i])) != NC_NOERR)
248  throw Error(errstat, string("Could not read dimension information about the variable `") + name() + string("'."));
249 
250  int nth_dim_size = vdims[num_dim - 1];
251  cor[num_dim - 1] = 0;
252  edg[num_dim - 1] = nth_dim_size;
253  if (has_stride)
254  step[num_dim - 1] = 1;
255 
256  if (!has_values) {
257  values.resize(nels * nth_dim_size * size);
258  if (has_stride)
259  errstat = nc_get_vars_text(ncid, varid, cor, edg, step, values.data());
260  else
261  errstat = nc_get_vara_text(ncid, varid, cor, edg, values.data());
262  if (errstat != NC_NOERR)
263  throw Error(errstat, string("Could not read the variable '") + name() + string("'."));
264  }
265 
266  // How large is the Nth dimension? Allocate space for the N-1 dims.
267  vector<string> strg(nels);
268  vector<char> buf(nth_dim_size + 1);
269  // put the char values in the string array
270  for (int i = 0; i < nels; i++) {
271  strncpy(buf.data(), values.data() + values_offset + (i * nth_dim_size), nth_dim_size);
272  buf[nth_dim_size] = '\0';
273  strg[i] = buf.data();
274  }
275 
276  set_read_p(true);
277  val2buf(strg.data());
278  break;
279  }
280 #if NETCDF_VERSION >= 4
281  case NC_STRING: {
282  if (!has_values) {
283  values.resize(nels * size);
284  if (has_stride)
285  errstat = nc_get_vars_string(ncid, varid, cor, edg, step, (char**)(values.data() + values_offset));
286  else
287  errstat = nc_get_vara_string(ncid, varid, cor, edg, (char**)(values.data() + values_offset));
288  if (errstat != NC_NOERR)
289  throw Error(errstat, string("Could not read the variable `") + name() + string("'."));
290  }
291 
292  // put the char values in the string array
293  vector < string > strg(nels);
294  for (int i = 0; i < nels; i++) {
295  // values_offset is in bytes; then cast to char** to find the
296  // ith element; then dereference to get the C-style string.
297  strg[i] = *((char**)(values.data() + values_offset) + i);
298  }
299 
300  nc_free_string(nels, (char**)values.data());
301  set_read_p(true);
302  val2buf(strg.data());
303  break;
304  }
305 #endif
306  default:
307  throw InternalErr(__FILE__, __LINE__, string("Unknown data type for the variable '") + name() + string("'."));
308  }
309 }
310 
311 void NCArray::do_array_read(int ncid, int varid, nc_type datatype,
312  vector<char> &values, bool has_values, int values_offset,
313  int nels, size_t cor[], size_t edg[], ptrdiff_t step[], bool has_stride)
314 {
315  int errstat;
316 
317 #if NETCDF_VERSION >= 4
318  if (datatype >= NC_FIRSTUSERTYPEID /*is_user_defined_type(ncid, datatype)*/) {
319  // datatype >= NC_FIRSTUSERTYPEID) {
320  char type_name[NC_MAX_NAME+1];
321  size_t size;
322  nc_type base_type;
323  size_t nfields;
324  int class_type;
325  errstat = nc_inq_user_type(ncid, datatype, type_name, &size, &base_type, &nfields, &class_type);
326  //cerr << "User-defined attribute type size: " << size << ", nfields: " << nfields << endl;
327  if (errstat != NC_NOERR)
328  throw(InternalErr(__FILE__, __LINE__, "Could not get information about a user-defined type (" + long_to_string(errstat) + ")."));
329 
330  switch (class_type) {
331  case NC_COMPOUND: {
332  if (!has_values) {
333  values.resize(size * nels);
334  if (has_stride)
335  errstat = nc_get_vars(ncid, varid, cor, edg, step, values.data());
336  else
337  errstat = nc_get_vara(ncid, varid, cor, edg, values.data());
338  if (errstat != NC_NOERR)
339  throw Error(errstat, string("Could not get the value for variable '") + name() + string("'"));
340  has_values = true;
341  }
342 
343  for (int element = 0; element < nels; ++element) {
344  NCStructure *ncs = dynamic_cast<NCStructure*> (var()->ptr_duplicate());
345  for (int i = 0; i < nfields; ++i) {
346  char field_name[NC_MAX_NAME+1];
347  nc_type field_typeid;
348  size_t field_offset;
349  // These are unused... should they be used?
350  // int field_ndims;
351  // int field_sizes[MAX_NC_DIMS];
352  nc_inq_compound_field(ncid, datatype, i, field_name, &field_offset, &field_typeid, nullptr, nullptr); //&field_ndims, field_sizes.data());
353  BaseType *field = ncs->var(field_name);
354  if (field_typeid >= NC_FIRSTUSERTYPEID /*is_user_defined_type(ncid, field_typeid)*/) {
355  // Interior user defined types have names, but not field_names
356  // so use the type name as the field name (matches the
357  // behavior of the ncdds.cc code).
358  nc_inq_compound_name(ncid, field_typeid, field_name);
359  field = ncs->var(field_name);
360  NCStructure &child_ncs = dynamic_cast<NCStructure&> (*field);
361  child_ncs.do_structure_read(ncid, varid, field_typeid,
362  values, has_values, field_offset + values_offset + size * element);
363  }
364  else if (field->is_vector_type()) {
365  // Because the netcdf api reads data 'atomically' from
366  // compounds, this call works for both cardinal and
367  // array variables.
368  NCArray &child_array = dynamic_cast<NCArray&>(*field);
369  child_array.do_array_read(ncid, varid, field_typeid,
370  values, has_values, field_offset + values_offset + size * element,
371  nels, cor, edg, step, has_stride);
372  }
373  else if (field->is_simple_type()) {
374  field->val2buf(values.data() + (element * size) + field_offset);
375  }
376  else {
377  throw InternalErr(__FILE__, __LINE__, "Expecting a netcdf user defined type or an array or a scalar.");
378  }
379 
380  field->set_read_p(true);
381  }
382  ncs->set_read_p(true);
383  set_vec(element, ncs);
384  }
385 
386  set_read_p(true);
387  break;
388  }
389 
390  case NC_VLEN:
391  if (NCRequestHandler::get_ignore_unknown_types())
392  cerr << "in build_user_defined; found a vlen." << endl;
393  else
394  throw Error("The netCDF handler does not currently support NC_VLEN attributes.");
395  break;
396 
397  case NC_OPAQUE: {
398  // Use the dimension info from netcdf since that's the place where
399  // this variable has N-dims. In the DAP representation it's a N-1
400  // dimensional variable.
401  int num_dim; // number of dim. in variable
402  int vdimids[MAX_VAR_DIMS]; // variable dimension ids
403  errstat = nc_inq_var(ncid, varid, (char *)0, (nc_type*)0, &num_dim, vdimids, (int *)0);
404  if (errstat != NC_NOERR)
405  throw Error(errstat, string("Could not read information about the variable `") + name() + string("'."));
406  if (num_dim < 1) // one-dim --> DAP String and we should not be here
407  throw Error(string("A one-dimensional NC_OPAQUE array should now map to a DAP Byte: '") + name() + string("'."));
408 
409  size_t vdims[MAX_VAR_DIMS]; // variable dimension sizes
410  for (int i = 0; i < num_dim; ++i)
411  if ((errstat = nc_inq_dimlen(ncid, vdimids[i], &vdims[i])) != NC_NOERR)
412  throw Error(errstat, string("Could not read dimension information about the variable `") + name() + string("'."));
413 
414  int nth_dim_size = vdims[num_dim - 1];
415  cor[num_dim - 1] = 0;
416  edg[num_dim - 1] = nth_dim_size;
417  if (has_stride)
418  step[num_dim - 1] = 1;
419 
420  if (!has_values) {
421  values.resize(size * nels);
422  if (has_stride)
423  errstat = nc_get_vars(ncid, varid, cor, edg, step, values.data());
424  else
425  errstat = nc_get_vara(ncid, varid, cor, edg, values.data());
426  if (errstat != NC_NOERR)
427  throw Error(errstat, string("Could not get the value for variable '") + name() + string("' (NC_OPAQUE)"));
428  has_values = true; // This value may never be used. jhrg 1/9/12
429  }
430 
431  val2buf(values.data() + values_offset);
432 
433  set_read_p(true);
434  break;
435  }
436 
437  case NC_ENUM: {
438  nc_type base_nc_type;
439  errstat = nc_inq_enum(ncid, datatype, nullptr /*name.data()*/, &base_nc_type, nullptr/*&base_size*/, nullptr/*&num_members*/);
440  if (errstat != NC_NOERR)
441  throw(InternalErr(__FILE__, __LINE__, "Could not get information about an enum(" + long_to_string(errstat) + ")."));
442 
443  do_cardinal_array_read(ncid, varid, base_nc_type,
444  values, has_values, values_offset,
445  nels, cor, edg, step, has_stride);
446 
447  set_read_p(true);
448  break;
449  }
450 
451  default:
452  throw InternalErr(__FILE__, __LINE__, "Expected one of NC_COMPOUND, NC_VLEN, NC_OPAQUE or NC_ENUM");
453  }
454 
455  }
456  else {
457  do_cardinal_array_read(ncid, varid, datatype, values, has_values, values_offset,
458  nels, cor, edg, step, has_stride);
459  }
460 #else
461  do_cardinal_array_read(ncid, varid, datatype, values, has_values, values_offset,
462  nels, cor, edg, step, has_stride);
463 #endif
464 }
465 
466 bool NCArray::read()
467 {
468  if (read_p()) // Nothing to do
469  return true;
470 
471  int ncid;
472  int errstat = nc_open(dataset().c_str(), NC_NOWRITE, &ncid); /* netCDF id */
473  if (errstat != NC_NOERR)
474  throw Error(errstat, string("Could not open the dataset's file (") + dataset().c_str() + string(")"));
475 
476  int varid; /* variable Id */
477  errstat = nc_inq_varid(ncid, name().c_str(), &varid);
478  if (errstat != NC_NOERR)
479  throw InternalErr(__FILE__, __LINE__, "Could not get variable ID for: " + name() + ". (error: " + long_to_string(errstat) + ").");
480 
481  nc_type datatype; // variable data type
482  errstat = nc_inq_vartype(ncid, varid, &datatype);
483  if (errstat != NC_NOERR)
484  throw Error(errstat, string("Could not read information about the variable `") + name() + string("'."));
485 
486  size_t cor[MAX_NC_DIMS]; /* corner coordinates */
487  size_t edg[MAX_NC_DIMS]; /* edges of hyper-cube */
488  ptrdiff_t step[MAX_NC_DIMS]; /* stride of hyper-cube */
489  bool has_stride;
490  for(unsigned int i=0; i<MAX_NC_DIMS; i++){
491  cor[i] = edg[i] = step[i] = 0;
492  }
493  long nels = format_constraint(cor, step, edg, &has_stride);
494 // ostringstream oss;
495 // for(unsigned int i=0; i<MAX_NC_DIMS; i++){
496 // oss << cor[i] << ", " << edg[i] << ", " << step[i] << endl;
497 // }
498 // BESDEBUG( MODULE, prolog << "corners, edges, stride" << endl << oss.str() << endl);
499 
500  vector<char> values;
501  do_array_read(ncid, varid, datatype, values, false /*has_values*/, 0 /*values_offset*/,
502  nels, cor, edg, step, has_stride);
503  set_read_p(true);
504 
505  if (nc_close(ncid) != NC_NOERR)
506  throw InternalErr(__FILE__, __LINE__, "Could not close the dataset!");
507 
508  return true;
509 }
NCArray(const string &n, const string &d, BaseType *v)
Definition: NCArray.cc:77