OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
ncdas.cc
Go to the documentation of this file.
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-1996
26 // Please read the full copyright statement in the file COPYRIGHT.
27 //
28 // Authors:
29 // reza Reza Nekovei (reza@intcomm.net)
30 
31 // This file contains functions which read the variables and their attributes
32 // from a netcdf file and build the in-memeory DAS. These functions form the
33 // core of the server-side software necessary to extract the DAS from a
34 // netcdf data file.
35 //
36 // It also contains test code which will print the in-memory DAS to
37 // stdout. It uses both the DAS class as well as the netcdf library.
38 // In addition, parts of these functions were taken from the netcdf program
39 // ncdump, from the netcdf standard distribution (ver 2.3.2)
40 //
41 // jhrg 9/23/94
42 
43 #include "config_nc.h"
44 
45 #include <iostream>
46 #include <string>
47 #include <sstream>
48 #include <iomanip>
49 #include <vector>
50 
51 #include <netcdf.h>
52 
53 #include <util.h>
54 #include <escaping.h>
55 #include <DAS.h>
56 
57 #include <BESDebug.h>
58 
59 #include "NCRequestHandler.h"
60 #include "nc_util.h"
61 
62 #define ATTR_STRING_QUOTE_FIX
63 
64 #if NETCDF_VERSION >= 4
65 #define READ_ATTRIBUTES_MACRO read_attributes_netcdf4
66 #else
67 #define READ_ATTRIBUTES_MACRO read_attributes_netcdf3
68 #endif
69 
70 using namespace libdap;
71 
82 static string print_attr(nc_type type, int loc, void *vals)
83 {
84  ostringstream rep;
85  union {
86  char *cp;
87  char **stringp;
88  short *sp;
89  unsigned short *usp;
90  int *i;
91  unsigned int *ui;
92  float *fp;
93  double *dp;
94  } gp;
95 
96  switch (type) {
97 #if NETCDF_VERSION >= 4
98  case NC_UBYTE:
99  unsigned char uc;
100  gp.cp = (char *) vals;
101 
102  uc = *(gp.cp + loc);
103  rep << (int) uc;
104  return rep.str();
105 #endif
106 
107  case NC_BYTE:
109  signed char sc;
110  gp.cp = (char *) vals;
111 
112  sc = *(gp.cp + loc);
113  rep << (int) sc;
114  return rep.str();
115  }
116  else {
117  unsigned char uc;
118  gp.cp = (char *) vals;
119 
120  uc = *(gp.cp + loc);
121  rep << (int) uc;
122  return rep.str();
123  }
124 
125  case NC_CHAR:
126 #ifndef ATTR_STRING_QUOTE_FIX
127  rep << "\"" << escattr(static_cast<const char*>(vals)) << "\"";
128  return rep.str();
129 #else
130  return escattr(static_cast<const char*> (vals));
131 #endif
132 
133 #if NETCDF_VERSION >= 4
134  case NC_STRING:
135  gp.stringp = (char **) vals;
136  rep << *(gp.stringp + loc);
137  return rep.str();
138 #endif
139 
140  case NC_SHORT:
141  gp.sp = (short *) vals;
142  rep << *(gp.sp + loc);
143  return rep.str();
144 
145 #if NETCDF_VERSION >= 4
146  case NC_USHORT:
147  gp.usp = (unsigned short *) vals;
148  rep << *(gp.usp + loc);
149  return rep.str();
150 #endif
151 
152  case NC_INT:
153  gp.i = (int *) vals; // warning: long int format, int arg (arg 3)
154  rep << *(gp.i + loc);
155  return rep.str();
156 
157 #if NETCDF_VERSION >= 4
158  case NC_UINT:
159  gp.ui = (unsigned int *) vals;
160  rep << *(gp.ui + loc);
161  return rep.str();
162 #endif
163 
164  case NC_FLOAT: {
165  gp.fp = (float *) vals;
166 
167  rep << std::showpoint;
168  rep << std::setprecision(9);
169 
170  rep << *(gp.fp + loc);
171  // If there's no decimal point and the rep does not use scientific
172  // notation, add a decimal point. This little jaunt was taken because
173  // this code is modeled after older code and that's what it did. I'm
174  // trying to keep the same behavior as the old code without it's
175  // problems. jhrg 8/11/2006
176  string tmp_value = rep.str();
177  if (tmp_value.find('.') == string::npos
178  && tmp_value.find('e') == string::npos
179  && tmp_value.find('E') == string::npos
180  && tmp_value.find("nan") == string::npos
181  && tmp_value.find("NaN") == string::npos
182  && tmp_value.find("NAN") == string::npos)
183  rep << ".";
184  return rep.str();
185  }
186 
187  case NC_DOUBLE: {
188  gp.dp = (double *) vals;
189  rep << std::showpoint;
190  rep << std::setprecision(16);
191  rep << *(gp.dp + loc);
192  string tmp_value = rep.str();
193  if (tmp_value.find('.') == string::npos
194  && tmp_value.find('e') == string::npos
195  && tmp_value.find('E') == string::npos
196  && tmp_value.find("nan") == string::npos
197  && tmp_value.find("NaN") == string::npos
198  && tmp_value.find("NAN") == string::npos)
199  rep << ".";
200  return rep.str();
201  }
202 
203  default:
205  cerr << "The netcdf handler tried to print an attribute that has an unrecognized type. (1)" << endl;
206  else
207  throw InternalErr(__FILE__, __LINE__, "The netcdf handler tried to print an attribute that has an unrecognized type. (1)");
208  break;
209  }
210 
211  return "";
212 }
213 
220 static string print_type(nc_type datatype)
221 {
222  switch (datatype) {
223 #if NETCDF_VERSION >= 4
224  case NC_STRING:
225 #endif
226  case NC_CHAR:
227  return "String";
228 
229 #if NETCDF_VERSION >= 4
230  case NC_UBYTE:
231  return "Byte";
232 #endif
233  case NC_BYTE:
235  return "Int16";
236  }
237  else {
238  return "Byte";
239  }
240 
241 
242  case NC_SHORT:
243  return "Int16";
244 
245  case NC_INT:
246  return "Int32";
247 
248 #if NETCDF_VERSION >= 4
249  case NC_USHORT:
250  return "UInt16";
251 
252  case NC_UINT:
253  return "UInt32";
254 #endif
255 
256  case NC_FLOAT:
257  return "Float32";
258 
259  case NC_DOUBLE:
260  return "Float64";
261 
262 #if NETCDF_VERSION >= 4
263  case NC_COMPOUND:
264  return "NC_COMPOUND";
265 #endif
266 
267 #if NETCDF_VERSION >= 4
268  // These are all new netcdf 4 types that we don't support yet
269  // as attributes. It's useful to have a print representation for
270  // them so that we can return useful information about why some
271  // information was elided or an exception thrown.
272  case NC_INT64:
273  return "NC_INT64";
274 
275  case NC_UINT64:
276  return "NC_UINT64";
277 
278  case NC_VLEN:
279  return "NC_VLEN";
280  case NC_OPAQUE:
281  return "NC_OPAQUE";
282  case NC_ENUM:
283  return "NC_ENUM";
284 #endif
285  default:
287  cerr << "The netcdf handler tried to print an attribute that has an unrecognized type. (2)" << endl;
288  else
289  throw InternalErr(__FILE__, __LINE__, "The netcdf handler tried to print an attribute that has an unrecognized type. (2)");
290  break;
291  }
292 
293  return "";
294 }
295 
300 static void append_values(int ncid, int v, int len, nc_type datatype, char *attrname, AttrTable *at)
301 {
302  size_t size;
303  int errstat;
304 #if NETCDF_VERSION >= 4
305  errstat = nc_inq_type(ncid, datatype, 0, &size);
306  if (errstat != NC_NOERR)
307  throw Error(errstat, "Could not get the size for the type.");
308 #else
309  size = nctypelen(datatype);
310 #endif
311 
312  vector<char> value((len + 1) * size);
313  errstat = nc_get_att(ncid, v, attrname, &value[0]);
314  if (errstat != NC_NOERR) {
315  throw Error(errstat, string("Could not get the value for attribute '") + attrname + string("'"));
316  }
317 
318  // If the datatype is NC_CHAR then we have a string. netCDF 3
319  // represents strings as arrays of char, but we represent them as X
320  // strings. So... Add the null and set the length to 1
321  if (datatype == NC_CHAR) {
322  value[len] = '\0';
323  len = 1;
324  }
325 
326  // add all the attributes in the array
327  for (int loc = 0; loc < len; loc++) {
328  string print_rep = print_attr(datatype, loc, &value[0]);
329  at->append_attr(attrname, print_type(datatype), print_rep);
330  }
331 }
332 
344 static void read_attributes_netcdf3(int ncid, int v, int natts, AttrTable *at)
345 {
346  char attrname[MAX_NC_NAME];
347  nc_type datatype;
348  size_t len;
349  int errstat = NC_NOERR;
350 
351  for (int a = 0; a < natts; ++a) {
352  errstat = nc_inq_attname(ncid, v, a, attrname);
353  if (errstat != NC_NOERR) {
354  string msg = "Could not get the name for attribute ";
355  msg += long_to_string(a);
356  throw Error(errstat, msg);
357  }
358 
359  // len is the number of values. Attributes in netcdf can be scalars or
360  // vectors
361  errstat = nc_inq_att(ncid, v, attrname, &datatype, &len);
362  if (errstat != NC_NOERR) {
363  string msg = "Could not get the name for attribute '";
364  msg += attrname + string("'");
365  throw Error(errstat, msg);
366  }
367 
368  switch (datatype) {
369  case NC_BYTE:
370  case NC_CHAR:
371  case NC_SHORT:
372  case NC_INT:
373  case NC_FLOAT:
374  case NC_DOUBLE:
375  append_values(ncid, v, len, datatype, attrname, at);
376  break;
377 
378  default:
380  cerr << "Unrecognized attribute type." << endl;
381  else
382  throw InternalErr(__FILE__, __LINE__, "Unrecognized attribute type.");
383  break;
384  }
385  }
386 }
387 
388 #if NETCDF_VERSION >= 4
389 
401 static void read_attributes_netcdf4(int ncid, int varid, int natts, AttrTable *at)
402 {
403  BESDEBUG("nc", "In read_attributes_netcdf4" << endl);
404 
405  for (int attr_num = 0; attr_num < natts; ++attr_num) {
406  int errstat = NC_NOERR;
407  // Get the attribute name
408  char attrname[MAX_NC_NAME];
409  errstat = nc_inq_attname(ncid, varid, attr_num, attrname);
410  if (errstat != NC_NOERR)
411  throw Error(errstat, "Could not get the name for attribute " + long_to_string(attr_num));
412 
413  // Get datatype and len; len is the number of values.
414  nc_type datatype;
415  size_t len;
416  errstat = nc_inq_att(ncid, varid, attrname, &datatype, &len);
417  if (errstat != NC_NOERR)
418  throw Error(errstat, "Could not get the name for attribute '" + string(attrname) + "'");
419 
420  BESDEBUG("nc", "nc_inq_att returned datatype = " << datatype << endl);
421 
422  if (is_user_defined_type(ncid, datatype)) {
423  // datatype >= NC_FIRSTUSERTYPEID) {
424  char type_name[NC_MAX_NAME+1];
425  size_t size;
426  nc_type base_type;
427  size_t nfields;
428  int class_type;
429  errstat = nc_inq_user_type(ncid, datatype, type_name, &size, &base_type, &nfields, &class_type);
430  if (errstat != NC_NOERR)
431  throw(InternalErr(__FILE__, __LINE__, "Could not get information about a user-defined type (" + long_to_string(errstat) + ")."));
432 
433  BESDEBUG("nc", "Before switch(class_type)" << endl);
434  switch (class_type) {
435  case NC_COMPOUND: {
436  // Make recursive attrs work?
437  vector<unsigned char> values((len + 1) * size);
438 
439  int errstat = nc_get_att(ncid, varid, attrname, &values[0]);
440  if (errstat != NC_NOERR)
441  throw Error(errstat, string("Could not get the value for attribute '") + attrname + string("'"));
442 
443  for (size_t i = 0; i < nfields; ++i) {
444  char field_name[NC_MAX_NAME+1];
445  nc_type field_typeid;
446  size_t field_offset;
447  nc_inq_compound_field(ncid, datatype, i, field_name, &field_offset, &field_typeid, 0, 0);
448 
449  at->append_attr(field_name, print_type(field_typeid), print_attr(field_typeid, 0, &values[0] + field_offset));
450  }
451  break;
452  }
453 
454  case NC_VLEN:
456  cerr << "in build_user_defined; found a vlen." << endl;
457  else
458  throw Error("The netCDF handler does not yet support the NC_VLEN type.");
459  break;
460 
461  case NC_OPAQUE: {
462  vector<unsigned char> values((len + 1) * size);
463 
464  int errstat = nc_get_att(ncid, varid, attrname, &values[0]);
465  if (errstat != NC_NOERR)
466  throw Error(errstat, string("Could not get the value for attribute '") + attrname + string("'"));
467 
468  for (size_t i = 0; i < size; ++i)
469  at->append_attr(attrname, print_type(NC_BYTE), print_attr(NC_BYTE, i, &values[0]));
470 
471  break;
472  }
473 
474  case NC_ENUM:{
475  vector<unsigned char> values((len + 1) * size);
476 
477  int errstat = nc_get_att(ncid, varid, attrname, &values[0]);
478  if (errstat != NC_NOERR)
479  throw Error(errstat, string("Could not get the value for attribute '") + attrname + string("'"));
480 
481  nc_type basetype;
482  errstat = nc_inq_enum(ncid, datatype, 0/*char *name*/, &basetype,
483  0/*size_t *base_sizep*/, 0/*size_t *num_membersp*/);
484  if (errstat != NC_NOERR)
485  throw Error(errstat, string("Could not get the size of the enum base type for '") + attrname + string("'"));
486 
487  for (size_t i = 0; i < size; ++i)
488  at->append_attr(attrname, print_type(basetype), print_attr(basetype, i, &values[0]));
489 
490  break;
491  }
492 
493  default:
494  throw InternalErr(__FILE__, __LINE__, "Expected one of NC_COMPOUND, NC_VLEN, NC_OPAQUE or NC_ENUM");
495  }
496 
497  BESDEBUG("nc", "After switch(class-type)" << endl);
498  }
499  else {
500  switch (datatype) {
501  case NC_STRING:
502  case NC_BYTE:
503  case NC_CHAR:
504  case NC_SHORT:
505  case NC_INT:
506  case NC_FLOAT:
507  case NC_DOUBLE:
508  case NC_UBYTE:
509  case NC_USHORT:
510  case NC_UINT:
511  BESDEBUG("nc", "Before append_values ..." << endl);
512  append_values(ncid, varid, len, datatype, attrname, at);
513  BESDEBUG("nc", "After append_values ..." << endl);
514  break;
515 
516  case NC_INT64:
517  case NC_UINT64: {
518  string note = "Attribute edlided: Unsupported attribute type ";
519  note += "(" + print_type(datatype) + ")";
520  at->append_attr(attrname, "String", note);
521  break;
522  }
523 
524  case NC_COMPOUND:
525  case NC_VLEN:
526  case NC_OPAQUE:
527  case NC_ENUM:
528  throw InternalErr(__FILE__, __LINE__, "user-defined attribute type not recognized as such!");
529 
530  default:
531  throw InternalErr(__FILE__, __LINE__, "Unrecognized attribute type.");
532  }
533  }
534  }
535  BESDEBUG("nc", "Exiting read_attributes_netcdf4" << endl);
536 }
537 #endif
538 
548 void nc_read_dataset_attributes(DAS &das, const string &filename)
549 {
550  BESDEBUG("nc", "In nc_read_dataset_attributes" << endl);
551 
552  int ncid, errstat;
553  errstat = nc_open(filename.c_str(), NC_NOWRITE, &ncid);
554  if (errstat != NC_NOERR)
555  throw Error(errstat, "Could not open " + path_to_filename(filename) + ".");
556 
557  // how many variables? how many global attributes?
558  int nvars, ngatts;
559  errstat = nc_inq(ncid, (int *) 0, &nvars, &ngatts, (int *) 0);
560  if (errstat != NC_NOERR)
561  throw Error(errstat, "Could not inquire about netcdf file: " + path_to_filename(filename) + ".");
562 
563  // for each variable
564  char varname[MAX_NC_NAME];
565  int natts = 0;
566  nc_type var_type;
567  for (int varid = 0; varid < nvars; ++varid) {
568  BESDEBUG("nc", "Top of for loop; for each var..." << endl);
569 
570  errstat = nc_inq_var(ncid, varid, varname, &var_type, (int*) 0, (int*) 0, &natts);
571  if (errstat != NC_NOERR)
572  throw Error(errstat, "Could not get information for variable: " + long_to_string(varid));
573 
574  AttrTable *attr_table_ptr = das.get_table(varname);
575  if (!attr_table_ptr)
576  attr_table_ptr = das.add_table(varname, new AttrTable);
577 
578  BESDEBUG("nc", "Before READ_ATTRIBUTES_MACRO" << endl);
579  READ_ATTRIBUTES_MACRO(ncid, varid, natts, attr_table_ptr);
580  BESDEBUG("nc", "After READ_ATTRIBUTES_MACRO" << endl);
581 
582  // Add a special attribute for string lengths
583  if (var_type == NC_CHAR) {
584  // number of dimensions and size of Nth dimension
585  int num_dim;
586  int vdimids[MAX_VAR_DIMS]; // variable dimension ids
587  errstat = nc_inq_var(ncid, varid, (char *) 0, (nc_type *) 0, &num_dim, vdimids, (int *) 0);
588  if (errstat != NC_NOERR)
589  throw Error(errstat, string("Could not read information about a NC_CHAR variable while building the DAS."));
590 
591  if (num_dim == 0) {
592  // a scalar NC_CHAR is stuffed into a string of length 1
593  int size = 1;
594  string print_rep = print_attr(NC_INT, 0, (void *) &size);
595  attr_table_ptr->append_attr("string_length", print_type(NC_INT), print_rep);
596  }
597  else {
598  // size_t *dim_sizes = new size_t[num_dim];
599  vector<size_t> dim_sizes(num_dim);
600  for (int i = 0; i < num_dim; ++i) {
601  if ((errstat = nc_inq_dimlen(ncid, vdimids[i], &dim_sizes[i])) != NC_NOERR) {
602  throw Error(errstat, string("Could not read dimension information about the variable `") + varname + string("'."));
603  }
604  }
605 
606  // add attribute
607  string print_rep = print_attr(NC_INT, 0, (void *) (&dim_sizes[num_dim - 1]));
608  attr_table_ptr->append_attr("string_length", print_type(NC_INT), print_rep);
609  }
610  }
611 
612 #if NETCDF_VERSION >= 4
613  else if (is_user_defined_type(ncid, var_type)) {
614  //var_type >= NC_FIRSTUSERTYPEID) {
615  vector<char> name(MAX_NC_NAME + 1);
616  int class_type;
617  errstat = nc_inq_user_type(ncid, var_type, &name[0], 0, 0, 0, &class_type);
618  if (errstat != NC_NOERR)
619  throw(InternalErr(__FILE__, __LINE__, "Could not get information about a user-defined type (" + long_to_string(errstat) + ")."));
620 
621  switch (class_type) {
622  case NC_OPAQUE: {
623  attr_table_ptr->append_attr("DAP2_OriginalNetCDFBaseType", print_type(NC_STRING), "NC_OPAQUE");
624  attr_table_ptr->append_attr("DAP2_OriginalNetCDFTypeName", print_type(NC_STRING), &name[0]);
625  break;
626  }
627 
628  case NC_ENUM: {
629  //vector<char> name(MAX_NC_NAME + 1);
630  nc_type base_nc_type;
631  size_t base_size, num_members;
632  errstat = nc_inq_enum(ncid, var_type, 0/*&name[0]*/, &base_nc_type, &base_size, &num_members);
633  if (errstat != NC_NOERR)
634  throw(InternalErr(__FILE__, __LINE__, "Could not get information about an enum(" + long_to_string(errstat) + ")."));
635 
636  // If the base type is a 64-bit int, bail with an error or
637  // a message about unsupported types
638  if (base_nc_type == NC_INT64 || base_nc_type == NC_UINT64) {
640  cerr << "An Enum uses 64-bit integers, but this handler does not support that type." << endl;
641  else
642  throw Error("An Enum uses 64-bit integers, but this handler does not support that type.");
643  break;
644  }
645 
646  for (size_t i = 0; i < num_members; ++i) {
647  vector<char> member_name(MAX_NC_NAME + 1);
648  vector<char> member_value(base_size);
649  errstat = nc_inq_enum_member(ncid, var_type, i, &member_name[0], &member_value[0]);
650  if (errstat != NC_NOERR)
651  throw(InternalErr(__FILE__, __LINE__, "Could not get information about an enum value (" + long_to_string(errstat) + ")."));
652  attr_table_ptr->append_attr("DAP2_EnumValues", print_type(base_nc_type), print_attr(base_nc_type, 0, &member_value[0]));
653  attr_table_ptr->append_attr("DAP2_EnumNames", print_type(NC_STRING), &member_name[0]);
654  }
655 
656  attr_table_ptr->append_attr("DAP2_OriginalNetCDFBaseType", print_type(NC_STRING), "NC_ENUM");
657  attr_table_ptr->append_attr("DAP2_OriginalNetCDFTypeName", print_type(NC_STRING), &name[0]);
658 
659  break;
660  }
661 
662  default:
663  break;
664  }
665  }
666 #endif // NETCDF_VERSION >= 4
667  }
668 
669  BESDEBUG("nc", "Starting global attributes" << endl);
670 
671  // global attributes
672  if (ngatts > 0) {
673  AttrTable *attr_table_ptr = das.add_table("NC_GLOBAL", new AttrTable);
674  READ_ATTRIBUTES_MACRO(ncid, NC_GLOBAL, ngatts, attr_table_ptr);
675  }
676 
677  // Add unlimited dimension name in DODS_EXTRA attribute table
678  int xdimid;
679  char dimname[MAX_NC_NAME];
680  nc_type datatype = NC_CHAR;
681  nc_inq(ncid, (int *) 0, (int *) 0, (int *) 0, &xdimid);
682  if (xdimid != -1) {
683  nc_inq_dim(ncid, xdimid, dimname, (size_t *) 0);
684  string print_rep = print_attr(datatype, 0, dimname);
685  AttrTable *attr_table_ptr = das.add_table("DODS_EXTRA", new AttrTable);
686  attr_table_ptr->append_attr("Unlimited_Dimension", print_type(datatype), print_rep);
687  }
688 
689  if (nc_close(ncid) != NC_NOERR)
690  throw InternalErr(__FILE__, __LINE__, "ncdds: Could not close the dataset!");
691 
692  BESDEBUG("nc", "Exiting nc_read_dataset_attributes" << endl);
693 }
#define READ_ATTRIBUTES_MACRO
Definition: ncdas.cc:67
static class NCMLUtil overview
void nc_read_dataset_attributes(DAS &das, const string &filename)
Given a reference to an instance of class DAS and a filename that refers to a netcdf file...
Definition: ncdas.cc:548
static bool get_ignore_unknown_types()
static bool get_promote_byte_to_short()
#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
string print_attr(hid_t type, int loc, void *sm_buf)
will get the printed representation of an attribute.
Definition: h5das.cc:288