bes  Updated for version 3.20.5
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 
75 bool FoDapCovJsonTransform::canConvert()
76 {
77  // If x, y, z, and t all exist
78  // We are assuming the following is true:
79  // - shapeVals[0] = x axis
80  // - shapeVals[1] = y axis
81  // - shapeVals[2] = z axis
82  // - shapeVals[3] = t axis
83  if(xExists && yExists && zExists && tExists) {
84 
85  if (shapeVals.size() < 4)
86  return false;
87 
88  // A domain with Grid domain type MUST have the axes "x" and "y"
89  // and MAY have the axes "z" and "t".
90  if((shapeVals[0] > 1) && (shapeVals[1] > 1) && (shapeVals[2] >= 1) && (shapeVals[3] >= 0)) {
91  domainType = Grid;
92  return true;
93  }
94 
95  // A domain with VerticalProfile domain type MUST have the axes "x",
96  // "y", and "z", where "x" and "y" MUST have a single coordinate only.
97  else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] >= 1) && ((shapeVals[3] <= 1) && (shapeVals[3] >= 0))) {
98  domainType = VerticalProfile;
99  return true;
100  }
101 
102  // A domain with PointSeries domain type MUST have the axes "x", "y",
103  // and "t" where "x" and "y" MUST have a single coordinate only. A
104  // domain with PointSeries domain type MAY have the axis "z" which
105  // MUST have a single coordinate only.
106  else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] == 1) && (shapeVals[3] >= 0)) {
107  domainType = PointSeries;
108  return true;
109  }
110 
111  // A domain with Point domain type MUST have the axes "x" and "y" and MAY
112  // have the axes "z" and "t" where all MUST have a single coordinate only.
113  else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] == 1) && (shapeVals[3] == 1)) {
114  domainType = Point;
115  return true;
116  }
117  }
118 
119  // If just x, y, and t exist
120  // We are assuming the following is true:
121  // - shapeVals[0] = x axis
122  // - shapeVals[1] = y axis
123  // - shapeVals[2] = t axis
124  else if(xExists && yExists && !zExists && tExists) {
125 
126  if (shapeVals.size() < 3)
127  return false;
128 
129  // A domain with Grid domain type MUST have the axes "x" and "y"
130  // and MAY have the axes "z" and "t".
131  if((shapeVals[0] > 1) && (shapeVals[1] > 1) && (shapeVals[2] >= 0)) {
132  domainType = Grid;
133  return true;
134  }
135 
136  // A domain with PointSeries domain type MUST have the axes "x", "y",
137  // and "t" where "x" and "y" MUST have a single coordinate only. A
138  // domain with PointSeries domain type MAY have the axis "z" which
139  // MUST have a single coordinate only.
140  else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] >= 0)) {
141  domainType = PointSeries;
142  return true;
143  }
144 
145  // A domain with Point domain type MUST have the axes "x" and "y" and MAY
146  // have the axes "z" and "t" where all MUST have a single coordinate only.
147  else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] == 1)) {
148  domainType = Point;
149  return true;
150  }
151  }
152 
153  // If just x and y exist
154  // We are assuming the following is true:
155  // - shapeVals[0] = x axis
156  // - shapeVals[1] = y axis
157  else if(xExists && yExists && !zExists && !tExists) {
158 
159  if (shapeVals.size() < 2)
160  return false;
161 
162  // A domain with Grid domain type MUST have the axes "x" and "y"
163  // and MAY have the axes "z" and "t".
164  if((shapeVals[0] > 1) && (shapeVals[1] > 1)) {
165  domainType = Grid;
166  return true;
167  }
168 
169  // A domain with Point domain type MUST have the axes "x" and "y" and MAY
170  // have the axes "z" and "t" where all MUST have a single coordinate only.
171  else if((shapeVals[0] == 1) && (shapeVals[1] == 1)) {
172  domainType = Point;
173  return true;
174  }
175  }
176  return false; // This source DDS is not valid as CovJSON
177 }
178 
179 
192 template<typename T>
193 unsigned int FoDapCovJsonTransform::covjsonSimpleTypeArrayWorker(ostream *strm, T *values, unsigned int indx,
194  vector<unsigned int> *shape, unsigned int currentDim)
195 {
196  unsigned int currentDimSize = (*shape)[currentDim];
197 
198  // FOR TESTING AND DEBUGGING PURPOSES
199  // *strm << "\"currentDim\": \"" << currentDim << "\"" << endl;
200  // *strm << "\"currentDimSize\": \"" << currentDimSize << "\"" << endl;
201 
202  *strm << "[";
203  for(unsigned int i = 0; i < currentDimSize; i++) {
204  if(currentDim < shape->size() - 1) {
205  BESDEBUG(FoDapCovJsonTransform_debug_key,
206  "covjsonSimpleTypeArrayWorker() - Recursing! indx: " << indx << " currentDim: " << currentDim << " currentDimSize: " << currentDimSize << endl);
207  indx = covjsonSimpleTypeArrayWorker<T>(strm, values, indx, shape, currentDim + 1);
208  if(i + 1 != currentDimSize) {
209  *strm << ", ";
210  }
211  }
212  else {
213  if(i) {
214  *strm << ", ";
215  }
216  if(typeid(T) == typeid(string)) {
217  // Strings need to be escaped to be included in a CovJSON object.
218  string val = reinterpret_cast<string*>(values)[indx++];
219  *strm << "\"" << focovjson::escape_for_covjson(val) << "\"";
220  }
221  else {
222  *strm << values[indx++];
223  }
224  }
225  }
226  *strm << "]";
227 
228  return indx;
229 }
230 
231 
249 template<typename T>
250 void FoDapCovJsonTransform::covjsonSimpleTypeArray(ostream *strm, libdap::Array *a, string indent, bool sendData)
251 {
252  string childindent = indent + _indent_increment;
253  bool axisRetrieved = false;
254  bool parameterRetrieved = false;
255 
256  currDataType = a->var()->type_name();
257 
258  // FOR TESTING AND DEBUGGING PURPOSES
259  // *strm << "\"type_name\": \"" << a->var()->type_name() << "\"" << endl;
260 
261  getAttributes(strm, a->get_attr_table(), a->name(), &axisRetrieved, &parameterRetrieved);
262 
263  // a->print_val(*strm, "\n", true); // For testing purposes
264 
265  // sendData = false; // For testing purposes
266 
267  // If we are dealing with an Axis
268  if((axisRetrieved == true) && (parameterRetrieved == false)) {
269  struct Axis *currAxis;
270  currAxis = axes[axisCount - 1];
271 
272  int numDim = a->dimensions(true);
273  vector<unsigned int> shape(numDim);
274  long length = focovjson::computeConstrainedShape(a, &shape);
275 
276  // FOR TESTING AND DEBUGGING PURPOSES
277  // *strm << "\"numDimensions\": \"" << numDim << "\"" << endl;
278  // *strm << "\"length\": \"" << length << "\"" << endl << endl;
279 
280  if (currAxis->name.compare("t") != 0) {
281  if (sendData) {
282  currAxis->values += "\"values\": ";
283  unsigned int indx = 0;
284  vector<T> src(length);
285  a->value(&src[0]);
286 
287  ostringstream astrm;
288  indx = covjsonSimpleTypeArrayWorker(&astrm, &src[0], 0, &shape, 0);
289  currAxis->values += astrm.str();
290 
291  if (length != indx) {
292  BESDEBUG(FoDapCovJsonTransform_debug_key,
293  "covjsonSimpleTypeArray(Axis) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
294  }
295  assert(length == indx);
296  }
297  else {
298  currAxis->values += "\"values\": []";
299  }
300  }
301  }
302 
303  // If we are dealing with a Parameter
304  else if(axisRetrieved == false && parameterRetrieved == true) {
305  struct Parameter *currParameter;
306  currParameter = parameters[parameterCount - 1];
307 
308  int numDim = a->dimensions(true);
309  vector<unsigned int> shape(numDim);
310  long length = focovjson::computeConstrainedShape(a, &shape);
311 
312  // FOR TESTING AND DEBUGGING PURPOSES
313  // *strm << "\"numDimensions\": \"" << a->dimensions(true) << "\"" << endl;
314  // *strm << "\"length\": \"" << length << "\"" << endl << endl;
315 
316  currParameter->shape += "\"shape\": [";
317  for(vector<unsigned int>::size_type i = 0; i < shape.size(); i++) {
318  if(i > 0) {
319  currParameter->shape += ", ";
320  }
321 
322  // Process the shape's values, which are strings,
323  // convert them into integers, and store them
324  ostringstream otemp;
325  istringstream itemp;
326  int tempVal = 0;
327  otemp << shape[i];
328  istringstream (otemp.str());
329  istringstream (otemp.str()) >> tempVal;
330  shapeVals.push_back(tempVal);
331 
332  // t may only have 1 value: the origin timestamp
333  // DANGER: t may not yet be defined
334  if((i == 0) && tExists) {
335  currParameter->shape += "1";
336  }
337  else {
338  currParameter->shape += otemp.str();
339  }
340  }
341  currParameter->shape += "],";
342 
343  if (sendData) {
344  currParameter->values += "\"values\": ";
345  unsigned int indx = 0;
346  vector<T> src(length);
347  a->value(&src[0]);
348 
349  ostringstream pstrm;
350  indx = covjsonSimpleTypeArrayWorker(&pstrm, &src[0], 0, &shape, 0);
351  currParameter->values += pstrm.str();
352 
353  if (length != indx) {
354  BESDEBUG(FoDapCovJsonTransform_debug_key,
355  "covjsonSimpleTypeArray(Parameter) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
356  }
357  assert(length == indx);
358  }
359  else {
360  currParameter->values += "\"values\": []";
361  }
362  }
363 }
364 
365 
386 void FoDapCovJsonTransform::covjsonStringArray(ostream *strm, libdap::Array *a, string indent, bool sendData)
387 {
388  string childindent = indent + _indent_increment;
389  bool axisRetrieved = false;
390  bool parameterRetrieved = false;
391 
392  currDataType = a->var()->type_name();
393 
394  // FOR TESTING AND DEBUGGING PURPOSES
395  // *strm << "\"attr_tableName\": \"" << a->name() << "\"" << endl;
396 
397  // FOR TESTING AND DEBUGGING PURPOSES
398  // *strm << "\"type_name\": \"" << a->var()->type_name() << "\"" << endl;
399 
400  getAttributes(strm, a->get_attr_table(), a->name(), &axisRetrieved, &parameterRetrieved);
401 
402  // a->print_val(*strm, "\n", true); // For testing purposes
403 
404  // sendData = false; // For testing purposes
405 
406  // If we are dealing with an Axis
407  if((axisRetrieved == true) && (parameterRetrieved == false)) {
408  struct Axis *currAxis;
409  currAxis = axes[axisCount - 1];
410 
411  int numDim = a->dimensions(true);
412  vector<unsigned int> shape(numDim);
413  long length = focovjson::computeConstrainedShape(a, &shape);
414 
415  if (currAxis->name.compare("t") != 0) {
416  if (sendData) {
417  currAxis->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 astrm;
424  indx = covjsonSimpleTypeArrayWorker(&astrm, (string *) (&sourceValues[0]), 0, &shape, 0);
425  currAxis->values += astrm.str();
426 
427  if (length != indx) {
428  BESDEBUG(FoDapCovJsonTransform_debug_key,
429  "covjsonStringArray(Axis) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
430  }
431  assert(length == indx);
432  }
433  else {
434  currAxis->values += "\"values\": []";
435  }
436  }
437  }
438 
439  // If we are dealing with a Parameter
440  else if(axisRetrieved == false && parameterRetrieved == true) {
441  struct Parameter *currParameter;
442  currParameter = parameters[parameterCount - 1];
443 
444  int numDim = a->dimensions(true);
445  vector<unsigned int> shape(numDim);
446  long length = focovjson::computeConstrainedShape(a, &shape);
447 
448  currParameter->shape += "\"shape\": [";
449  for(vector<unsigned int>::size_type i = 0; i < shape.size(); i++) {
450  if(i > 0) {
451  currParameter->shape += ", ";
452  }
453 
454  // Process the shape's values, which are strings,
455  // convert them into integers, and store them
456  ostringstream otemp;
457  istringstream itemp;
458  int tempVal = 0;
459  otemp << shape[i];
460  istringstream (otemp.str());
461  istringstream (otemp.str()) >> tempVal;
462  shapeVals.push_back(tempVal);
463 
464  // t may only have 1 value: the origin timestamp
465  // DANGER: t may not yet be defined
466  if((i == 0) && tExists) {
467  currParameter->shape += "1";
468  }
469  else {
470  currParameter->shape += otemp.str();
471  }
472  }
473  currParameter->shape += "],";
474 
475  if (sendData) {
476  currParameter->values += "\"values\": ";
477  unsigned int indx = 0;
478  // The string type utilizes a specialized version of libdap:Array.value()
479  vector<string> sourceValues;
480  a->value(sourceValues);
481 
482  ostringstream pstrm;
483  indx = covjsonSimpleTypeArrayWorker(&pstrm, (string *) (&sourceValues[0]), 0, &shape, 0);
484  currParameter->values += pstrm.str();
485 
486  if (length != indx) {
487  BESDEBUG(FoDapCovJsonTransform_debug_key,
488  "covjsonStringArray(Parameter) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
489  }
490  assert(length == indx);
491  }
492  else {
493  currParameter->values += "\"values\": []";
494  }
495  }
496 }
497 
498 
520 void FoDapCovJsonTransform::getAttributes(ostream *strm, libdap::AttrTable &attr_table, string name,
521  bool *axisRetrieved, bool *parameterRetrieved)
522 {
523  string currAxisName;
524  string currAxisTimeOrigin;
525  string currUnit;
526  string currLongName;
527  string currStandardName;
528 
529  isAxis = false;
530  isParam = false;
531 
532  // FOR TESTING AND DEBUGGING PURPOSES
533  // *strm << "\"attr_tableName\": \"" << name << "\"" << endl;
534 
535  // Using CF-1.6 naming conventions -- Also checks for Coads Climatology conventions
536  // http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html
537  if((name.compare("lon") == 0) || (name.compare("LON") == 0)
538  || (name.compare("longitude") == 0) || (name.compare("LONGITUDE") == 0)
539  || (name.compare("COADSX") == 0)) {
540 
541  // FOR TESTING AND DEBUGGING PURPOSES
542  // *strm << "\"Found X-Axis\": \"" << name << "\"" << endl;
543 
544  if(!xExists) {
545  xExists = true;
546  isAxis = true;
547  currAxisName = "x";
548  }
549  }
550  else if((name.compare("lat") == 0) || (name.compare("LAT") == 0)
551  || (name.compare("latitude") == 0) || (name.compare("LATITUDE") == 0)
552  || (name.compare("COADSY") == 0)) {
553 
554  // FOR TESTING AND DEBUGGING PURPOSES
555  // *strm << "\"Found Y-Axis\": \"" << name << "\"" << endl;
556 
557  if(!yExists) {
558  yExists = true;
559  isAxis = true;
560  currAxisName = "y";
561  }
562  }
563  else if((name.compare("lev") == 0) || (name.compare("LEV") == 0)
564  || (name.compare("height") == 0) || (name.compare("HEIGHT") == 0)
565  || (name.compare("depth") == 0) || (name.compare("DEPTH") == 0)
566  || (name.compare("pres") == 0) || (name.compare("PRES") == 0)) {
567 
568  // FOR TESTING AND DEBUGGING PURPOSES
569  // *strm << "\"Found Z-Axis\": \"" << name << "\"" << endl;
570 
571  if(!zExists) {
572  zExists = true;
573  isAxis = true;
574  currAxisName = "z";
575  }
576  }
577  else if((name.compare("time") == 0) || (name.compare("TIME") == 0)) {
578 
579  // FOR TESTING AND DEBUGGING PURPOSES
580  // *strm << "\"Found T-Axis\": \"" << name << "\"" << endl;
581 
582  if(!tExists) {
583  tExists = true;
584  isAxis = true;
585  currAxisName = "t";
586  }
587  }
588  else {
589  isParam = true;
590  }
591 
592  // Only do more if there are actually attributes in the table
593  if(attr_table.get_size() != 0) {
594  libdap::AttrTable::Attr_iter begin = attr_table.attr_begin();
595  libdap::AttrTable::Attr_iter end = attr_table.attr_end();
596 
597  for(libdap::AttrTable::Attr_iter at_iter = begin; at_iter != end; at_iter++) {
598  switch (attr_table.get_attr_type(at_iter)) {
599  case libdap::Attr_container: {
600  libdap::AttrTable *atbl = attr_table.get_attr_table(at_iter);
601  // Recursive call for child attribute table
602  getAttributes(strm, *atbl, name, axisRetrieved, parameterRetrieved);
603  break;
604  }
605  default: {
606  vector<string> *values = attr_table.get_attr_vector(at_iter);
607 
608  for(vector<string>::size_type i = 0; i < values->size(); i++) {
609  string currName = attr_table.get_name(at_iter);
610  string currValue = (*values)[i];
611 
612  // FOR TESTING AND DEBUGGING PURPOSES
613  // *strm << "\"currName\": \"" << currName << "\", \"currValue\": \"" << currValue << "\"" << endl;
614 
615  // From Climate and Forecast (CF) Conventions:
616  // http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#_description_of_the_data
617 
618  // We continue to support the use of the units and long_name attributes as defined in COARDS.
619  // We extend COARDS by adding the optional standard_name attribute which is used to provide unique
620  // identifiers for variables. This is important for data exchange since one cannot necessarily
621  // identify a particular variable based on the name assigned to it by the institution that provided
622  // the data.
623 
624  // The standard_name attribute can be used to identify variables that contain coordinate data. But since it is an
625  // optional attribute, applications that implement these standards must continue to be able to identify coordinate
626  // types based on the COARDS conventions.
627 
628  // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
629  if(currName.compare("units") == 0) {
630  if(isAxis) {
631  if(currAxisName.compare("t") == 0) {
632  currAxisTimeOrigin = currValue;
633  }
634  }
635  else if(isParam) {
636  currUnit = currValue;
637  }
638  }
639  // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#long-name
640  else if(currName.compare("long_name") == 0) {
641  currLongName = currValue;
642  }
643  // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#standard-name
644  else if(currName.compare("standard_name") == 0) {
645  currStandardName = currValue;
646  }
647  }
648 
649  break;
650  }
651  }
652  }
653  }
654 
655  if(isAxis) {
656  // Push a new axis
657  struct Axis *newAxis = new Axis;
658  newAxis->name = currAxisName;
659 
660  // If we're dealing with the time axis, capture the time
661  // origin timestamp value with the appropriate formatting
662  // for printing.
663  // @TODO See https://covjson.org/spec/#temporal-reference-systems
664  if(currAxisName.compare("t") == 0) {
665  // If the calendar is based on years, months, days,
666  // then the referenced values SHOULD use one of the
667  // following ISO8601-based lexical representations:
668 
669  // YYYY
670  // ±XYYYY (where X stands for extra year digits)
671  // YYYY-MM
672  // YYYY-MM-DD
673  // YYYY-MM-DDTHH:MM:SS[.F]Z where Z is either “Z”
674  // or a time scale offset + -HH:MM
675 
676  // If calendar dates with reduced precision are
677  // used in a lexical representation (e.g. "2016"),
678  // then a client SHOULD interpret those dates in
679  // that reduced precision.
680 
681  // string item;
682  // vector<string> tokens;
683  // char *dup = strdup(currAxisTimeOrigin.c_str());
684  // char *token = strtok(dup, " ");
685  // while(token != NULL){
686  // tokens.push_back(string(token));
687  // token = strtok(NULL, " ");
688  // }
689 
690  // free(token);
691  // free(dup);
692  // free(token);
693 
694  // For testing purposes
695  // for(unsigned int i = 0; i < tokens.size(); i++) {
696  // *strm << tokens[i] << endl;
697  // }
698 
699  // @TODO Need to figure out a way to dynamically parse
700  // origin timestamps and convert them to an appropriate
701  // format for CoverageJSON
702 
703  newAxis->values += "\"values\": [\"";
704  // newAxis->values += currAxisTimeOrigin;
705  newAxis->values += "2018-01-01T00:12:20Z";
706  newAxis->values += "\"]";
707  }
708  axes.push_back(newAxis);
709  axisCount++;
710  *axisRetrieved = true;
711  *parameterRetrieved = false;
712  }
713  else if(isParam) {
714  // Kent says: Use LongName to select the new Parameter is too strict.
715  // but when the test 'currLongName.compare("") != 0' is removed,
716  // all of the tests fail and do so by generating output that looks clearly
717  // wrong. I'm going to hold off on this part of the patch for now. jhrg 3/28/19
718 
719  // Removed the 'currLongName.compare("") != 0' test statement. The removed
720  // statement was a constraint to ensure that any parameter retrieved would
721  // have a descriptive long_name attribute for printing observedProperty->label.
722  //
723  // Per Jon Blower:
724  // observedProperty->label comes from:
725  // - The CF long_name, if it exists
726  // - If not, the CF standard_name, perhaps with underscores removed
727  // - If the standard_name doesn’t exist, use the variable ID
728  //
729  // This constraint is now evaluated in the printParametersWorker
730  // rather than within this function where the parameter is retrieved.
731  // -CH 5/11/2019
732 
733  // Push a new parameter
734  struct Parameter *newParameter = new Parameter;
735  newParameter->name = name;
736  newParameter->dataType = currDataType;
737  newParameter->unit = currUnit;
738  newParameter->longName = currLongName;
739  parameters.push_back(newParameter);
740  parameterCount++;
741  *axisRetrieved = false;
742  *parameterRetrieved = true;
743  }
744  else {
745  // Do nothing
746  }
747 }
748 
749 
767  _dds(dds), _returnAs(""), _indent_increment(" "), atomicVals(""), currDataType(""), domainType(0),
768  xExists(false), yExists(false), zExists(false), tExists(false), isParam(false), isAxis(false),
769  canConvertToCovJson(false), axisCount(0), parameterCount(0) // not used , shapeValsCount(0)
770 {
771  if (!_dds) throw BESInternalError("File out COVJSON, null DDS passed to constructor", __FILE__, __LINE__);
772 }
773 
774 
785 void FoDapCovJsonTransform::dump(ostream &strm) const
786 {
787  strm << BESIndent::LMarg << "FoDapCovJsonTransform::dump - (" << (void *) this << ")" << endl;
788  BESIndent::Indent();
789  if(_dds != 0) {
790  _dds->print(strm);
791  }
792  BESIndent::UnIndent();
793 }
794 
795 
809 void FoDapCovJsonTransform::transform(ostream &ostrm, bool sendData, bool testOverride)
810 {
811  transform(&ostrm, _dds, "", sendData, testOverride);
812 }
813 
814 
832 void FoDapCovJsonTransform::transform(ostream *strm, libdap::Constructor *cnstrctr, string indent, bool sendData)
833 {
834  vector<libdap::BaseType *> leaves;
835  vector<libdap::BaseType *> nodes;
836  // Sort the variables into two sets
837  libdap::DDS::Vars_iter vi = cnstrctr->var_begin();
838  libdap::DDS::Vars_iter ve = cnstrctr->var_end();
839 
840  for(; vi != ve; vi++) {
841  if((*vi)->send_p()) {
842  libdap::BaseType *v = *vi;
843  libdap::Type type = v->type();
844 
845  if(type == libdap::dods_array_c) {
846  type = v->var()->type();
847  }
848  if(v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type())) {
849  nodes.push_back(v);
850  }
851  else {
852  leaves.push_back(v);
853  }
854  }
855  }
856 
857  transformNodeWorker(strm, leaves, nodes, indent, sendData);
858 }
859 
860 
871 void FoDapCovJsonTransform::transformNodeWorker(ostream *strm, vector<libdap::BaseType *> leaves,
872  vector<libdap::BaseType *> nodes, string indent, bool sendData)
873 {
874  // Get this node's leaves
875  for(vector<libdap::BaseType *>::size_type l = 0; l < leaves.size(); l++) {
876  libdap::BaseType *v = leaves[l];
877  BESDEBUG(FoDapCovJsonTransform_debug_key, "Processing LEAF: " << v->name() << endl);
878 
879  // FOR TESTING AND DEBUGGING PURPOSES
880  // *strm << "Processing LEAF: " << v->name() << endl;
881 
882  transform(strm, v, indent + _indent_increment, sendData);
883  }
884 
885  // Get this node's child nodes
886  for(vector<libdap::BaseType *>::size_type n = 0; n < nodes.size(); n++) {
887  libdap::BaseType *v = nodes[n];
888  BESDEBUG(FoDapCovJsonTransform_debug_key, "Processing NODE: " << v->name() << endl);
889 
890  // FOR TESTING AND DEBUGGING PURPOSES
891  // *strm << "Processing NODE: " << v->name() << endl;
892 
893  transform(strm, v, indent + _indent_increment, sendData);
894  }
895 }
896 
897 
906 void FoDapCovJsonTransform::printCoverageHeaderWorker(ostream *strm, string indent, bool isCoverageCollection)
907 {
908  string child_indent1 = indent + _indent_increment;
909  string child_indent2 = child_indent1 + _indent_increment;
910 
911  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing COVERAGE HEADER" << endl);
912 
913  // A lot of conditional printing based on whether we have a single
914  // Coverage or a Coverage Collection. Nothing fancy here.
915  *strm << indent << "{" << endl;
916  *strm << child_indent1 << "\"type\": \"Coverage\"," << endl;
917  *strm << child_indent1 << "\"domain\": {" << endl;
918  *strm << child_indent2 << "\"type\" : \"Domain\"," << endl;
919 
920  if(domainType == Grid) {
921  *strm << child_indent2 << "\"domainType\": \"Grid\"," << endl;
922  }
923  else if(domainType == VerticalProfile) {
924  *strm << child_indent2 << "\"domainType\": \"Vertical Profile\"," << endl;
925  }
926  else if(domainType == PointSeries) {
927  *strm << child_indent2 << "\"domainType\": \"Point Series\"," << endl;
928  }
929  else if(domainType == Point) {
930  *strm << child_indent2 << "\"domainType\": \"Point\"," << endl;
931  }
932  else {
933  *strm << child_indent2 << "\"domainType\": \"Unknown\"," << endl;
934  }
935 
936  // @TODO NEEDS REFACTORING FOR BES ISSUE #245
937  // https://github.com/OPENDAP/bes/issues/245
938 
939  // if(parameterCount > 1 && isCoverageCollection) {
940  // *strm << indent << "{" << endl;
941  // *strm << child_indent1 << "\"type\": \"CoverageCollection\"," << endl;
942  // }
943  // else if(parameterCount > 1 && !isCoverageCollection) {
944  // *strm << indent << "\"coverages\": [{" << endl;
945  // *strm << child_indent1 << "\"type\": \"Coverage\"," << endl;
946  // *strm << child_indent1 << "\"domain\": {" << endl;
947  // }
948  // else {
949  // *strm << indent << "{" << endl;
950  // *strm << child_indent1 << "\"type\": \"Coverage\"," << endl;
951  // *strm << child_indent1 << "\"domain\": {" << endl;
952  // }
953  //
954  // if(parameterCount > 1 && !isCoverageCollection) {
955  // *strm << child_indent2 << "\"type\" : \"Domain\"," << endl;
956  // }
957  //
958  // if(parameterCount == 1 && !isCoverageCollection) {
959  // if(domainType == Grid) {
960  // *strm << child_indent2 << "\"domainType\": \"Grid\"," << endl;
961  // }
962  // else if(domainType == VerticalProfile) {
963  // *strm << child_indent2 << "\"domainType\": \"Vertical Profile\"," << endl;
964  // }
965  // else if(domainType == PointSeries) {
966  // *strm << child_indent2 << "\"domainType\": \"Point Series\"," << endl;
967  // }
968  // else if(domainType == Point) {
969  // *strm << child_indent2 << "\"domainType\": \"Point\"," << endl;
970  // }
971  // else {
972  // *strm << child_indent2 << "\"domainType\": \"Unknown\"," << endl;
973  // }
974  // }
975  // else if(parameterCount > 1 && isCoverageCollection) {
976  // if(domainType == Grid) {
977  // *strm << child_indent1 << "\"domainType\": \"Grid\"," << endl;
978  // }
979  // else if(domainType == VerticalProfile) {
980  // *strm << child_indent1 << "\"domainType\": \"Vertical Profile\"," << endl;
981  // }
982  // else if(domainType == PointSeries) {
983  // *strm << child_indent1 << "\"domainType\": \"Point Series\"," << endl;
984  // }
985  // else if(domainType == Point) {
986  // *strm << child_indent1 << "\"domainType\": \"Point\"," << endl;
987  // }
988  // else {
989  // *strm << child_indent1 << "\"domainType\": \"Unknown\"," << endl;
990  // }
991  // }
992 }
993 
994 
1001 void FoDapCovJsonTransform::printAxesWorker(ostream *strm, string indent)
1002 {
1003  string child_indent1 = indent + _indent_increment;
1004  string child_indent2 = child_indent1 + _indent_increment;
1005 
1006  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing AXES" << endl);
1007 
1008  // FOR TESTING AND DEBUGGING PURPOSES
1009  // *strm << "\"type_name\": \"" << a->var()->type_name() << "\"" << endl;
1010 
1011  // Write the axes to strm
1012  *strm << indent << "\"axes\": {" << endl;
1013  for(unsigned int i = 0; i < axisCount; i++) {
1014  for(unsigned int j = 0; j < axisCount; j++) {
1015  // Logic for printing axes in the appropriate order
1016 
1017  // If x, y, z, and t all exist (x, y, z, t)
1018  if(xExists && yExists && zExists && tExists) {
1019  if((i == 0) && (axes[j]->name.compare("x") == 0)) {
1020  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1021  *strm << child_indent2 << axes[j]->values << endl;
1022  }
1023  else if((i == 1) && (axes[j]->name.compare("y") == 0)) {
1024  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1025  *strm << child_indent2 << axes[j]->values << endl;
1026  }
1027  else if((i == 2) && (axes[j]->name.compare("z") == 0)) {
1028  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1029  *strm << child_indent2 << axes[j]->values << endl;
1030  }
1031  else if((i == 3) && (axes[j]->name.compare("t") == 0)) {
1032  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1033  *strm << child_indent2 << axes[j]->values << endl;
1034  }
1035  }
1036  // If just x, y, and t exist (x, y, t)
1037  else if(xExists && yExists && !zExists && tExists) {
1038  if((i == 0) && (axes[j]->name.compare("x") == 0)) {
1039  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1040  *strm << child_indent2 << axes[j]->values << endl;
1041  }
1042  else if((i == 1) && (axes[j]->name.compare("y") == 0)) {
1043  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1044  *strm << child_indent2 << axes[j]->values << endl;
1045  }
1046  else if((i == 2) && (axes[j]->name.compare("t") == 0)) {
1047  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1048  *strm << child_indent2 << axes[j]->values << endl;
1049  }
1050  }
1051  // If just x and y exist (x, y)
1052  else if(xExists && yExists && !zExists && !tExists) {
1053  if((i == 0) && (axes[j]->name.compare("x") == 0)) {
1054  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1055  *strm << child_indent2 << axes[j]->values << endl;
1056  }
1057  else if((i == 1) && (axes[j]->name.compare("y") == 0)) {
1058  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1059  *strm << child_indent2 << axes[j]->values << endl;
1060  }
1061  }
1062  }
1063  if(i == axisCount - 1) {
1064  *strm << child_indent1 << "}" << endl;
1065  }
1066  else {
1067  *strm << child_indent1 << "}," << endl;
1068  }
1069  }
1070  if(parameterCount == 1) {
1071  *strm << indent << "}," << endl;
1072  }
1073  else {
1074  *strm << indent << "}" << endl;
1075  }
1076 }
1077 
1078 
1085 void FoDapCovJsonTransform::printReferenceWorker(ostream *strm, string indent)
1086 {
1087  string child_indent1 = indent + _indent_increment;
1088  string child_indent2 = child_indent1 + _indent_increment;
1089  string coordVars;
1090 
1091  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing REFERENCES" << endl);
1092 
1093  if(xExists) {
1094  coordVars += "\"x\"";
1095  }
1096 
1097  if(yExists) {
1098  if(coordVars.length() > 0) {
1099  coordVars += ", ";
1100  }
1101  coordVars += "\"y\"";
1102  }
1103 
1104  if(zExists) {
1105  if(coordVars.length() > 0) {
1106  coordVars += ", ";
1107  }
1108  coordVars += "\"z\"";
1109  }
1110 
1111  // "referencing": [{
1112  // "coordinates": ["t"],
1113  // "system": {
1114  // "type": "TemporalRS",
1115  // "calendar": "Gregorian"
1116  // }
1117  // },
1118  // {
1119  // "coordinates": ["x", "y", "z"],
1120  // "system": {
1121  // "type": "GeographicCRS",
1122  // "id": "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
1123  // }
1124  // }]
1125 
1126  *strm << indent << "\"referencing\": [{" << endl;
1127  if(tExists) {
1128  *strm << child_indent1 << "\"coordinates\": [\"t\"]," << endl;
1129  *strm << child_indent1 << "\"system\": {" << endl;
1130  *strm << child_indent2 << "\"type\": \"TemporalRS\"," << endl;
1131  *strm << child_indent2 << "\"calendar\": \"Gregorian\"" << endl;
1132  *strm << child_indent1 << "}" << endl;
1133  *strm << indent << "}," << endl;
1134  *strm << indent << "{" << endl;
1135  }
1136  *strm << child_indent1 << "\"coordinates\": [" << coordVars << "]," << endl;
1137  *strm << child_indent1 << "\"system\": {" << endl;
1138  *strm << child_indent2 << "\"type\": \"GeographicCRS\"," << endl;
1139  *strm << child_indent2 << "\"id\": \"http://www.opengis.net/def/crs/OGC/1.3/CRS84\"" << endl;
1140  *strm << child_indent1 << "}" << endl;
1141 
1142  if(parameterCount > 1) {
1143  *strm << indent << "}]," << endl;
1144  }
1145  else {
1146  *strm << indent << "}]" << endl;
1147  }
1148 
1149  if(parameterCount == 1) {
1150  *strm << _indent_increment << "}," << endl;
1151  }
1152 }
1153 
1154 
1161 void FoDapCovJsonTransform::printParametersWorker(ostream *strm, string indent)
1162 {
1163  string child_indent1 = indent + _indent_increment;
1164  string child_indent2 = child_indent1 + _indent_increment;
1165  string child_indent3 = child_indent2 + _indent_increment;
1166  string child_indent4 = child_indent3 + _indent_increment;
1167 
1168  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing PARAMETERS" << endl);
1169 
1170  // @TODO NEEDS REFACTORING FOR BES ISSUE #244
1171  // https://github.com/OPENDAP/bes/issues/244
1172  // Write down the parameter metadata
1173  *strm << indent << "\"parameters\": {" << endl;
1174  for(unsigned int i = 0; i < parameterCount; i++) {
1175  *strm << child_indent1 << "\"" << parameters[i]->name << "\": {" << endl;
1176  *strm << child_indent2 << "\"type\": \"Parameter\"," << endl;
1177  *strm << child_indent2 << "\"description\": {" << endl;
1178 
1179  if(parameters[i]->longName.compare("") != 0) {
1180  *strm << child_indent3 << "\"en\": \"" << parameters[i]->longName << "\"," << endl;
1181  }
1182  else if(parameters[i]->standardName.compare("") != 0) {
1183  *strm << child_indent3 << "\"en\": \"" << parameters[i]->standardName << "\"" << endl;
1184  }
1185  else {
1186  *strm << child_indent3 << "\"en\": \"" << parameters[i]->name << "\"" << endl;
1187  }
1188 
1189  *strm << child_indent2 << "}," << endl;
1190  *strm << child_indent2 << "\"unit\": {" << endl;
1191  *strm << child_indent3 << "\"label\": {" << endl;
1192  *strm << child_indent4 << "\"en\": \"" << parameters[i]->unit << "\"" << endl;
1193  *strm << child_indent3 << "}" << endl;
1194  *strm << child_indent2 << "}," << endl;
1195  *strm << child_indent2 << "\"symbol\": {" << endl;
1196  *strm << child_indent3 << "\"value\": \"" << parameters[i]->unit << "\"," << endl;
1197  *strm << child_indent3 << "\"type\": \"http://www.opengis.net/def/uom/UCUM/\"" << endl;
1198  *strm << child_indent2 << "}," << endl;
1199  *strm << child_indent2 << "\"observedProperty\": {" << endl;
1200 
1201  // Per Jon Blower:
1202  // observedProperty->id comes from the CF standard_name,
1203  // mapped to a URI like this: http://vocab.nerc.ac.uk/standard_name/<standard_name>.
1204  // If the standard_name is not present, omit the id.
1205  if(parameters[i]->standardName.compare("") != 0) {
1206  *strm << child_indent3 << "\"id\": \"http://vocab.nerc.ac.uk/standard_name/" << parameters[i]->standardName << "/\"" << endl;
1207  }
1208 
1209  // Per Jon Blower:
1210  // observedProperty->label comes from:
1211  // - The CF long_name, if it exists
1212  // - If not, the CF standard_name, perhaps with underscores removed
1213  // - If the standard_name doesn’t exist, use the variable ID
1214  *strm << child_indent3 << "\"label\": {" << endl;
1215 
1216  if(parameters[i]->longName.compare("") != 0) {
1217  *strm << child_indent4 << "\"en\": \"" << parameters[i]->longName << "\"" << endl;
1218  }
1219  else if(parameters[i]->standardName.compare("") != 0) {
1220  *strm << child_indent4 << "\"en\": \"" << parameters[i]->standardName << "\"" << endl;
1221  }
1222  else {
1223  *strm << child_indent4 << "\"en\": \"" << parameters[i]->name << "\"" << endl;
1224  }
1225 
1226  *strm << child_indent3 << "}" << endl;
1227  *strm << child_indent2 << "}" << endl;
1228 
1229  if(i == parameterCount - 1) {
1230  *strm << child_indent1 << "}" << endl;
1231  }
1232  else {
1233  *strm << child_indent1 << "}," << endl;
1234  }
1235  }
1236  if(parameterCount > 1) {
1237  *strm << indent << "}," << endl;
1238  }
1239 }
1240 
1241 
1251 void FoDapCovJsonTransform::printRangesWorker(ostream *strm, string indent)
1252 {
1253  string child_indent1 = indent + _indent_increment;
1254  string child_indent2 = child_indent1 + _indent_increment;
1255  string child_indent3 = child_indent2 + _indent_increment;
1256  string axisNames;
1257 
1258  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing RANGES" << endl);
1259 
1260  if(tExists) {
1261  axisNames += "\"t\"";
1262  }
1263 
1264  if(zExists) {
1265  if(axisNames.length() > 0) {
1266  axisNames += ", ";
1267  }
1268  axisNames += "\"z\"";
1269  }
1270 
1271  if(yExists) {
1272  if(axisNames.length() > 0) {
1273  axisNames += ", ";
1274  }
1275  axisNames += "\"y\"";
1276  }
1277 
1278  if(xExists) {
1279  if(axisNames.length() > 0) {
1280  axisNames += ", ";
1281  }
1282  axisNames += "\"x\"";
1283  }
1284 
1285  // Axis name (x, y, or z)
1286  *strm << indent << "}," << endl;
1287  *strm << indent << "\"ranges\": {" << endl;
1288  for(unsigned int i = 0; i < parameterCount; i++) {
1289  string dataType;
1290  // See spec: https://covjson.org/spec/#ndarray-objects
1291  if(parameters[i]->dataType.find("int") == 0 || parameters[i]->dataType.find("Int") == 0
1292  || parameters[i]->dataType.find("integer") == 0 || parameters[i]->dataType.find("Integer") == 0) {
1293  dataType = "integer";
1294  }
1295  else if(parameters[i]->dataType.find("float") == 0 || parameters[i]->dataType.find("Float") == 0) {
1296  dataType = "float";
1297  }
1298  else if(parameters[i]->dataType.find("string") == 0 || parameters[i]->dataType.find("String") == 0) {
1299  dataType = "string";
1300  }
1301  else {
1302  dataType = "string";
1303  }
1304 
1305  // @TODO NEEDS REFACTORING FOR BES ISSUE #244
1306  // https://github.com/OPENDAP/bes/issues/244
1307  *strm << child_indent1 << "\"" << parameters[i]->name << "\": {" << endl;
1308  *strm << child_indent2 << "\"type\": \"NdArray\"," << endl;
1309  *strm << child_indent2 << "\"dataType\": \"" << dataType << "\", " << endl;
1310  *strm << child_indent2 << "\"axisNames\": [" << axisNames << "]," << endl;
1311  *strm << child_indent2 << parameters[i]->shape << endl;
1312  *strm << child_indent2 << parameters[i]->values << endl;
1313 
1314  if(i == parameterCount - 1) {
1315  *strm << child_indent1 << "}" << endl;
1316  }
1317  else {
1318  *strm << child_indent1 << "}," << endl;
1319  }
1320  }
1321  if(parameterCount == 1) {
1322  *strm << indent << "}" << endl;
1323  }
1324 }
1325 
1326 
1333 void FoDapCovJsonTransform::printCoverageFooterWorker(ostream *strm, string indent)
1334 {
1335  string child_indent1 = indent + _indent_increment;
1336  string child_indent2 = child_indent1 + _indent_increment;
1337 
1338  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing COVERAGE FOOTER" << endl);
1339 
1340  if(parameterCount > 1) {
1341  *strm << child_indent2 << "}" << endl;
1342  *strm << child_indent1 << "}]" << endl;
1343  }
1344 
1345  *strm << indent << "}" << endl;
1346 }
1347 
1348 
1359 void FoDapCovJsonTransform::printCoverageJSON(ostream *strm, string indent, bool testOverride)
1360 {
1361  string child_indent1 = indent + _indent_increment;
1362  string child_indent2 = child_indent1 + _indent_increment;
1363  string child_indent3 = child_indent2 + _indent_increment;
1364 
1365  // Determine if the attribute values we read can be converted to CovJSON.
1366  // Test override forces printing output to stream regardless of whether
1367  // or not the file can be converted into CoverageJSON format.
1368  if(testOverride) {
1369  canConvertToCovJson = true;
1370  }
1371  else {
1372  canConvertToCovJson = canConvert();
1373  }
1374 
1375  // Only print if this file can be converted to CovJSON
1376  if(canConvertToCovJson) {
1377  // If we have more than one parameter, we are dealing with a
1378  // Coverage Collection, so we must print accordingly
1379  // if(parameterCount > 1) {
1380  // // Prints header and domain type
1381  // printCoverageHeaderWorker(strm, indent, true);
1382  //
1383  // // Prints parameter metadata
1384  // printParametersWorker(strm, child_indent1);
1385  //
1386  // // Prints the references for the given Axes
1387  // printReferenceWorker(strm, child_indent1);
1388  //
1389  // // Prints header and domain type
1390  // printCoverageHeaderWorker(strm, child_indent1, false);
1391  //
1392  // // Prints the axes metadata and range values
1393  // printAxesWorker(strm, child_indent3);
1394  //
1395  // // Prints the parameter range values
1396  // printRangesWorker(strm, child_indent2);
1397  //
1398  // // Prints footer
1399  // printCoverageFooterWorker(strm, indent);
1400  // }
1401  // // If we have a single parameter, or even no parameters,
1402  // // we will print as a "normal" coverage
1403  // else {
1404 
1405  // Prints header and domain type
1406  printCoverageHeaderWorker(strm, indent, false);
1407 
1408  // Prints the axes metadata and range values
1409  printAxesWorker(strm, child_indent2);
1410 
1411  // Prints the references for the given Axes
1412  printReferenceWorker(strm, child_indent2);
1413 
1414  // Prints parameter metadata
1415  printParametersWorker(strm, child_indent2);
1416 
1417  // Prints the parameter range values
1418  printRangesWorker(strm, child_indent1);
1419 
1420  // Prints footer
1421  printCoverageFooterWorker(strm, indent);
1422  // }
1423  }
1424  else {
1425  // If this file can't be converted, then its failing spatial/temporal requirements
1426  throw BESInternalError("File cannot be converted to COVJSON format due to missing or incompatible spatial dimensions", __FILE__, __LINE__);
1427  }
1428 }
1429 
1430 
1446 void FoDapCovJsonTransform::transform(ostream *strm, libdap::DDS *dds, string indent, bool sendData, bool testOverride)
1447 {
1448  // Sort the variables into two sets
1449  vector<libdap::BaseType *> leaves;
1450  vector<libdap::BaseType *> nodes;
1451 
1452  libdap::DDS::Vars_iter vi = dds->var_begin();
1453  libdap::DDS::Vars_iter ve = dds->var_end();
1454  for(; vi != ve; vi++) {
1455  if((*vi)->send_p()) {
1456  libdap::BaseType *v = *vi;
1457  libdap::Type type = v->type();
1458  if(type == libdap::dods_array_c) {
1459  type = v->var()->type();
1460  }
1461  if(v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type())) {
1462  nodes.push_back(v);
1463  }
1464  else {
1465  leaves.push_back(v);
1466  }
1467  }
1468  }
1469 
1470  // Read through the source DDS leaves and nodes, extract all axes and
1471  // parameter data, and store that data as Axis and Parameters
1472  transformNodeWorker(strm, leaves, nodes, indent + _indent_increment + _indent_increment, sendData);
1473 
1474  // Print the Coverage data to stream as CoverageJSON
1475  printCoverageJSON(strm, indent, testOverride);
1476 }
1477 
1478 
1491 void FoDapCovJsonTransform::transform(ostream *strm, libdap::BaseType *bt, string indent, bool sendData)
1492 {
1493  switch(bt->type()) {
1494  // Handle the atomic types - that's easy!
1495  case libdap::dods_byte_c:
1496  case libdap::dods_int16_c:
1497  case libdap::dods_uint16_c:
1498  case libdap::dods_int32_c:
1499  case libdap::dods_uint32_c:
1500  case libdap::dods_float32_c:
1501  case libdap::dods_float64_c:
1502  case libdap::dods_str_c:
1503  case libdap::dods_url_c:
1504  transformAtomic(bt, indent, sendData);
1505  break;
1506 
1507  case libdap::dods_structure_c:
1508  transform(strm, (libdap::Structure *) bt, indent, sendData);
1509  break;
1510 
1511  case libdap::dods_grid_c:
1512  transform(strm, (libdap::Grid *) bt, indent, sendData);
1513  break;
1514 
1515  case libdap::dods_sequence_c:
1516  transform(strm, (libdap::Sequence *) bt, indent, sendData);
1517  break;
1518 
1519  case libdap::dods_array_c:
1520  transform(strm, (libdap::Array *) bt, indent, sendData);
1521  break;
1522 
1523  case libdap::dods_int8_c:
1524  case libdap::dods_uint8_c:
1525  case libdap::dods_int64_c:
1526  case libdap::dods_uint64_c:
1527  case libdap::dods_enum_c:
1528  case libdap::dods_group_c: {
1529  string s = (string) "File out COVJSON, DAP4 types not yet supported.";
1530  throw BESInternalError(s, __FILE__, __LINE__);
1531  break;
1532  }
1533 
1534  default: {
1535  string s = (string) "File out COVJSON, Unrecognized type.";
1536  throw BESInternalError(s, __FILE__, __LINE__);
1537  break;
1538  }
1539  }
1540 }
1541 
1542 
1557 void FoDapCovJsonTransform::transformAtomic(libdap::BaseType *b, string indent, bool sendData)
1558 {
1559  string childindent = indent + _indent_increment;
1560  struct Axis *newAxis = new Axis;
1561 
1562  newAxis->name = "test";
1563  if(sendData) {
1564  newAxis->values += "\"values\": [";
1565  if(b->type() == libdap::dods_str_c || b->type() == libdap::dods_url_c) {
1566  libdap::Str *strVar = (libdap::Str *) b;
1567  string tmpString = strVar->value();
1568  newAxis->values += "\"";
1569  newAxis->values += focovjson::escape_for_covjson(tmpString);
1570  newAxis->values += "\"";
1571  }
1572  else {
1573  ostringstream otemp;
1574  istringstream itemp;
1575  int tempVal = 0;
1576  b->print_val(otemp, "", false);
1577  istringstream (otemp.str());
1578  istringstream (otemp.str()) >> tempVal;
1579  newAxis->values += otemp.str();
1580  }
1581  newAxis->values += "]";
1582  }
1583  else {
1584  newAxis->values += "\"values\": []";
1585  }
1586 
1587  axes.push_back(newAxis);
1588  axisCount++;
1589 }
1590 
1591 
1602 void FoDapCovJsonTransform::transform(ostream *strm, libdap::Array *a, string indent, bool sendData)
1603 {
1604  BESDEBUG(FoDapCovJsonTransform_debug_key,
1605  "FoCovJsonTransform::transform() - Processing Array. " << " a->type(): " << a->type() << " a->var()->type(): " << a->var()->type() << endl);
1606 
1607  switch(a->var()->type()) {
1608  // Handle the atomic types - that's easy!
1609  case libdap::dods_byte_c:
1610  covjsonSimpleTypeArray<libdap::dods_byte>(strm, a, indent, sendData);
1611  break;
1612 
1613  case libdap::dods_int16_c:
1614  covjsonSimpleTypeArray<libdap::dods_int16>(strm, a, indent, sendData);
1615  break;
1616 
1617  case libdap::dods_uint16_c:
1618  covjsonSimpleTypeArray<libdap::dods_uint16>(strm, a, indent, sendData);
1619  break;
1620 
1621  case libdap::dods_int32_c:
1622  covjsonSimpleTypeArray<libdap::dods_int32>(strm, a, indent, sendData);
1623  break;
1624 
1625  case libdap::dods_uint32_c:
1626  covjsonSimpleTypeArray<libdap::dods_uint32>(strm, a, indent, sendData);
1627  break;
1628 
1629  case libdap::dods_float32_c:
1630  covjsonSimpleTypeArray<libdap::dods_float32>(strm, a, indent, sendData);
1631  break;
1632 
1633  case libdap::dods_float64_c:
1634  covjsonSimpleTypeArray<libdap::dods_float64>(strm, a, indent, sendData);
1635  break;
1636 
1637  case libdap::dods_str_c: {
1638  covjsonStringArray(strm, a, indent, sendData);
1639  break;
1640  }
1641 
1642  case libdap::dods_url_c: {
1643  covjsonStringArray(strm, a, indent, sendData);
1644  break;
1645  }
1646 
1647  case libdap::dods_structure_c:
1648  throw BESInternalError("File out COVJSON, Arrays of Structure objects not a supported return type.", __FILE__, __LINE__);
1649 
1650  case libdap::dods_grid_c:
1651  throw BESInternalError("File out COVJSON, Arrays of Grid objects not a supported return type.", __FILE__, __LINE__);
1652 
1653  case libdap::dods_sequence_c:
1654  throw BESInternalError("File out COVJSON, Arrays of Sequence objects not a supported return type.", __FILE__, __LINE__);
1655 
1656  case libdap::dods_array_c:
1657  throw BESInternalError("File out COVJSON, Arrays of Array objects not a supported return type.", __FILE__, __LINE__);
1658 
1659  case libdap::dods_int8_c:
1660  case libdap::dods_uint8_c:
1661  case libdap::dods_int64_c:
1662  case libdap::dods_uint64_c:
1663  case libdap::dods_enum_c:
1664  case libdap::dods_group_c:
1665  throw BESInternalError("File out COVJSON, DAP4 types not yet supported.", __FILE__, __LINE__);
1666 
1667  default:
1668  throw BESInternalError("File out COVJSON, Unrecognized type.", __FILE__, __LINE__);
1669  }
1670 }
FoDapCovJsonTransform(libdap::DDS *dds)
Get the CovJSON encoding for a DDS.
exception thrown if inernal error encountered
virtual void dump(std::ostream &strm) const
Dumps information about this transformation object for debugging purposes.
Type
Type of JSON value.
Definition: rapidjson.h:603