[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[netCDF #WLR-457355]: putting in strings in netcdf 4.01



> Can u please send me a link or the file.  I looked in my cxx and could not
> find the nctst.cpp

I'm just attaching it after copying it out of the cxx/ directory in the
netcdf-4.0.1 distribution, which you can download (and unpack) from here:

  ftp://ftp.unidata.ucar.edu/pub/netcdf/netcdf-4.0.1.tar.gz

--Russ

> 
> address@hidden> wrote:
> 
> > > I am using c++
> >
> > There is a small C++ example in the netCDF sources, in cxx/nctst.cpp,
> > which has an example of defining a netCDF variable of type ncChar and
> > writing a string into it, with the statement:
> >
> >      reftime->put(s, strlen(s));
> >
> > Here reftime is the variable and s is the string, really an array of
> > char.  The netCDF-3 C++ API you are presumably using was written in the
> > early 90's before exceptions, templates, and STL were made part of the
> > C++ standard, so those it may seem primitive.  It provides no support
> > for C++ Strings, just C-style char arrays.  Because the data also must
> > be accessible to Fortran-77 programs, fixed-size char arrays are used
> > rather than real strings, and you have to provide lengths rather than
> > null termination, as in the call to strlen(s) above.
> >
> > The netCDF-4 data model and interface, on the other hand, supports an
> > actual netCDF string type that corresponds to variable-length strings.
> > But netCDF-4 files written using variable-length strings are not
> > recognized by netCDF-3 programs, so you would have a compatibility
> > problem if you chose to use them.
> >
> > There is a very new and experimental modern C++ interface for netCDF-4
> > included in the post-4.1 snapshot releases of netCDF, contributed by
> > Lynton Appel of the Culham Centre for Fusion Energy in the UK.  I believe
> > it also supports creating and writing netCDF-3 files, but I haven't yet
> > had much chance to work with it and understand all its capabilities.
> >
> > --Russ
> >
> > string type
> >
> > > address@hidden> wrote:
> > >
> > > > Hi Richard,
> > > >
> > > > > I am trying to put in strings into a netcdf file and I am having
> > > > problems.
> > > > > I have download the examples and the documentation and nothing is
> > working
> > > > at
> > > > > the moment.  I was wondering do you have any coding examples, that
> > can
> > > > > describe what i need to do. Thank you in advance.
> > > >
> > > > Are you using C, Fortran, Java, or some other programming language?
> > > >
> > > > --Russ
> > > >
> > > > Russ Rew                                         UCAR Unidata Program
> > > > address@hidden                      http://www.unidata.ucar.edu
> > > >
> > > >
> > > >
> > > > Ticket Details
> > > > ===================
> > > > Ticket ID: WLR-457355
> > > > Department: Support netCDF
> > > > Priority: Normal
> > > > Status: Closed
> > > >
> > > >
> > >
> > >
> > > --
> > > Richard L. Messick
> > > NC A&T SU
> > >
> > >
> >
> > Russ Rew                                         UCAR Unidata Program
> > address@hidden                      http://www.unidata.ucar.edu
> >
> >
> >
> > Ticket Details
> > ===================
> > Ticket ID: WLR-457355
> > Department: Support netCDF
> > Priority: Normal
> > Status: Closed
> >
> >
> 
> 
> --
> Richard L. Messick
> NC A&T SU
> 
> 

Russ Rew                                         UCAR Unidata Program
address@hidden                      http://www.unidata.ucar.edu



Ticket Details
===================
Ticket ID: WLR-457355
Department: Support netCDF
Priority: Normal
Status: Closed
/*********************************************************************
 * Copyright 2005, UCAR/Unidata See COPYRIGHT file for copying and
 * redistribution conditions.
 * 
 * This runs the C++ tests for netCDF.
 * 
 * $Id: nctst.cpp,v 1.28 2007/04/02 21:01:12 ed Exp $
 *********************************************************************/

#include <config.h>
#include <iostream>
using namespace std;

#include <string.h>
#include "netcdfcpp.h"

const char LAT[] = "lat";
const char LON[] = "lon";
const char FRTIME[] = "frtime";
const char TIMELEN1[] = "timelen";
const char P_NAME[] = "P";
const char PRES_MAX_WIND[] = "pressure at maximum wind";
const char LONG_NAME[] = "long_name";
const char UNITS[] = "units";
const char VALID_RANGE[] = "valid_range";
const char FILL_VALUE[] = "_FillValue";
const char DEGREES_NORTH[] = "degrees_north";
const char LONGITUDE[] = "longitude";
const char LATITUDE[] = "latitude";
const char HECTOPASCALS[] = "hectopascals";
const char DEGREES_EAST[] = "degrees_east";
const char HOURS[] = "hours";
const char FORECAST_TIME[] = "forecast time";
const char REFERENCE_TIME[] = "reference time";
const char REFTIME[] = "reftime";
const char TEXT_TIME[] = "text_time";
const char SCALARV[] = "scalarv";
const char SCALAR_ATT[] = "scalar_att";
const int SCALAR_VALUE = 1;
const char HISTORY[] = "history";
const char TITLE[] = "title";
const char HISTORY_STR[] = "created by Unidata LDM from NPS broadcast";
const char TITLE_STR[] = "NMC Global Product Set: Pressure at Maximum Wind";

const int NC_ERR = 2;
const int NLATS = 4;
const int NLONS = 3;
const int NFRTIMES = 2;
const int TIMESTRINGLEN = 20;
const int NRANGES = 2;

// These are data values written out by the gen() function, and read
// in again and checked by the read() function.
static float range[] = {0., 1500.};
static float lats[NLATS] = {-90, -87.5, -85, -82.5};
static float lons[NLONS] = {-180, -175, -170};
static int frtimes[NFRTIMES] = {12, 18};
static const char* s = "1992-3-21 12:00";
static float fill_value = -9999.0;
static float P_data[NFRTIMES][NLATS][NLONS] = {
   {{950, 951, 952}, {953, 954, 955}, {956, 957, 958}, {959, 960, 961}},
   {{962, 963, 964}, {965, 966, 967}, {968, 969, 970}, {971, 972, 973}}
};


// Check a string attribute to make sure it has the correct value.
int 
check_string_att(NcAtt *att, const char *theName, const char *value)
{
   if (!att->is_valid() || strncmp(att->name(), theName, strlen(theName)) || 
       att->type() != ncChar || att->num_vals() != (long)strlen(value)) 
      return NC_ERR;

   char *value_in = att->as_string(0);
   if (strncmp(value_in, value, strlen(value)))
      return NC_ERR;
   delete value_in;

   return 0;
}

// Check the units and long_name attributes of a var to make sure they
// are what is expected.
int
check_u_ln_atts(NcVar *var, const char *units, const char *long_name)
{
   NcAtt *att;

   if (!(att = var->get_att(UNITS)))
      return NC_ERR;
   if (check_string_att(att, UNITS, units))
      return NC_ERR;
   delete att;

   if (!(att = var->get_att(LONG_NAME)))
      return NC_ERR;
   if (check_string_att(att, LONG_NAME, long_name))
      return NC_ERR;
   delete att;

   return 0;
}

// This reads the netCDF file created by gen() and ensures that
// everything is there as expected.
int read(const char* path, NcFile::FileFormat format)
{
   NcAtt *att;

   // open the file
   NcFile nc(path); 

   if (!nc.is_valid())
      return NC_ERR;

   // Check the format.
   if (nc.get_format() != format)
      return NC_ERR;

   // Check the numbers of things.
   if (nc.num_dims() != 4 || nc.num_vars() != 6 || 
       nc.num_atts() != 2)  
      return NC_ERR;

   // Check the global attributes.
   if (!(att = nc.get_att(HISTORY)))
      return NC_ERR;
   if (check_string_att(att, HISTORY, HISTORY_STR))
      return NC_ERR;
   delete att;

   if (!(att = nc.get_att(TITLE)))
      return NC_ERR;
   if (check_string_att(att, TITLE, TITLE_STR))
      return NC_ERR;
   delete att;
   
   // Check the dimensions.
   NcDim *latDim, *lonDim, *frtimeDim, *timeLenDim;

   if (!(latDim = nc.get_dim(LAT)))
      return NC_ERR;
   if (!latDim->is_valid() || strncmp(latDim->name(), LAT, strlen(LAT)) || 
       latDim->size() != NLATS || latDim->is_unlimited()) 
      return NC_ERR;

   if (!(lonDim = nc.get_dim(LON)))
      return NC_ERR;
   if (!lonDim->is_valid() || strncmp(lonDim->name(), LON, strlen(LON)) || 
       lonDim->size() != NLONS || lonDim->is_unlimited()) 
      return NC_ERR;

   if (!(frtimeDim = nc.get_dim(FRTIME)))
      return NC_ERR;
   if (!frtimeDim->is_valid() || strncmp(frtimeDim->name(), FRTIME, 
strlen(FRTIME)) || 
       frtimeDim->size() != 2 || !frtimeDim->is_unlimited()) 
      return NC_ERR;

   if (!(timeLenDim = nc.get_dim(TIMELEN1)))
      return NC_ERR;
   if (!timeLenDim->is_valid() || strncmp(timeLenDim->name(), TIMELEN1, 
strlen(TIMELEN1)) || 
       timeLenDim->size() != TIMESTRINGLEN || timeLenDim->is_unlimited()) 
      return NC_ERR;

   // Check the coordinate variables.
   NcVar *latVar, *lonVar, *frtimeVar, *refTimeVar;

   // Get the latitude.
   if (!(latVar = nc.get_var(LAT)))
      return NC_ERR;

   // Check units and long name.
   if (check_u_ln_atts(latVar, DEGREES_NORTH, LATITUDE))
      return NC_ERR;

   // Get the longitude.
   if (!(lonVar = nc.get_var(LON)))
      return NC_ERR;

   // Check units and long name.
   if (check_u_ln_atts(lonVar, DEGREES_EAST, LONGITUDE))
      return NC_ERR;

   // Get the forecast time coordinate variable.
   if (!(frtimeVar = nc.get_var(FRTIME)))
      return NC_ERR;

   // Check units and long name.
   if (check_u_ln_atts(frtimeVar, HOURS, FORECAST_TIME))
      return NC_ERR;

   // Get the refTime coordinate variable.
   if (!(refTimeVar = nc.get_var(REFTIME)))
      return NC_ERR;

   // Check units and long name.
   if (check_u_ln_atts(refTimeVar, TEXT_TIME, REFERENCE_TIME))
      return NC_ERR;

   // Check the data variables.
   NcVar *pVar, *scalarVar;
     
   if (!(pVar = nc.get_var(P_NAME)))
      return NC_ERR;

   // Check units and long name.
   if (check_u_ln_atts(pVar, HECTOPASCALS, PRES_MAX_WIND))
      return NC_ERR;

   // Check the valid range, and check the values.
   if (!(att = pVar->get_att(VALID_RANGE)))
      return NC_ERR;
   if (!att->is_valid() || strncmp(att->name(), VALID_RANGE, 
strlen(VALID_RANGE)) || 
       att->type() != ncFloat || att->num_vals() != NRANGES) 
      return NC_ERR;
   float range_in[NRANGES] = {att->as_float(0), att->as_float(1)};
   if (range_in[0] != range[0] || range_in[1] != range[1])
      return NC_ERR;
   delete att;

   // Check the fill value, and check the value.
   if (!(att = pVar->get_att(FILL_VALUE)))
      return NC_ERR;
   if (!att->is_valid() || strncmp(att->name(), FILL_VALUE, strlen(FILL_VALUE)) 
|| 
       att->type() != ncFloat || att->num_vals() != 1) 
      return NC_ERR;
   float fill_value_in = att->as_float(0);
   if (fill_value_in != fill_value)
      return NC_ERR;
   delete att;

   // Check the data in the pressure variable.
   float P_data_in[NFRTIMES][NLATS][NLONS];
   pVar->get(&P_data_in[0][0][0], NFRTIMES, NLATS, NLONS);
   for (int f = 0; f < NFRTIMES; f++)
      for (int la = 0; la < NLATS; la++)
         for (int lo = 0; lo < NLONS; lo++)
            if (P_data_in[f][la][lo] != P_data[f][la][lo])
               return NC_ERR;

   // Get the scalar variable.
   if (!(scalarVar = nc.get_var(SCALARV)))
      return NC_ERR;

   // Check for the scalar attribute of the scalar variable and check its value.
   if (!(att = scalarVar->get_att(SCALAR_ATT)))
      return NC_ERR;
   if (!att->is_valid() || strncmp(att->name(), SCALAR_ATT, strlen(SCALAR_ATT)) 
|| 
       att->type() != ncInt || att->num_vals() != 1) 
      return NC_ERR;
   int value_in = att->as_int(0);
   if (value_in != SCALAR_VALUE)
      return NC_ERR;
   delete att;

   // Check the value of the scalar variable.


   return 0;
}

int gen(const char* path, NcFile::FileFormat format)            // Generate a 
netCDF file
{

    NcFile nc(path, NcFile::Replace, NULL, 0, format); // Create, leave in 
define mode

    // Check if the file was opened successfully
    if (! nc.is_valid()) {
        cerr << "can't create netCDF file " << path << "\n";
        return NC_ERR;
    }

    // Create dimensions
    NcDim* latd = nc.add_dim(LAT, NLATS);
    NcDim* lond = nc.add_dim(LON, NLONS);
    NcDim* frtimed = nc.add_dim(FRTIME); // unlimited dimension
    NcDim* timelend = nc.add_dim(TIMELEN1, TIMESTRINGLEN); 

    // Create variables and their attributes
    NcVar* P = nc.add_var(P_NAME, ncFloat, frtimed, latd, lond);
    P->add_att(LONG_NAME, PRES_MAX_WIND);
    P->add_att(UNITS, HECTOPASCALS);
    P->add_att(VALID_RANGE, NRANGES, range);
    P->add_att(FILL_VALUE, fill_value);

    NcVar* lat = nc.add_var(LAT, ncFloat, latd);
    lat->add_att(LONG_NAME, LATITUDE);
    lat->add_att(UNITS, DEGREES_NORTH);

    NcVar* lon = nc.add_var(LON, ncFloat, lond);
    lon->add_att(LONG_NAME, LONGITUDE);
    lon->add_att(UNITS, DEGREES_EAST);

    NcVar* frtime = nc.add_var(FRTIME, ncLong, frtimed);
    frtime->add_att(LONG_NAME, FORECAST_TIME);
    frtime->add_att(UNITS, HOURS);

    NcVar* reftime = nc.add_var(REFTIME, ncChar, timelend);
    reftime->add_att(LONG_NAME, REFERENCE_TIME);
    reftime->add_att(UNITS, TEXT_TIME);

    NcVar* scalar = nc.add_var(SCALARV, ncInt);
    scalar->add_att(SCALAR_ATT, SCALAR_VALUE);

    // Global attributes
    nc.add_att(HISTORY, HISTORY_STR);
    nc.add_att(TITLE, TITLE_STR);

    // Start writing data, implictly leaves define mode

    lat->put(lats, NLATS);

    lon->put(lons, NLONS);

    frtime->put(frtimes, NFRTIMES);

    reftime->put(s, strlen(s));

    // We could write all P data at once with P->put(&P_data[0][0][0], 
P->edges()),
    // but instead we write one record at a time, to show use of setcur().
    long rec = 0;                                      // start at zero-th
    const long nrecs = 1;                              // # records to write
    P->put(&P_data[0][0][0], nrecs, NLATS, NLONS);           // write zero-th 
record
    P->set_cur(++rec);                                 // set to next record
    P->put(&P_data[1][0][0], nrecs, NLATS, NLONS); // write next record

    // close of nc takes place in destructor
    return 0;
}

/*
 * Convert pathname of netcdf file into name for CDL, by taking last component
 * of path and stripping off any extension.  The returned string is in static
 * storage, so copy it if you need to keep it.
 */
static char* 
cdl_name(const char* path)
{
    const char* cp = path + strlen(path);
    while (*(cp-1) != '/' && cp != path) // assumes UNIX path separator
        cp--;

    static char np[NC_MAX_NAME];
    strncpy(&np[0], cp, NC_MAX_NAME);

    char* ep = np + strlen(np);
    while (*ep != '.' && ep != np)
        ep--;
    if (*ep == '.')
      *ep = '\0';
    return np;
}

// A derived class, just like NcFile except knows how to "dump" its
// dimensions, variables, global attributes, and data in ASCII form.
class DumpableNcFile : public NcFile
{
  public:
    DumpableNcFile(const char* path, NcFile::FileMode mode = ReadOnly)
        : NcFile(path, mode) {} ;
    void dumpdims( void );
    void dumpvars( void );
    void dumpgatts( void );
    void dumpdata( void );
};

void DumpableNcFile::dumpdims( void )
{

    for (int n=0; n < num_dims(); n++) {
        NcDim* dim = get_dim(n);
        cout << "\t" << dim->name() << " = " ;
        if (dim->is_unlimited())
          cout << "UNLIMITED" << " ;\t " << "// " << dim->size() <<
            " currently\n";
        else
          cout << dim->size() << " ;\n";
    }
}

void dumpatts(NcVar& var)
{
    NcToken vname = var.name();
    NcAtt* ap;
    for(int n = 0; (ap = var.get_att(n)); n++) {
        cout << "\t\t" << vname << ":" << ap->name() << " = " ;
        NcValues* vals = ap->values();
        cout << *vals << " ;" << endl ;
        delete ap;
        delete vals;
    }
}

void DumpableNcFile::dumpvars( void )
{
    int n;
    static const char* types[] =
      {"","byte","char","short","long","float","double"};
    NcVar* vp;

    for(n = 0; (vp = get_var(n)); n++) {
        cout << "\t" << types[vp->type()] << " " << vp->name() ;

        if (vp->num_dims() > 0) {
            cout << "(";
            for (int d = 0; d < vp->num_dims(); d++) {
                NcDim* dim = vp->get_dim(d);
                cout << dim->name();
                if (d < vp->num_dims()-1)
                  cout << ", ";           
            }
            cout << ")";
        }
        cout << " ;\n";
        // now dump each of this variable's attributes
        dumpatts(*vp);
    }
}

void DumpableNcFile::dumpgatts( void )
{
    NcAtt* ap;
    for(int n = 0; (ap = get_att(n)); n++) {
        cout << "\t\t" << ":" << ap->name() << " = " ;
        NcValues* vals = ap->values();
        cout << *vals << " ;" << endl ;
        delete vals;
        delete ap;
    }
}

void DumpableNcFile::dumpdata( )
{
    NcVar* vp;
    for (int n = 0; (vp = get_var(n)); n++) {
        cout << " " << vp->name() << " = ";
        NcValues* vals = vp->values();
        cout << *vals << " ;" << endl ;
        delete vals;
    }
}

void dump(const char* path)
{
    DumpableNcFile nc(path);    // default is open in read-only mode

    cout << "netcdf " << cdl_name(path) << " {" << endl <<
            "dimensions:" << endl ;

    nc.dumpdims();

    cout << "variables:" << endl;

    nc.dumpvars();

    if (nc.num_atts() > 0)
      cout << "// global attributes" << endl ;

    nc.dumpgatts();

    cout << "data:" << endl;

    nc.dumpdata();

    cout << "}" << endl;
}

/* Test everything for classic and 64-bit offsetfiles. If netcdf-4 is
 * included, that means another whole round of testing. */
#ifdef USE_NETCDF4
#define NUM_FORMATS (4)
#else
#define NUM_FORMATS (2)
#endif

int
main( void )    // test new netCDF interface
{

   cout << "*** Testing C++ API with " << NUM_FORMATS 
        << " different netCDF formats.\n";

   // Set up the format constants.
   NcFile::FileFormat format[NUM_FORMATS] = {NcFile::Classic, 
NcFile::Offset64Bits
#ifdef USE_NETCDF4
                                             , NcFile::Netcdf4, 
NcFile::Netcdf4Classic
#endif
   };

   // Set up the file names.
   char file_name[NUM_FORMATS][NC_MAX_NAME] = 
      {"nctst_classic.nc", "nctst_64bit_offset.nc"
#ifdef USE_NETCDF4
       , "nctst_netcdf4.nc", "nctst_netcdf4_classic.nc"
#endif
   };

   int errs = 0;
   for (int i = 0; i < NUM_FORMATS; i++)
   {
      if (gen(file_name[i], format[i]) || 
          read(file_name[i], format[i]))
      {
         cout << "*** FAILURE with file " << file_name[i] << "\n";
         errs++;
      }
      else
         cout << "*** SUCCESS with file " << file_name[i] << "\n";
   }

   cout << "\n*** Total number of failures: " << errs << "\n";
   if (errs)
      cout << "*** nctst FAILURE!\n";
   else
      cout << "*** nctst SUCCESS!\n";
      
   return errs;
}