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