OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
NCArray.cc
Go to the documentation of this file.
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 <BaseType.h>
50 #include <Error.h>
51 #include <InternalErr.h>
52 #include <util.h>
53 #include <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 BaseType *
64 {
65  return new NCArray(*this);
66 }
67 
75 NCArray::NCArray(const string &n, const string &d, BaseType *v)
76  : Array(n, d, v)
77 {}
78 
79 NCArray::NCArray(const NCArray &rhs) : Array(rhs)
80 {}
81 
83 {
84 }
85 
86 NCArray &
88 {
89  if (this == &rhs)
90  return *this;
91 
92  dynamic_cast<Array &>(*this) = rhs;
93 
94  return *this;
95 }
96 
97 
98 // Should this be a private method? jhrg 11/3/04
112 long
113 NCArray::format_constraint(size_t *cor, ptrdiff_t *step, size_t *edg,
114  bool *has_stride)
115 {
116  int start, stride, stop;
117  int id = 0;
118  long nels = 1;
119 
120  *has_stride = false;
121 
122  for (Dim_iter p = dim_begin(); p != dim_end(); ++p) {
123  start = dimension_start(p, true);
124  stride = dimension_stride(p, true);
125  stop = dimension_stop(p, true);
126  // Check for an empty constraint and use the whole dimension if so.
127  if (start + stop + stride == 0) {
128  start = dimension_start(p, false);
129  stride = dimension_stride(p, false);
130  stop = dimension_stop(p, false);
131  }
132 
133  cor[id] = start;
134  step[id] = stride;
135  edg[id] = ((stop - start) / stride) + 1; // count of elements
136  nels *= edg[id++]; // total number of values for variable
137 
138  if (stride != 1)
139  *has_stride = true;
140  }
141 
142  return nels;
143 }
144 
145 void NCArray::do_cardinal_array_read(int ncid, int varid, nc_type datatype,
146  vector<char> &values, bool has_values, int values_offset,
147  int nels, size_t cor[], size_t edg[], ptrdiff_t step[], bool has_stride)
148 {
149  size_t size;
150  int errstat;
151 #if NETCDF_VERSION >= 4
152  errstat = nc_inq_type(ncid, datatype, 0, &size);
153  if (errstat != NC_NOERR)
154  throw Error(errstat, "Could not get the size for the type.");
155 #else
156  size = nctypelen(datatype);
157 #endif
158 
159  BESDEBUG("nc", "In NCArray::do_cardinal_array_read, size = " << size << endl);
160  switch (datatype) {
161  case NC_FLOAT:
162  case NC_DOUBLE:
163  case NC_SHORT:
164  case NC_INT:
165 #if NETCDF_VERSION >= 4
166  case NC_USHORT:
167  case NC_UINT:
168  case NC_UBYTE:
169 #endif
170  {
171  if (!has_values) {
172  values.resize(nels * size);
173  if (has_stride)
174  errstat = nc_get_vars(ncid, varid, cor, edg, step, &values[0]);
175  else
176  errstat = nc_get_vara(ncid, varid, cor, edg, &values[0]);
177  if (errstat != NC_NOERR)
178  throw Error(errstat, string("Could not get the value for variable '") + name() + string("' (NCArray::do_cardinal_array_read)"));
179  // Do not set has_values to true here because the 'true' state
180  // indicates that the values for an entire compound have been
181  // read.
182  }
183 
184  val2buf(&values[0] + values_offset);
185  set_read_p(true);
186  break;
187  }
188 
189  case NC_BYTE:{
190  if (!has_values) {
191  values.resize(nels * size);
192  if (has_stride)
193  errstat = nc_get_vars(ncid, varid, cor, edg, step, &values[0]);
194  else
195  errstat = nc_get_vara(ncid, varid, cor, edg, &values[0]);
196  if (errstat != NC_NOERR)
197  throw Error(errstat, string("Could not get the value for variable '") + name() + string("' (NCArray::do_cardinal_array_read)"));
198  }
200  // the data set's signed byte data are going to be stored in a short
201  // not an unsigned byte array. But double check that the template
202  // data type is Int16.
203  if (var()->type() != libdap::dods_int16_c) {
204  throw Error(string("NC.PromoteByteToShort is set but the underlying array type is still a Byte: ") + name() + string("."));
205  }
206  // temporary vector for short (int16) data
207  vector<short int> tmp(nels);
208 
209  // Pointer into the byte data. These values might be part of a compound and
210  // thus might have been read by a previous call (has_values is true in that
211  // case).
212  char *raw_byte_data = &values[0] + values_offset;
213  for (int i = 0; i < nels; ++i)
214  tmp[i] = *raw_byte_data++;
215  val2buf(&tmp[0]);
216  set_read_p(true);
217  }
218  else {
219  val2buf(&values[0] + values_offset);
220  set_read_p(true);
221  }
222  break;
223  }
224 
225  case NC_CHAR: {
226  // Use the dimension info from netcdf since that's the place where
227  // this variable has N-dims. In the DAP representation it's a N-1
228  // dimensional variable.
229  int num_dim; // number of dim. in variable
230  int vdimids[MAX_VAR_DIMS]; // variable dimension ids
231  errstat = nc_inq_var(ncid, varid, (char *)0, (nc_type*)0, &num_dim, vdimids, (int *)0);
232  if (errstat != NC_NOERR)
233  throw Error(errstat, string("Could not read information about the variable `") + name() + string("'."));
234  if (num_dim < 2) // one-dim --> DAP String and we should not be here
235  throw Error(string("A one-dimensional NC_CHAR array should now map to a DAP string: '") + name() + string("'."));
236 
237  size_t vdims[MAX_VAR_DIMS]; // variable dimension sizes
238  for (int i = 0; i < num_dim; ++i)
239  if ((errstat = nc_inq_dimlen(ncid, vdimids[i], &vdims[i])) != NC_NOERR)
240  throw Error(errstat, string("Could not read dimension information about the variable `") + name() + string("'."));
241 
242  int nth_dim_size = vdims[num_dim - 1];
243  cor[num_dim - 1] = 0;
244  edg[num_dim - 1] = nth_dim_size;
245  if (has_stride)
246  step[num_dim - 1] = 1;
247 
248  if (!has_values) {
249  values.resize(nels * nth_dim_size * size);
250  if (has_stride)
251  errstat = nc_get_vars_text(ncid, varid, cor, edg, step, &values[0]);
252  else
253  errstat = nc_get_vara_text(ncid, varid, cor, edg, &values[0]);
254  if (errstat != NC_NOERR)
255  throw Error(errstat, string("Could not read the variable '") + name() + string("'."));
256  }
257 
258  // How large is the Nth dimension? Allocate space for the N-1 dims.
259  vector<string> strg(nels);
260  vector<char> buf(nth_dim_size + 1);
261  // put the char values in the string array
262  for (int i = 0; i < nels; i++) {
263  strncpy(&buf[0], &values[0] + values_offset + (i * nth_dim_size), nth_dim_size);
264  buf[nth_dim_size] = '\0';
265  strg[i] = &buf[0];
266  }
267 
268  set_read_p(true);
269  val2buf(&strg[0]);
270  break;
271  }
272 #if NETCDF_VERSION >= 4
273  case NC_STRING: {
274  if (!has_values) {
275  values.resize(nels * size);
276  if (has_stride)
277  errstat = nc_get_vars_string(ncid, varid, cor, edg, step, (char**)(&values[0] + values_offset));
278  else
279  errstat = nc_get_vara_string(ncid, varid, cor, edg, (char**)(&values[0] + values_offset));
280  if (errstat != NC_NOERR)
281  throw Error(errstat, string("Could not read the variable `") + name() + string("'."));
282  }
283 
284  // put the char values in the string array
285  vector < string > strg(nels);
286  for (int i = 0; i < nels; i++) {
287  // values_offset is in bytes; then cast to char** to find the
288  // ith element; then dereference to get the C-style string.
289  strg[i] = *((char**)(&values[0] + values_offset) + i);
290  }
291 
292  nc_free_string(nels, (char**)&values[0]);
293  set_read_p(true);
294  val2buf(&strg[0]);
295  break;
296  }
297 #endif
298  default:
299  throw InternalErr(__FILE__, __LINE__, string("Unknown data type for the variable '") + name() + string("'."));
300  }
301 }
302 
303 void NCArray::do_array_read(int ncid, int varid, nc_type datatype,
304  vector<char> &values, bool has_values, int values_offset,
305  int nels, size_t cor[], size_t edg[], ptrdiff_t step[], bool has_stride)
306 {
307  int errstat;
308 
309 #if NETCDF_VERSION >= 4
310  if (is_user_defined_type(ncid, datatype)) {
311  // datatype >= NC_FIRSTUSERTYPEID) {
312  char type_name[NC_MAX_NAME+1];
313  size_t size;
314  nc_type base_type;
315  size_t nfields;
316  int class_type;
317  errstat = nc_inq_user_type(ncid, datatype, type_name, &size, &base_type, &nfields, &class_type);
318  //cerr << "User-defined attribute type size: " << size << ", nfields: " << nfields << endl;
319  if (errstat != NC_NOERR)
320  throw(InternalErr(__FILE__, __LINE__, "Could not get information about a user-defined type (" + long_to_string(errstat) + ")."));
321 
322  switch (class_type) {
323  case NC_COMPOUND: {
324  if (!has_values) {
325  values.resize(size * nels);
326  if (has_stride)
327  errstat = nc_get_vars(ncid, varid, cor, edg, step, &values[0]);
328  else
329  errstat = nc_get_vara(ncid, varid, cor, edg, &values[0]);
330  if (errstat != NC_NOERR)
331  throw Error(errstat, string("Could not get the value for variable '") + name() + string("'"));
332  has_values = true;
333  }
334 
335  for (int element = 0; element < nels; ++element) {
336  NCStructure *ncs = dynamic_cast<NCStructure*> (var()->ptr_duplicate());
337  for (size_t i = 0; i < nfields; ++i) {
338  char field_name[NC_MAX_NAME+1];
339  nc_type field_typeid;
340  size_t field_offset;
341  // These are unused... should they be used?
342  // int field_ndims;
343  // int field_sizes[MAX_NC_DIMS];
344  nc_inq_compound_field(ncid, datatype, i, field_name, &field_offset, &field_typeid, 0, 0); //&field_ndims, &field_sizes[0]);
345  BaseType *field = ncs->var(field_name);
346  if (is_user_defined_type(ncid, field_typeid)) {
347  // field_typeid >= NC_FIRSTUSERTYPEID) {
348  // Interior user defined types have names, but not field_names
349  // so use the type name as the field name (matches the
350  // behavior of the ncdds.cc code).
351  nc_inq_compound_name(ncid, field_typeid, field_name);
352  field = ncs->var(field_name);
353  NCStructure &child_ncs = dynamic_cast<NCStructure&> (*field);
354  child_ncs.do_structure_read(ncid, varid, field_typeid,
355  values, has_values, field_offset + values_offset + size * element);
356  }
357  else if (field->is_vector_type()) {
358  // Because the netcdf api reads data 'atomically' from
359  // compounds, this call works for both cardinal and
360  // array variables.
361  NCArray &child_array = dynamic_cast<NCArray&>(*field);
362  child_array.do_array_read(ncid, varid, field_typeid,
363  values, has_values, field_offset + values_offset + size * element,
364  nels, cor, edg, step, has_stride);
365  }
366  else if (field->is_simple_type()) {
367  field->val2buf(&values[0] + (element * size) + field_offset);
368  }
369  else {
370  throw InternalErr(__FILE__, __LINE__, "Expecting a netcdf user defined type or an array or a scalar.");
371  }
372 
373  field->set_read_p(true);
374  }
375  ncs->set_read_p(true);
376  set_vec(element, ncs);
377  }
378 
379  set_read_p(true);
380  break;
381  }
382 
383  case NC_VLEN:
385  cerr << "in build_user_defined; found a vlen." << endl;
386  else
387  throw Error("The netCDF handler does not currently support NC_VLEN attributes.");
388  break;
389 
390  case NC_OPAQUE: {
391  // Use the dimension info from netcdf since that's the place where
392  // this variable has N-dims. In the DAP representation it's a N-1
393  // dimensional variable.
394  int num_dim; // number of dim. in variable
395  int vdimids[MAX_VAR_DIMS]; // variable dimension ids
396  errstat = nc_inq_var(ncid, varid, (char *)0, (nc_type*)0, &num_dim, vdimids, (int *)0);
397  if (errstat != NC_NOERR)
398  throw Error(errstat, string("Could not read information about the variable `") + name() + string("'."));
399  if (num_dim < 1) // one-dim --> DAP String and we should not be here
400  throw Error(string("A one-dimensional NC_OPAQUE array should now map to a DAP Byte: '") + name() + string("'."));
401 
402  size_t vdims[MAX_VAR_DIMS]; // variable dimension sizes
403  for (int i = 0; i < num_dim; ++i)
404  if ((errstat = nc_inq_dimlen(ncid, vdimids[i], &vdims[i])) != NC_NOERR)
405  throw Error(errstat, string("Could not read dimension information about the variable `") + name() + string("'."));
406 
407  int nth_dim_size = vdims[num_dim - 1];
408  cor[num_dim - 1] = 0;
409  edg[num_dim - 1] = nth_dim_size;
410  if (has_stride)
411  step[num_dim - 1] = 1;
412 
413  if (!has_values) {
414  values.resize(size * nels);
415  if (has_stride)
416  errstat = nc_get_vars(ncid, varid, cor, edg, step, &values[0]);
417  else
418  errstat = nc_get_vara(ncid, varid, cor, edg, &values[0]);
419  if (errstat != NC_NOERR)
420  throw Error(errstat, string("Could not get the value for variable '") + name() + string("' (NC_OPAQUE)"));
421  has_values = true; // This value may never be used. jhrg 1/9/12
422  }
423 
424  val2buf(&values[0] + values_offset);
425 
426  set_read_p(true);
427  break;
428  }
429 
430  case NC_ENUM: {
431  nc_type base_nc_type;
432  errstat = nc_inq_enum(ncid, datatype, 0 /*&name[0]*/, &base_nc_type, 0/*&base_size*/, 0/*&num_members*/);
433  if (errstat != NC_NOERR)
434  throw(InternalErr(__FILE__, __LINE__, "Could not get information about an enum(" + long_to_string(errstat) + ")."));
435 
436  do_cardinal_array_read(ncid, varid, base_nc_type,
437  values, has_values, values_offset,
438  nels, cor, edg, step, has_stride);
439 
440  set_read_p(true);
441  break;
442  }
443 
444  default:
445  throw InternalErr(__FILE__, __LINE__, "Expected one of NC_COMPOUND, NC_VLEN, NC_OPAQUE or NC_ENUM");
446  }
447 
448  }
449  else {
450  do_cardinal_array_read(ncid, varid, datatype, values, has_values, values_offset,
451  nels, cor, edg, step, has_stride);
452  }
453 #else
454  do_cardinal_array_read(ncid, varid, datatype, values, has_values, values_offset,
455  nels, cor, edg, step, has_stride);
456 #endif
457 }
458 
460 {
461  if (read_p()) // Nothing to do
462  return true;
463 
464  int ncid;
465  int errstat = nc_open(dataset().c_str(), NC_NOWRITE, &ncid); /* netCDF id */
466  if (errstat != NC_NOERR)
467  throw Error(errstat, string("Could not open the dataset's file (") + dataset().c_str() + string(")"));
468 
469  int varid; /* variable Id */
470  errstat = nc_inq_varid(ncid, name().c_str(), &varid);
471  if (errstat != NC_NOERR)
472  throw InternalErr(__FILE__, __LINE__, "Could not get variable ID for: " + name() + ". (error: " + long_to_string(errstat) + ").");
473 
474  nc_type datatype; // variable data type
475  errstat = nc_inq_vartype(ncid, varid, &datatype);
476  if (errstat != NC_NOERR)
477  throw Error(errstat, string("Could not read information about the variable `") + name() + string("'."));
478 
479  size_t cor[MAX_NC_DIMS]; /* corner coordinates */
480  size_t edg[MAX_NC_DIMS]; /* edges of hyper-cube */
481  ptrdiff_t step[MAX_NC_DIMS]; /* stride of hyper-cube */
482  bool has_stride;
483  long nels = format_constraint(cor, step, edg, &has_stride);
484 
485  vector<char> values;
486  do_array_read(ncid, varid, datatype, values, false /*has_values*/, 0 /*values_offset*/,
487  nels, cor, edg, step, has_stride);
488  set_read_p(true);
489 
490  if (nc_close(ncid) != NC_NOERR)
491  throw InternalErr(__FILE__, __LINE__, "Could not close the dataset!");
492 
493  return true;
494 }
virtual bool read()
Definition: NCArray.cc:459
virtual BaseType * ptr_duplicate()
Definition: NCArray.cc:63
NCArray(const string &n, const string &d, BaseType *v)
Build an NCArray instance.
Definition: NCArray.cc:75
virtual ~NCArray()
Definition: NCArray.cc:82
static bool get_ignore_unknown_types()
static bool get_promote_byte_to_short()
NCArray & operator=(const NCArray &rhs)
Definition: NCArray.cc:87
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
bool is_user_defined_type(int ncid, int type)
Definition: nc_util.cc:33