bes  Updated for version 3.20.6
FoDapCovJsonTransform.cc
1 // -*- mode: c++; c-basic-offset:4 -*-
2 //
3 // FoDapCovJsonTransform.cc
4 //
5 // This file is part of BES CovJSON File Out Module
6 //
7 // Copyright (c) 2018 OPeNDAP, Inc.
8 // Author: Corey Hemphill <hemphilc@oregonstate.edu>
9 // Author: River Hendriksen <hendriri@oregonstate.edu>
10 // Author: Riley Rimer <rrimer@oregonstate.edu>
11 //
12 // Adapted from the File Out JSON module implemented by Nathan Potter
13 //
14 // This library is free software; you can redistribute it and/or
15 // modify it under the terms of the GNU Lesser General Public
16 // License as published by the Free Software Foundation; either
17 // version 2.1 of the License, or (at your option) any later version.
18 //
19 // This library is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 // Lesser General Public License for more details.
23 //
24 // You should have received a copy of the GNU Lesser General Public
25 // License along with this library; if not, write to the Free Software
26 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 //
28 
29 #include "config.h"
30 
31 #include <cassert>
32 #include <sstream>
33 #include <iostream>
34 #include <fstream>
35 #include <stddef.h>
36 #include <string>
37 #include <cstring>
38 #include <typeinfo>
39 #include <iomanip> // setprecision
40 #include <sstream> // stringstream
41 #include <vector>
42 #include <ctime>
43 
44 using std::ostringstream;
45 using std::istringstream;
46 
47 #include <DDS.h>
48 #include <Structure.h>
49 #include <Constructor.h>
50 #include <Array.h>
51 #include <Grid.h>
52 #include <Sequence.h>
53 #include <Float64.h>
54 #include <Str.h>
55 #include <Url.h>
56 #include <BESDebug.h>
57 #include <BESInternalError.h>
58 #include <DapFunctionUtils.h>
59 #include "FoDapCovJsonTransform.h"
60 #include "focovjson_utils.h"
61 
62 #define FoDapCovJsonTransform_debug_key "focovjson"
63 
64 
65 bool FoDapCovJsonTransform::canConvert()
66 {
67  // If x, y, z, and t all exist
68  // We are assuming the following is true:
69  // - shapeVals[0] = x axis
70  // - shapeVals[1] = y axis
71  // - shapeVals[2] = z axis
72  // - shapeVals[3] = t axis
73  if(xExists && yExists && zExists && tExists) {
74 
75  if (shapeVals.size() < 4)
76  return false;
77 
78  // A domain with Grid domain type MUST have the axes "x" and "y"
79  // and MAY have the axes "z" and "t".
80  if((shapeVals[0] > 1) && (shapeVals[1] > 1) && (shapeVals[2] >= 1) && (shapeVals[3] >= 0)) {
81  domainType = "Grid";
82  return true;
83  }
84 
85  // A domain with VerticalProfile domain type MUST have the axes "x",
86  // "y", and "z", where "x" and "y" MUST have a single coordinate only.
87  else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] >= 1) && ((shapeVals[3] <= 1) && (shapeVals[3] >= 0))) {
88  domainType = "Vertical Profile";
89  return true;
90  }
91 
92  // A domain with PointSeries domain type MUST have the axes "x", "y",
93  // and "t" where "x" and "y" MUST have a single coordinate only. A
94  // domain with PointSeries domain type MAY have the axis "z" which
95  // MUST have a single coordinate only.
96  else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] == 1) && (shapeVals[3] >= 0)) {
97  domainType = "Point Series";
98  return true;
99  }
100 
101  // A domain with Point domain type MUST have the axes "x" and "y" and MAY
102  // have the axes "z" and "t" where all MUST have a single coordinate only.
103  else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] == 1) && (shapeVals[3] == 1)) {
104  domainType = "Point";
105  return true;
106  }
107  }
108 
109  // If just x, y, and t exist
110  // We are assuming the following is true:
111  // - shapeVals[0] = x axis
112  // - shapeVals[1] = y axis
113  // - shapeVals[2] = t axis
114  else if(xExists && yExists && !zExists && tExists) {
115 
116  if (shapeVals.size() < 3)
117  return false;
118 
119  // A domain with Grid domain type MUST have the axes "x" and "y"
120  // and MAY have the axes "z" and "t".
121  if((shapeVals[0] > 1) && (shapeVals[1] > 1) && (shapeVals[2] >= 0)) {
122  domainType = "Grid";
123  return true;
124  }
125 
126  // A domain with PointSeries domain type MUST have the axes "x", "y",
127  // and "t" where "x" and "y" MUST have a single coordinate only. A
128  // domain with PointSeries domain type MAY have the axis "z" which
129  // MUST have a single coordinate only.
130  else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] >= 0)) {
131  domainType = "Point Series";
132  return true;
133  }
134 
135  // A domain with Point domain type MUST have the axes "x" and "y" and MAY
136  // have the axes "z" and "t" where all MUST have a single coordinate only.
137  else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] == 1)) {
138  domainType = "Point";
139  return true;
140  }
141  }
142 
143  // If just x and y exist
144  // We are assuming the following is true:
145  // - shapeVals[0] = x axis
146  // - shapeVals[1] = y axis
147  else if(xExists && yExists && !zExists && !tExists) {
148 
149  if (shapeVals.size() < 2)
150  return false;
151 
152  // A domain with Grid domain type MUST have the axes "x" and "y"
153  // and MAY have the axes "z" and "t".
154  if((shapeVals[0] > 1) && (shapeVals[1] > 1)) {
155  domainType = "Grid";
156  return true;
157  }
158 
159  // A domain with Point domain type MUST have the axes "x" and "y" and MAY
160  // have the axes "z" and "t" where all MUST have a single coordinate only.
161  else if((shapeVals[0] == 1) && (shapeVals[1] == 1)) {
162  domainType = "Point";
163  return true;
164  }
165  }
166 
167  return false; // This source DDS is not valid as CovJSON
168 }
169 
170 template<typename T>
171 unsigned int FoDapCovJsonTransform::covjsonSimpleTypeArrayWorker(ostream *strm, T *values, unsigned int indx,
172  vector<unsigned int> *shape, unsigned int currentDim)
173 {
174  unsigned int currentDimSize = (*shape)[currentDim];
175 
176  // FOR TESTING AND DEBUGGING PURPOSES
177  // *strm << "\"currentDim\": \"" << currentDim << "\"" << endl;
178  // *strm << "\"currentDimSize\": \"" << currentDimSize << "\"" << endl;
179 
180  for(unsigned int i = 0; i < currentDimSize; i++) {
181  if(currentDim < shape->size() - 1) {
182  BESDEBUG(FoDapCovJsonTransform_debug_key,
183  "covjsonSimpleTypeArrayWorker() - Recursing! indx: " << indx << " currentDim: " << currentDim << " currentDimSize: " << currentDimSize << endl);
184  indx = covjsonSimpleTypeArrayWorker<T>(strm, values, indx, shape, currentDim + 1);
185  if(i + 1 != currentDimSize) {
186  *strm << ", ";
187  }
188  }
189  else {
190  if(i) {
191  *strm << ", ";
192  }
193  if(typeid(T) == typeid(string)) {
194  // Strings need to be escaped to be included in a CovJSON object.
195  string val = reinterpret_cast<string*>(values)[indx++];
196  *strm << "\"" << focovjson::escape_for_covjson(val) << "\"";
197  }
198  else {
199  *strm << values[indx++];
200  }
201  }
202  }
203 
204  return indx;
205 }
206 
207 template<typename T>
208 void FoDapCovJsonTransform::covjsonSimpleTypeArray(ostream *strm, libdap::Array *a, string indent, bool sendData)
209 {
210  string childindent = indent + _indent_increment;
211  bool axisRetrieved = false;
212  bool parameterRetrieved = false;
213 
214  currDataType = a->var()->type_name();
215 
216  // FOR TESTING AND DEBUGGING PURPOSES
217  // *strm << "\"type_name\": \"" << a->var()->type_name() << "\"" << endl;
218 
219  getAttributes(strm, a->get_attr_table(), a->name(), &axisRetrieved, &parameterRetrieved);
220 
221  // a->print_val(*strm, "\n", true); // For testing purposes
222 
223  // sendData = false; // For testing purposes
224 
225  // If we are dealing with an Axis
226  if((axisRetrieved == true) && (parameterRetrieved == false)) {
227  struct Axis *currAxis;
228  currAxis = axes[axisCount - 1];
229 
230  int numDim = a->dimensions(true);
231  vector<unsigned int> shape(numDim);
232  long length = focovjson::computeConstrainedShape(a, &shape);
233 
234  // FOR TESTING AND DEBUGGING PURPOSES
235  // *strm << "\"numDimensions\": \"" << numDim << "\"" << endl;
236  // *strm << "\"length\": \"" << length << "\"" << endl << endl;
237 
238  if (currAxis->name.compare("t") != 0) {
239  if (sendData) {
240  currAxis->values += "\"values\": [";
241  unsigned int indx = 0;
242  vector<T> src(length);
243  a->value(&src[0]);
244 
245  ostringstream astrm;
246  indx = covjsonSimpleTypeArrayWorker(&astrm, &src[0], 0, &shape, 0);
247  currAxis->values += astrm.str();
248 
249  currAxis->values += "]";
250 
251  if (length != indx) {
252  BESDEBUG(FoDapCovJsonTransform_debug_key,
253  "covjsonSimpleTypeArray(Axis) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
254  }
255  assert(length == indx);
256  }
257  else {
258  currAxis->values += "\"values\": []";
259  }
260  }
261  }
262 
263  // If we are dealing with a Parameter
264  else if(axisRetrieved == false && parameterRetrieved == true) {
265  struct Parameter *currParameter;
266  currParameter = parameters[parameterCount - 1];
267 
268  int numDim = a->dimensions(true);
269  vector<unsigned int> shape(numDim);
270  long length = focovjson::computeConstrainedShape(a, &shape);
271 
272  // FOR TESTING AND DEBUGGING PURPOSES
273  // *strm << "\"numDimensions\": \"" << a->dimensions(true) << "\"" << endl;
274  // *strm << "\"length\": \"" << length << "\"" << endl << endl;
275 
276  currParameter->shape += "\"shape\": [";
277  for(vector<unsigned int>::size_type i = 0; i < shape.size(); i++) {
278  if(i > 0) {
279  currParameter->shape += ", ";
280  }
281 
282  // Process the shape's values, which are strings,
283  // convert them into integers, and store them
284  ostringstream otemp;
285  istringstream itemp;
286  int tempVal = 0;
287  otemp << shape[i];
288  istringstream (otemp.str());
289  istringstream (otemp.str()) >> tempVal;
290  shapeVals.push_back(tempVal);
291 
292  // t may only have 1 value: the origin timestamp
293  // DANGER: t may not yet be defined
294  if((i == 0) && tExists) {
295  currParameter->shape += "1";
296  }
297  else {
298  currParameter->shape += otemp.str();
299  }
300  }
301  currParameter->shape += "],";
302 
303  if (sendData) {
304  currParameter->values += "\"values\": [";
305  unsigned int indx = 0;
306  vector<T> src(length);
307  a->value(&src[0]);
308 
309  ostringstream pstrm;
310  indx = covjsonSimpleTypeArrayWorker(&pstrm, &src[0], 0, &shape, 0);
311  currParameter->values += pstrm.str();
312 
313  currParameter->values += "]";
314 
315  if (length != indx) {
316  BESDEBUG(FoDapCovJsonTransform_debug_key,
317  "covjsonSimpleTypeArray(Parameter) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
318  }
319  assert(length == indx);
320  }
321  else {
322  currParameter->values += "\"values\": []";
323  }
324  }
325 }
326 
327 void FoDapCovJsonTransform::covjsonStringArray(ostream *strm, libdap::Array *a, string indent, bool sendData)
328 {
329  string childindent = indent + _indent_increment;
330  bool axisRetrieved = false;
331  bool parameterRetrieved = false;
332 
333  currDataType = a->var()->type_name();
334 
335  // FOR TESTING AND DEBUGGING PURPOSES
336  // *strm << "\"attr_tableName\": \"" << a->name() << "\"" << endl;
337 
338  // FOR TESTING AND DEBUGGING PURPOSES
339  // *strm << "\"type_name\": \"" << a->var()->type_name() << "\"" << endl;
340 
341  getAttributes(strm, a->get_attr_table(), a->name(), &axisRetrieved, &parameterRetrieved);
342 
343  // a->print_val(*strm, "\n", true); // For testing purposes
344 
345  // sendData = false; // For testing purposes
346 
347  // If we are dealing with an Axis
348  if((axisRetrieved == true) && (parameterRetrieved == false)) {
349  struct Axis *currAxis;
350  currAxis = axes[axisCount - 1];
351 
352  int numDim = a->dimensions(true);
353  vector<unsigned int> shape(numDim);
354  long length = focovjson::computeConstrainedShape(a, &shape);
355 
356  if (currAxis->name.compare("t") != 0) {
357  if (sendData) {
358  currAxis->values += "\"values\": ";
359  unsigned int indx = 0;
360  // The string type utilizes a specialized version of libdap:Array.value()
361  vector<string> sourceValues;
362  a->value(sourceValues);
363 
364  ostringstream astrm;
365  indx = covjsonSimpleTypeArrayWorker(&astrm, (string *) (&sourceValues[0]), 0, &shape, 0);
366  currAxis->values += astrm.str();
367 
368  if (length != indx) {
369  BESDEBUG(FoDapCovJsonTransform_debug_key,
370  "covjsonStringArray(Axis) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
371  }
372  assert(length == indx);
373  }
374  else {
375  currAxis->values += "\"values\": []";
376  }
377  }
378  }
379 
380  // If we are dealing with a Parameter
381  else if(axisRetrieved == false && parameterRetrieved == true) {
382  struct Parameter *currParameter;
383  currParameter = parameters[parameterCount - 1];
384 
385  int numDim = a->dimensions(true);
386  vector<unsigned int> shape(numDim);
387  long length = focovjson::computeConstrainedShape(a, &shape);
388 
389  currParameter->shape += "\"shape\": [";
390  for(vector<unsigned int>::size_type i = 0; i < shape.size(); i++) {
391  if(i > 0) {
392  currParameter->shape += ", ";
393  }
394 
395  // Process the shape's values, which are strings,
396  // convert them into integers, and store them
397  ostringstream otemp;
398  istringstream itemp;
399  int tempVal = 0;
400  otemp << shape[i];
401  istringstream (otemp.str());
402  istringstream (otemp.str()) >> tempVal;
403  shapeVals.push_back(tempVal);
404 
405  // t may only have 1 value: the origin timestamp
406  // DANGER: t may not yet be defined
407  if((i == 0) && tExists) {
408  currParameter->shape += "1";
409  }
410  else {
411  currParameter->shape += otemp.str();
412  }
413  }
414  currParameter->shape += "],";
415 
416  if (sendData) {
417  currParameter->values += "\"values\": ";
418  unsigned int indx = 0;
419  // The string type utilizes a specialized version of libdap:Array.value()
420  vector<string> sourceValues;
421  a->value(sourceValues);
422 
423  ostringstream pstrm;
424  indx = covjsonSimpleTypeArrayWorker(&pstrm, (string *) (&sourceValues[0]), 0, &shape, 0);
425  currParameter->values += pstrm.str();
426 
427  if (length != indx) {
428  BESDEBUG(FoDapCovJsonTransform_debug_key,
429  "covjsonStringArray(Parameter) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
430  }
431  assert(length == indx);
432  }
433  else {
434  currParameter->values += "\"values\": []";
435  }
436  }
437 }
438 
439 void FoDapCovJsonTransform::addAxis(string name, string values)
440 {
441  struct Axis *newAxis = new Axis;
442 
443  newAxis->name = name;
444  newAxis->values = values;
445 
446  this->axes.push_back(newAxis);
447  this->axisCount++;
448 }
449 
450 void FoDapCovJsonTransform::addParameter(string id, string name, string type, string dataType, string unit,
451  string longName, string standardName, string shape, string values)
452 {
453  struct Parameter *newParameter = new Parameter;
454 
455  newParameter->id = id;
456  newParameter->name = name;
457  newParameter->type = type;
458  newParameter->dataType = dataType;
459  newParameter->unit = unit;
460  newParameter->longName = longName;
461  newParameter->standardName = standardName;
462  newParameter->shape = shape;
463  newParameter->values = values;
464 
465  this->parameters.push_back(newParameter);
466  this->parameterCount++;
467 }
468 
469 void FoDapCovJsonTransform::getAttributes(ostream *strm, libdap::AttrTable &attr_table, string name,
470  bool *axisRetrieved, bool *parameterRetrieved)
471 {
472  string currAxisName;
473  string currAxisTimeOrigin;
474  string currUnit;
475  string currLongName;
476  string currStandardName;
477 
478  isAxis = false;
479  isParam = false;
480 
481  *axisRetrieved = false;
482  *parameterRetrieved = false;
483 
484  // FOR TESTING AND DEBUGGING PURPOSES
485  //*strm << "\"attr_tableName\": \"" << name << "\"" << endl;
486 
487  // Using CF-1.6 naming conventions -- Also checks for Coads Climatology conventions
488  // http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html
489  if((name.compare("lon") == 0) || (name.compare("LON") == 0)
490  || (name.compare("longitude") == 0) || (name.compare("LONGITUDE") == 0)
491  || (name.compare("COADSX") == 0)) {
492 
493  // FOR TESTING AND DEBUGGING PURPOSES
494  // *strm << "\"Found X-Axis\": \"" << name << "\"" << endl;
495 
496  if(!xExists) {
497  xExists = true;
498  isAxis = true;
499  currAxisName = "x";
500  }
501  }
502  else if((name.compare("lat") == 0) || (name.compare("LAT") == 0)
503  || (name.compare("latitude") == 0) || (name.compare("LATITUDE") == 0)
504  || (name.compare("COADSY") == 0)) {
505 
506  // FOR TESTING AND DEBUGGING PURPOSES
507  // *strm << "\"Found Y-Axis\": \"" << name << "\"" << endl;
508 
509  if(!yExists) {
510  yExists = true;
511  isAxis = true;
512  currAxisName = "y";
513  }
514  }
515  else if((name.compare("lev") == 0) || (name.compare("LEV") == 0)
516  || (name.compare("height") == 0) || (name.compare("HEIGHT") == 0)
517  || (name.compare("depth") == 0) || (name.compare("DEPTH") == 0)
518  || (name.compare("pres") == 0) || (name.compare("PRES") == 0)) {
519 
520  // FOR TESTING AND DEBUGGING PURPOSES
521  // *strm << "\"Found Z-Axis\": \"" << name << "\"" << endl;
522 
523  if(!zExists) {
524  zExists = true;
525  isAxis = true;
526  currAxisName = "z";
527  }
528  }
529  else if((name.compare("time") == 0) || (name.compare("TIME") == 0)) {
530 
531  // FOR TESTING AND DEBUGGING PURPOSES
532  // *strm << "\"Found T-Axis\": \"" << name << "\"" << endl;
533 
534  if(!tExists) {
535  tExists = true;
536  isAxis = true;
537  currAxisName = "t";
538  }
539  }
540  else {
541  isParam = true;
542  }
543 
544  // Only do more if there are actually attributes in the table
545  if(attr_table.get_size() != 0) {
546  libdap::AttrTable::Attr_iter begin = attr_table.attr_begin();
547  libdap::AttrTable::Attr_iter end = attr_table.attr_end();
548 
549  for(libdap::AttrTable::Attr_iter at_iter = begin; at_iter != end; at_iter++) {
550  // FOR TESTING AND DEBUGGING PURPOSES
551  // attr_table.print(*strm);
552 
553  switch (attr_table.get_attr_type(at_iter)) {
554  case libdap::Attr_container: {
555  libdap::AttrTable *atbl = attr_table.get_attr_table(at_iter);
556  // Recursive call for child attribute table
557  getAttributes(strm, *atbl, name, axisRetrieved, parameterRetrieved);
558  break;
559  }
560  default: {
561  vector<string> *values = attr_table.get_attr_vector(at_iter);
562 
563  for(vector<string>::size_type i = 0; i < values->size(); i++) {
564  string currName = attr_table.get_name(at_iter);
565  string currValue = (*values)[i];
566 
567  // FOR TESTING AND DEBUGGING PURPOSES
568  //*strm << "\"currName\": \"" << currName << "\", \"currValue\": \"" << currValue << "\"" << endl;
569 
570  // From Climate and Forecast (CF) Conventions:
571  // http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#_description_of_the_data
572 
573  // We continue to support the use of the units and long_name attributes as defined in COARDS.
574  // We extend COARDS by adding the optional standard_name attribute which is used to provide unique
575  // identifiers for variables. This is important for data exchange since one cannot necessarily
576  // identify a particular variable based on the name assigned to it by the institution that provided
577  // the data.
578 
579  // The standard_name attribute can be used to identify variables that contain coordinate data. But since it is an
580  // optional attribute, applications that implement these standards must continue to be able to identify coordinate
581  // types based on the COARDS conventions.
582 
583  // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
584  if(currName.compare("units") == 0) {
585  currUnit = currValue;
586 
587  if(isAxis) {
588  if(currAxisName.compare("t") == 0) {
589  currAxisTimeOrigin = currValue;
590  }
591  }
592  }
593 
594  // Per Jon Blower:
595  // observedProperty->label comes from:
596  // - The CF long_name, if it exists
597  // - If not, the CF standard_name, perhaps with underscores removed
598  // - If the standard_name doesn’t exist, use the variable ID
599  // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#long-name
600  else if(currName.compare("long_name") == 0) {
601  currLongName = currValue;
602  }
603  // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#standard-name
604  else if(currName.compare("standard_name") == 0) {
605  currStandardName = currValue;
606  }
607  }
608 
609  break;
610  }
611  }
612  }
613  }
614 
615  if(isAxis) {
616  // If we're dealing with the time axis, capture the time origin
617  // timestamp value with the appropriate formatting for printing.
618  // @TODO See https://covjson.org/spec/#temporal-reference-systems
619  if(currAxisName.compare("t") == 0) {
620  addAxis(currAxisName, "\"values\": [\"" + sanitizeTimeOriginString(currAxisTimeOrigin) + "\"]");
621  }
622  else {
623  addAxis(currAxisName, "");
624  }
625 
626  // See https://covjson.org/spec/#projected-coordinate-reference-systems
627  if((currUnit.find("east") != string::npos) || (currUnit.find("East") != string::npos) ||
628  (currUnit.find("north") != string::npos) || (currUnit.find("North") != string::npos)) {
629  coordRefType = "ProjectedCRS";
630  }
631 
632  *axisRetrieved = true;
633  }
634  else if(isParam) {
635  addParameter("", name, "", currDataType, currUnit, currLongName, currStandardName, "", "");
636  *parameterRetrieved = true;
637  }
638  else {
639  // Do nothing
640  }
641 }
642 
643 string FoDapCovJsonTransform::sanitizeTimeOriginString(string timeOrigin)
644 {
645  // If the calendar is based on years, months, days,
646  // then the referenced values SHOULD use one of the
647  // following ISO8601-based lexical representations:
648 
649  // YYYY
650  // ±XYYYY (where X stands for extra year digits)
651  // YYYY-MM
652  // YYYY-MM-DD
653  // YYYY-MM-DDTHH:MM:SS[.F]Z where Z is either “Z”
654  // or a time scale offset + -HH:MM
655  // ex: "2018-01-01T00:12:20Z"
656 
657  // If calendar dates with reduced precision are
658  // used in a lexical representation (e.g. "2016"),
659  // then a client SHOULD interpret those dates in
660  // that reduced precision.
661 
662  // Remove any commonly found words from the origin timestamp
663  vector<string> subStrs = { "hours", "hour", "minutes", "minute",
664  "seconds", "second", "since", " " };
665 
666  string cleanTimeOrigin = timeOrigin;
667 
668  // If base time, use an arbitrary base time string
669  if(timeOrigin.find("base_time") != string::npos) {
670  cleanTimeOrigin = "2020-01-01T12:00:00Z";
671  }
672  else {
673  for(unsigned int i = 0; i < subStrs.size(); i++)
674  focovjson::removeSubstring(cleanTimeOrigin, subStrs[i]);
675  }
676 
677  return cleanTimeOrigin;
678 }
679 
681  _dds(dds), _returnAs(""), _indent_increment(" "), atomicVals(""), currDataType(""), domainType("Unknown"),
682  coordRefType("GeographicCRS"), xExists(false), yExists(false), zExists(false), tExists(false), isParam(false),
683  isAxis(false), canConvertToCovJson(false), axisCount(0), parameterCount(0)
684 {
685  if (!_dds) throw BESInternalError("File out COVJSON, null DDS passed to constructor", __FILE__, __LINE__);
686 }
687 
688 void FoDapCovJsonTransform::dump(ostream &strm) const
689 {
690  strm << BESIndent::LMarg << "FoDapCovJsonTransform::dump - (" << (void *) this << ")" << endl;
691  BESIndent::Indent();
692  if(_dds != 0) {
693  _dds->print(strm);
694  }
695  BESIndent::UnIndent();
696 }
697 
698 void FoDapCovJsonTransform::transform(ostream &ostrm, bool sendData, bool testOverride)
699 {
700  transform(&ostrm, _dds, "", sendData, testOverride);
701 }
702 
703 void FoDapCovJsonTransform::transform(ostream *strm, libdap::Constructor *cnstrctr, string indent, bool sendData)
704 {
705  vector<libdap::BaseType *> leaves;
706  vector<libdap::BaseType *> nodes;
707  // Sort the variables into two sets
708  libdap::DDS::Vars_iter vi = cnstrctr->var_begin();
709  libdap::DDS::Vars_iter ve = cnstrctr->var_end();
710 
711  for(; vi != ve; vi++) {
712  if((*vi)->send_p()) {
713  libdap::BaseType *v = *vi;
714  libdap::Type type = v->type();
715 
716  if(type == libdap::dods_array_c) {
717  type = v->var()->type();
718  }
719  if(v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type())) {
720  nodes.push_back(v);
721  }
722  else {
723  leaves.push_back(v);
724  }
725  }
726  }
727 
728  transformNodeWorker(strm, leaves, nodes, indent, sendData);
729 }
730 
731 void FoDapCovJsonTransform::transformNodeWorker(ostream *strm, vector<libdap::BaseType *> leaves,
732  vector<libdap::BaseType *> nodes, string indent, bool sendData)
733 {
734  // Get this node's leaves
735  for(vector<libdap::BaseType *>::size_type l = 0; l < leaves.size(); l++) {
736  libdap::BaseType *v = leaves[l];
737  BESDEBUG(FoDapCovJsonTransform_debug_key, "Processing LEAF: " << v->name() << endl);
738 
739  // FOR TESTING AND DEBUGGING PURPOSES
740  // *strm << "Processing LEAF: " << v->name() << endl;
741 
742  transform(strm, v, indent + _indent_increment, sendData);
743  }
744 
745  // Get this node's child nodes
746  for(vector<libdap::BaseType *>::size_type n = 0; n < nodes.size(); n++) {
747  libdap::BaseType *v = nodes[n];
748  BESDEBUG(FoDapCovJsonTransform_debug_key, "Processing NODE: " << v->name() << endl);
749 
750  // FOR TESTING AND DEBUGGING PURPOSES
751  // *strm << "Processing NODE: " << v->name() << endl;
752 
753  transform(strm, v, indent + _indent_increment, sendData);
754  }
755 }
756 
757 void FoDapCovJsonTransform::printCoverageJSON(ostream *strm, string indent, bool testOverride)
758 {
759  // Determine if the attribute values we read can be converted to CovJSON.
760  // Test override forces printing output to stream regardless of whether
761  // or not the file can be converted into CoverageJSON format.
762  if(testOverride) {
763  canConvertToCovJson = true;
764  }
765  else {
766  canConvertToCovJson = canConvert();
767  }
768 
769  // Only print if this file can be converted to CovJSON
770  if(canConvertToCovJson) {
771  // Prints the entire Coverage to stream
772  printCoverage(strm, indent);
773  }
774  else {
775  // If this file can't be converted, then its failing spatial/temporal requirements
776  throw BESInternalError("File cannot be converted to CovJSON format due to missing or incompatible spatial dimensions", __FILE__, __LINE__);
777  }
778 }
779 
780 void FoDapCovJsonTransform::printCoverage(ostream *strm, string indent)
781 {
782  string child_indent1 = indent + _indent_increment;
783  string child_indent2 = child_indent1 + _indent_increment;
784 
785  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing COVERAGE" << endl);
786 
787  *strm << indent << "{" << endl;
788  *strm << child_indent1 << "\"type\": \"Coverage\"," << endl;
789 
790  printDomain(strm, child_indent1);
791 
792  printParameters(strm, child_indent1);
793 
794  printRanges(strm, child_indent1);
795 
796  *strm << indent << "}" << endl;
797 }
798 
799 void FoDapCovJsonTransform::printDomain(ostream *strm, string indent)
800 {
801  string child_indent1 = indent + _indent_increment;
802 
803  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing DOMAIN" << endl);
804 
805  *strm << indent << "\"domain\": {" << endl;
806  *strm << child_indent1 << "\"type\" : \"Domain\"," << endl;
807  *strm << child_indent1 << "\"domainType\": \"" + domainType + "\"," << endl;
808 
809  // Prints the axes metadata and range values
810  printAxes(strm, child_indent1);
811 
812  // Prints the references for the given Axes
813  printReference(strm, child_indent1);
814 
815  *strm << indent << "}," << endl;
816 }
817 
818 void FoDapCovJsonTransform::printAxes(ostream *strm, string indent)
819 {
820  string child_indent1 = indent + _indent_increment;
821  string child_indent2 = child_indent1 + _indent_increment;
822 
823  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing AXES" << endl);
824 
825  // FOR TESTING AND DEBUGGING PURPOSES
826  // *strm << "\"type_name\": \"" << a->var()->type_name() << "\"" << endl;
827 
828  // Write the axes to strm
829  *strm << indent << "\"axes\": {" << endl;
830  for(unsigned int i = 0; i < axisCount; i++) {
831  for(unsigned int j = 0; j < axisCount; j++) {
832  // Logic for printing axes in the appropriate order
833 
834  // If x, y, z, and t all exist (x, y, z, t)
835  if(xExists && yExists && zExists && tExists) {
836  if((i == 0) && (axes[j]->name.compare("x") == 0)) {
837  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
838  *strm << child_indent2 << axes[j]->values << endl;
839  }
840  else if((i == 1) && (axes[j]->name.compare("y") == 0)) {
841  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
842  *strm << child_indent2 << axes[j]->values << endl;
843  }
844  else if((i == 2) && (axes[j]->name.compare("z") == 0)) {
845  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
846  *strm << child_indent2 << axes[j]->values << endl;
847  }
848  else if((i == 3) && (axes[j]->name.compare("t") == 0)) {
849  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
850  *strm << child_indent2 << axes[j]->values << endl;
851  }
852  }
853  // If just x, y, and t exist (x, y, t)
854  else if(xExists && yExists && !zExists && tExists) {
855  if((i == 0) && (axes[j]->name.compare("x") == 0)) {
856  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
857  *strm << child_indent2 << axes[j]->values << endl;
858  }
859  else if((i == 1) && (axes[j]->name.compare("y") == 0)) {
860  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
861  *strm << child_indent2 << axes[j]->values << endl;
862  }
863  else if((i == 2) && (axes[j]->name.compare("t") == 0)) {
864  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
865  *strm << child_indent2 << axes[j]->values << endl;
866  }
867  }
868  // If just x and y exist (x, y)
869  else if(xExists && yExists && !zExists && !tExists) {
870  if((i == 0) && (axes[j]->name.compare("x") == 0)) {
871  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
872  *strm << child_indent2 << axes[j]->values << endl;
873  }
874  else if((i == 1) && (axes[j]->name.compare("y") == 0)) {
875  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
876  *strm << child_indent2 << axes[j]->values << endl;
877  }
878  }
879  }
880  if(i == axisCount - 1) {
881  *strm << child_indent1 << "}" << endl;
882  }
883  else {
884  *strm << child_indent1 << "}," << endl;
885  }
886  }
887  *strm << indent << "}," << endl;
888 }
889 
890 void FoDapCovJsonTransform::printReference(ostream *strm, string indent)
891 {
892  string child_indent1 = indent + _indent_increment;
893  string child_indent2 = child_indent1 + _indent_increment;
894  string coordVars;
895 
896  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing REFERENCES" << endl);
897 
898  if(xExists) {
899  coordVars += "\"x\"";
900  }
901 
902  if(yExists) {
903  if(coordVars.length() > 0) {
904  coordVars += ", ";
905  }
906  coordVars += "\"y\"";
907  }
908 
909  if(zExists) {
910  if(coordVars.length() > 0) {
911  coordVars += ", ";
912  }
913  coordVars += "\"z\"";
914  }
915 
916  *strm << indent << "\"referencing\": [{" << endl;
917 
918  // See https://covjson.org/spec/#temporal-reference-systems
919  if(tExists) {
920  *strm << child_indent1 << "\"coordinates\": [\"t\"]," << endl;
921  *strm << child_indent1 << "\"system\": {" << endl;
922  *strm << child_indent2 << "\"type\": \"TemporalRS\"," << endl;
923  *strm << child_indent2 << "\"calendar\": \"Gregorian\"" << endl;
924  *strm << child_indent1 << "}" << endl;
925  *strm << indent << "}," << endl;
926  *strm << indent << "{" << endl;
927  }
928 
929  // See https://covjson.org/spec/#geospatial-coordinate-reference-systems
930  *strm << child_indent1 << "\"coordinates\": [" << coordVars << "]," << endl;
931  *strm << child_indent1 << "\"system\": {" << endl;
932  *strm << child_indent2 << "\"type\": \"" + coordRefType + "\"," << endl;
933 
934  // Most of the datasets I've seen do not contain a link to a coordinate
935  // reference system, so I've set some defaults here - CRH 1/2020
936  if(coordRefType.compare("ProjectedCRS") == 0) {
937  // Projected Coordinate Reference System (north/east): http://www.opengis.net/def/crs/EPSG/0/27700
938  *strm << child_indent2 << "\"id\": \"http://www.opengis.net/def/crs/EPSG/0/27700\"" << endl;
939  }
940  else {
941  if(xExists && yExists && zExists) {
942  // 3-Dimensional Geographic Coordinate Reference System (lat/lon/height): http://www.opengis.net/def/crs/EPSG/0/4979
943  *strm << child_indent2 << "\"id\": \"http://www.opengis.net/def/crs/EPSG/0/4979\"" << endl;
944  }
945  else {
946  // 2-Dimensional Geographic Coordinate Reference System (lat/lon): http://www.opengis.net/def/crs/OGC/1.3/CRS84
947  *strm << child_indent2 << "\"id\": \"http://www.opengis.net/def/crs/OGC/1.3/CRS84\"" << endl;
948  }
949  }
950 
951  *strm << child_indent1 << "}" << endl;
952  *strm << indent << "}]" << endl;
953 }
954 
955 void FoDapCovJsonTransform::printParameters(ostream *strm, string indent)
956 {
957  string child_indent1 = indent + _indent_increment;
958  string child_indent2 = child_indent1 + _indent_increment;
959  string child_indent3 = child_indent2 + _indent_increment;
960  string child_indent4 = child_indent3 + _indent_increment;
961 
962  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing PARAMETERS" << endl);
963 
964  // Write down the parameter metadata
965  *strm << indent << "\"parameters\": {" << endl;
966  for(unsigned int i = 0; i < parameterCount; i++) {
967  *strm << child_indent1 << "\"" << parameters[i]->name << "\": {" << endl;
968  *strm << child_indent2 << "\"type\": \"Parameter\"," << endl;
969  *strm << child_indent2 << "\"description\": {" << endl;
970 
971  if(parameters[i]->longName.compare("") != 0) {
972  *strm << child_indent3 << "\"en\": \"" << parameters[i]->longName << "\"" << endl;
973  }
974  else if(parameters[i]->standardName.compare("") != 0) {
975  *strm << child_indent3 << "\"en\": \"" << parameters[i]->standardName << "\"" << endl;
976  }
977  else {
978  *strm << child_indent3 << "\"en\": \"" << parameters[i]->name << "\"" << endl;
979  }
980 
981  *strm << child_indent2 << "}," << endl;
982  *strm << child_indent2 << "\"unit\": {" << endl;
983  *strm << child_indent3 << "\"label\": {" << endl;
984  *strm << child_indent4 << "\"en\": \"" << parameters[i]->unit << "\"" << endl;
985  *strm << child_indent3 << "}," << endl;
986  *strm << child_indent3 << "\"symbol\": {" << endl;
987  *strm << child_indent4 << "\"value\": \"" << parameters[i]->unit << "\"," << endl;
988  *strm << child_indent4 << "\"type\": \"http://www.opengis.net/def/uom/UCUM/\"" << endl;
989  *strm << child_indent3 << "}" << endl;
990  *strm << child_indent2 << "}," << endl;
991  *strm << child_indent2 << "\"observedProperty\": {" << endl;
992 
993  // Per Jon Blower:
994  // observedProperty->id comes from the CF standard_name,
995  // mapped to a URI like this: http://vocab.nerc.ac.uk/standard_name/<standard_name>.
996  // If the standard_name is not present, omit the id.
997  if(parameters[i]->standardName.compare("") != 0) {
998  *strm << child_indent3 << "\"id\": \"http://vocab.nerc.ac.uk/standard_name/" << parameters[i]->standardName << "/\"," << endl;
999  }
1000 
1001  // Per Jon Blower:
1002  // observedProperty->label comes from:
1003  // - The CF long_name, if it exists
1004  // - If not, the CF standard_name, perhaps with underscores removed
1005  // - If the standard_name doesn’t exist, use the variable ID
1006  *strm << child_indent3 << "\"label\": {" << endl;
1007 
1008  if(parameters[i]->longName.compare("") != 0) {
1009  *strm << child_indent4 << "\"en\": \"" << parameters[i]->longName << "\"" << endl;
1010  }
1011  else if(parameters[i]->standardName.compare("") != 0) {
1012  *strm << child_indent4 << "\"en\": \"" << parameters[i]->standardName << "\"" << endl;
1013  }
1014  else {
1015  *strm << child_indent4 << "\"en\": \"" << parameters[i]->name << "\"" << endl;
1016  }
1017 
1018  *strm << child_indent3 << "}" << endl;
1019  *strm << child_indent2 << "}" << endl;
1020 
1021  if(i == parameterCount - 1) {
1022  *strm << child_indent1 << "}" << endl;
1023  }
1024  else {
1025  *strm << child_indent1 << "}," << endl;
1026  }
1027  }
1028 
1029  *strm << indent << "}," << endl;
1030 }
1031 
1032 void FoDapCovJsonTransform::printRanges(ostream *strm, string indent)
1033 {
1034  string child_indent1 = indent + _indent_increment;
1035  string child_indent2 = child_indent1 + _indent_increment;
1036  string child_indent3 = child_indent2 + _indent_increment;
1037  string axisNames;
1038 
1039  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing RANGES" << endl);
1040 
1041  if(tExists) {
1042  axisNames += "\"t\"";
1043  }
1044 
1045  if(zExists) {
1046  if(axisNames.length() > 0) {
1047  axisNames += ", ";
1048  }
1049  axisNames += "\"z\"";
1050  }
1051 
1052  if(yExists) {
1053  if(axisNames.length() > 0) {
1054  axisNames += ", ";
1055  }
1056  axisNames += "\"y\"";
1057  }
1058 
1059  if(xExists) {
1060  if(axisNames.length() > 0) {
1061  axisNames += ", ";
1062  }
1063  axisNames += "\"x\"";
1064  }
1065 
1066  // Axis name (x, y, or z)
1067  *strm << indent << "\"ranges\": {" << endl;
1068  for(unsigned int i = 0; i < parameterCount; i++) {
1069  string dataType;
1070  // See spec: https://covjson.org/spec/#ndarray-objects
1071  if(parameters[i]->dataType.find("int") == 0 || parameters[i]->dataType.find("Int") == 0
1072  || parameters[i]->dataType.find("integer") == 0 || parameters[i]->dataType.find("Integer") == 0) {
1073  dataType = "integer";
1074  }
1075  else if(parameters[i]->dataType.find("float") == 0 || parameters[i]->dataType.find("Float") == 0) {
1076  dataType = "float";
1077  }
1078  else if(parameters[i]->dataType.find("string") == 0 || parameters[i]->dataType.find("String") == 0) {
1079  dataType = "string";
1080  }
1081  else {
1082  dataType = "string";
1083  }
1084 
1085  // @TODO NEEDS REFACTORING FOR BES ISSUE #244
1086  // https://github.com/OPENDAP/bes/issues/244
1087  *strm << child_indent1 << "\"" << parameters[i]->name << "\": {" << endl;
1088  *strm << child_indent2 << "\"type\": \"NdArray\"," << endl;
1089  *strm << child_indent2 << "\"dataType\": \"" << dataType << "\", " << endl;
1090  *strm << child_indent2 << "\"axisNames\": [" << axisNames << "]," << endl;
1091  *strm << child_indent2 << parameters[i]->shape << endl;
1092  *strm << child_indent2 << parameters[i]->values << endl;
1093 
1094  if(i == parameterCount - 1) {
1095  *strm << child_indent1 << "}" << endl;
1096  }
1097  else {
1098  *strm << child_indent1 << "}," << endl;
1099  }
1100  }
1101 
1102  *strm << indent << "}" << endl;
1103 }
1104 
1105 void FoDapCovJsonTransform::transform(ostream *strm, libdap::DDS *dds, string indent, bool sendData, bool testOverride)
1106 {
1107  // Sort the variables into two sets
1108  vector<libdap::BaseType *> leaves;
1109  vector<libdap::BaseType *> nodes;
1110 
1111  libdap::DDS::Vars_iter vi = dds->var_begin();
1112  libdap::DDS::Vars_iter ve = dds->var_end();
1113  for(; vi != ve; vi++) {
1114  if((*vi)->send_p()) {
1115  libdap::BaseType *v = *vi;
1116  libdap::Type type = v->type();
1117  if(type == libdap::dods_array_c) {
1118  type = v->var()->type();
1119  }
1120  if(v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type())) {
1121  nodes.push_back(v);
1122  }
1123  else {
1124  leaves.push_back(v);
1125  }
1126  }
1127  }
1128 
1129  // Read through the source DDS leaves and nodes, extract all axes and
1130  // parameter data, and store that data as Axis and Parameters
1131  transformNodeWorker(strm, leaves, nodes, indent + _indent_increment + _indent_increment, sendData);
1132 
1133  // Print the Coverage data to stream as CoverageJSON
1134  printCoverageJSON(strm, indent, testOverride);
1135 }
1136 
1137 void FoDapCovJsonTransform::transform(ostream *strm, libdap::BaseType *bt, string indent, bool sendData)
1138 {
1139  switch(bt->type()) {
1140  // Handle the atomic types - that's easy!
1141  case libdap::dods_byte_c:
1142  case libdap::dods_int16_c:
1143  case libdap::dods_uint16_c:
1144  case libdap::dods_int32_c:
1145  case libdap::dods_uint32_c:
1146  case libdap::dods_float32_c:
1147  case libdap::dods_float64_c:
1148  case libdap::dods_str_c:
1149  case libdap::dods_url_c:
1150  transformAtomic(bt, indent, sendData);
1151  break;
1152 
1153  case libdap::dods_structure_c:
1154  transform(strm, (libdap::Structure *) bt, indent, sendData);
1155  break;
1156 
1157  case libdap::dods_grid_c:
1158  transform(strm, (libdap::Grid *) bt, indent, sendData);
1159  break;
1160 
1161  case libdap::dods_sequence_c:
1162  transform(strm, (libdap::Sequence *) bt, indent, sendData);
1163  break;
1164 
1165  case libdap::dods_array_c:
1166  transform(strm, (libdap::Array *) bt, indent, sendData);
1167  break;
1168 
1169  case libdap::dods_int8_c:
1170  case libdap::dods_uint8_c:
1171  case libdap::dods_int64_c:
1172  case libdap::dods_uint64_c:
1173  case libdap::dods_enum_c:
1174  case libdap::dods_group_c: {
1175  string s = (string) "File out COVJSON, DAP4 types not yet supported.";
1176  throw BESInternalError(s, __FILE__, __LINE__);
1177  break;
1178  }
1179 
1180  default: {
1181  string s = (string) "File out COVJSON, Unrecognized type.";
1182  throw BESInternalError(s, __FILE__, __LINE__);
1183  break;
1184  }
1185  }
1186 }
1187 
1188 void FoDapCovJsonTransform::transformAtomic(libdap::BaseType *b, string indent, bool sendData)
1189 {
1190  string childindent = indent + _indent_increment;
1191  struct Axis *newAxis = new Axis;
1192 
1193  newAxis->name = "test";
1194  if(sendData) {
1195  newAxis->values += "\"values\": [";
1196  if(b->type() == libdap::dods_str_c || b->type() == libdap::dods_url_c) {
1197  libdap::Str *strVar = (libdap::Str *) b;
1198  string tmpString = strVar->value();
1199  newAxis->values += "\"";
1200  newAxis->values += focovjson::escape_for_covjson(tmpString);
1201  newAxis->values += "\"";
1202  }
1203  else {
1204  ostringstream otemp;
1205  istringstream itemp;
1206  int tempVal = 0;
1207  b->print_val(otemp, "", false);
1208  istringstream (otemp.str());
1209  istringstream (otemp.str()) >> tempVal;
1210  newAxis->values += otemp.str();
1211  }
1212  newAxis->values += "]";
1213  }
1214  else {
1215  newAxis->values += "\"values\": []";
1216  }
1217 
1218  axes.push_back(newAxis);
1219  axisCount++;
1220 }
1221 
1222 void FoDapCovJsonTransform::transform(ostream *strm, libdap::Array *a, string indent, bool sendData)
1223 {
1224  BESDEBUG(FoDapCovJsonTransform_debug_key,
1225  "FoCovJsonTransform::transform() - Processing Array. " << " a->type(): " << a->type() << " a->var()->type(): " << a->var()->type() << endl);
1226 
1227  switch(a->var()->type()) {
1228  // Handle the atomic types - that's easy!
1229  case libdap::dods_byte_c:
1230  covjsonSimpleTypeArray<libdap::dods_byte>(strm, a, indent, sendData);
1231  break;
1232 
1233  case libdap::dods_int16_c:
1234  covjsonSimpleTypeArray<libdap::dods_int16>(strm, a, indent, sendData);
1235  break;
1236 
1237  case libdap::dods_uint16_c:
1238  covjsonSimpleTypeArray<libdap::dods_uint16>(strm, a, indent, sendData);
1239  break;
1240 
1241  case libdap::dods_int32_c:
1242  covjsonSimpleTypeArray<libdap::dods_int32>(strm, a, indent, sendData);
1243  break;
1244 
1245  case libdap::dods_uint32_c:
1246  covjsonSimpleTypeArray<libdap::dods_uint32>(strm, a, indent, sendData);
1247  break;
1248 
1249  case libdap::dods_float32_c:
1250  covjsonSimpleTypeArray<libdap::dods_float32>(strm, a, indent, sendData);
1251  break;
1252 
1253  case libdap::dods_float64_c:
1254  covjsonSimpleTypeArray<libdap::dods_float64>(strm, a, indent, sendData);
1255  break;
1256 
1257  case libdap::dods_str_c: {
1258  covjsonStringArray(strm, a, indent, sendData);
1259  break;
1260  }
1261 
1262  case libdap::dods_url_c: {
1263  covjsonStringArray(strm, a, indent, sendData);
1264  break;
1265  }
1266 
1267  case libdap::dods_structure_c:
1268  throw BESInternalError("File out COVJSON, Arrays of Structure objects not a supported return type.", __FILE__, __LINE__);
1269 
1270  case libdap::dods_grid_c:
1271  throw BESInternalError("File out COVJSON, Arrays of Grid objects not a supported return type.", __FILE__, __LINE__);
1272 
1273  case libdap::dods_sequence_c:
1274  throw BESInternalError("File out COVJSON, Arrays of Sequence objects not a supported return type.", __FILE__, __LINE__);
1275 
1276  case libdap::dods_array_c:
1277  throw BESInternalError("File out COVJSON, Arrays of Array objects not a supported return type.", __FILE__, __LINE__);
1278 
1279  case libdap::dods_int8_c:
1280  case libdap::dods_uint8_c:
1281  case libdap::dods_int64_c:
1282  case libdap::dods_uint64_c:
1283  case libdap::dods_enum_c:
1284  case libdap::dods_group_c:
1285  throw BESInternalError("File out COVJSON, DAP4 types not yet supported.", __FILE__, __LINE__);
1286 
1287  default:
1288  throw BESInternalError("File out COVJSON, Unrecognized type.", __FILE__, __LINE__);
1289  }
1290 }
FoDapCovJsonTransform::FoDapCovJsonTransform
FoDapCovJsonTransform(libdap::DDS *dds)
Get the CovJSON encoding for a DDS.
Definition: FoDapCovJsonTransform.cc:680
Type
Type
Type of JSON value.
Definition: cmr_module/rapidjson/rapidjson.h:603
FoDapCovJsonTransform::dump
virtual void dump(std::ostream &strm) const
Dumps information about this transformation object for debugging purposes.
Definition: FoDapCovJsonTransform.cc:688
BESInternalError
exception thrown if internal error encountered
Definition: BESInternalError.h:43