Building a DDS object is the heart of writing your own OPeNDAP server. This object will be used to generate the DDS response and it will be the basis of the DataDDS response. You have to do two things to accomplish building the DDS. First you must decide how the variables that make up your dataset can be represented using the data type hierarchy that is part of the DAP. Once you have done this, you need to write code that can build an instance of DDS for your dataset. In practice the hardest part of this choosing the appropriate data types for you data set; once you know how to map variables in your dataset to the DAP data types, writing code to build the DDS instance is easy.
Many data sets are actually a representative of large group. In some cases there may be an API that can read the datasets and there may even be a formal data model. In such a case you're best off using the API and writing general code to build the DDS while performing a depth-first scan of the variables in the dataset.
Here's how the Matlab server builds a DDS object:
void
read_descriptors(DDS &dds_table, string filename)
{
MATFile *fp;
Matrix *mp;
// dataset name
dds_table.set_dataset_name(name_path(filename));
fp = matOpen(filename.c_str(), "r");
if (fp == NULL)
throw Error(string("Could not open the file: ") + filename);
// Read all the matrices in file
while ((mp = matGetNextMatrix(fp)) != NULL) {
// String types are used as attributes
if(mxIsNumeric(mp)) {
if(mxIsComplex(mp)) {
string Real = (string)mxGetName(mp) + "_Real";
// real part
MakeMatrix(&dds_table, Real, mxGetM(mp),mxGetN(mp));
string Imag = (string)mxGetName(mp) + "_Imaginary";
// imaginary part
MakeMatrix(&dds_table, Imag, mxGetM(mp),mxGetN(mp));
} else
MakeMatrix(&dds_table, (string)mxGetName(mp), mxGetM(mp),
mxGetN(mp));
}
mxFreeMatrix(mp);
}
matClose(fp);
return true;
}
This function iterates over all the variables in the Matlab file named
by filename and creates a variable in the DDS for each numerical
array in the dataset. Matlab does not have the notion of attributes
bound to specific variables, but it is often the case that attribute
information is present in string variables, something for which this code checks. However, this function simply ignores the string variables since
attribute information is the job of a different object. Of course, a
function could be written to build both objects at the same time...
void
MakeMatrix(DDS *dds_table, string name, int row, int column)
{
Array *ar;
string DimName;
size_t pos;
// complex matrices have common rows and columns
if ((pos = name.find("_Real")) != name.npos)
DimName = name.substr(0, pos);
else{
if ((pos = name.find("_Imaginary")) != name.npos)
DimName = name.substr(0, pos);
else
DimName = name;
}
BaseType *bt = NewFloat64(name);
ar = NewArray(name);
ar->add_var(bt);
ar->append_dim(row,DimName+"_row");
ar->append_dim(column,DimName+"_column");
if (!dds_table)
throw InternalErr(__FILE__, __LINE__, "NULL DDS object.");
dds_table->add_var(ar);
}
This function has two main parts, the first, which is of less interest to this tutorial, checks to see if the matrix holds complex numbers and does some special stuff if it does. The second part creates a new array of 64 bit floating point numbers. Here's the code:
BaseType *bt = NewFloat64(name); ar = NewArray(name); ar->add_var(bt); ar->append_dim(row, DimName+"_row"); ar->append_dim(column, DimName+"_column");
The first line creates a new instance of the Float64 data type and
assigns it to a BaseType, the parent of all the data types. There's no
reason it couldn't be bound to an instance of Float64, but in a server
where there might be many types of arrays, it is easier to use a pointer to a
more general object. The second line creates a new Array instance and the
third line binds the Float64 object to the new Array object,
making the array an array of Float64s. The last two lines set the sizes
of the Array's dimensions. Because instances of Array occur frequently, it
is a good idea to be familiar with the
Array
and
Vector
classes (Vector is the parent of both Array and List).
Finally, the last line of the function,
dds_table->add_var(ar);
Adds variable ar to the DDS.
Note that our Matlab server supports only the data types that can appear in a Matlab 5 file. This means that the only numeric data type supported is a matrix of 64 bit floating point numbers. Strings, as mentioned earlier, are handled specially. In most other servers, the code to build the variables and load them in a DDS object is more complex since it must handle mapping the dataset's different types to the DAP's.