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