bes  Updated for version 3.20.13
GeoTiffTransmitter.cc
1 // GeoTiffTransmitter.cc
2 
3 // This file is part of BES GDAL File Out Module
4 
5 // Copyright (c) 2012 OPeNDAP, Inc.
6 // Author: James Gallagher <jgallagher@opendap.org>
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
17 //
18 // You should have received a copy of the GNU Lesser General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 //
22 // You can contact University Corporation for Atmospheric Research at
23 // 3080 Center Green Drive, Boulder, CO 80301
24 
25 #include "config.h"
26 
27 #include <unistd.h>
28 
29 #include <cstdio>
30 #include <cstdlib>
31 
32 #include <sys/types.h> // For umask
33 #include <sys/stat.h>
34 
35 #include <iostream>
36 #include <fstream>
37 
38 #include <libdap/DataDDS.h>
39 #include <libdap/BaseType.h>
40 #include <libdap/escaping.h>
41 
42 #include <dispatch/BESUtil.h>
43 #include <dispatch/BESInternalError.h>
44 #include <dispatch/BESContextManager.h>
45 #include <dispatch/BESDataNames.h>
46 #include <dispatch/BESDebug.h>
47 #include <dispatch/RequestServiceTimer.h>
48 #include <dispatch/TheBESKeys.h>
49 
50 #include <dap/BESDapError.h>
51 #include <dap/BESDataDDSResponse.h>
52 #include <dap/BESDapNames.h>
53 #include <dap/DapFunctionUtils.h>
54 #include <dap/TempFile.h>
55 
56 #include "GeoTiffTransmitter.h"
57 #include "FONgTransform.h"
58 
59 #define FONG_TEMP_DIR "/tmp"
60 #define FONG_GCS "WGS84"
61 #define MODULE "gdal"
62 #define prolog string("GeoTiffTransmitter::").append(__func__).append("() - ")
63 
64 using namespace libdap;
65 using namespace std;
66 
67 string GeoTiffTransmitter::temp_dir;
68 string GeoTiffTransmitter::default_gcs;
69 
87 {
88  // DATA_SERVICE == "dods"
89  add_method(DATA_SERVICE, GeoTiffTransmitter::send_data_as_geotiff);
90 
91  if (GeoTiffTransmitter::temp_dir.empty()) {
92  // Where is the temp directory for creating these files
93  bool found = false;
94  string key = "FONg.Tempdir";
95  TheBESKeys::TheKeys()->get_value(key, GeoTiffTransmitter::temp_dir, found);
96  if (!found || GeoTiffTransmitter::temp_dir.empty()) {
97  GeoTiffTransmitter::temp_dir = FONG_TEMP_DIR;
98  }
99  string::size_type len = GeoTiffTransmitter::temp_dir.length();
100  if (GeoTiffTransmitter::temp_dir[len - 1] == '/') {
101  GeoTiffTransmitter::temp_dir = GeoTiffTransmitter::temp_dir.substr(0, len - 1);
102  }
103  }
104 
105  if (GeoTiffTransmitter::default_gcs.empty()) {
106  // Use what as the default Geographic coordinate system?
107  bool found = false;
108  string key = "FONg.Default_GCS";
109  TheBESKeys::TheKeys()->get_value(key, GeoTiffTransmitter::default_gcs, found);
110  if (!found || GeoTiffTransmitter::default_gcs.empty()) {
111  GeoTiffTransmitter::default_gcs = FONG_GCS;
112  }
113  }
114 }
115 
132 {
133  BESDataDDSResponse *bdds = dynamic_cast<BESDataDDSResponse *>(obj);
134  if (!bdds)
135  throw BESInternalError("cast error", __FILE__, __LINE__);
136 
137  DDS *dds = bdds->get_dds();
138  if (!dds)
139  throw BESInternalError("No DataDDS has been created for transmit", __FILE__, __LINE__);
140 
141  ostream &strm = dhi.get_output_stream();
142  if (!strm)
143  throw BESInternalError("Output stream is not set, cannot return as", __FILE__, __LINE__);
144 
145  BESDEBUG(MODULE, "GeoTiffTransmitter::send_data - parsing the constraint" << endl);
146 
147  // ticket 1248 jhrg 2/23/09
148  string ce = www2id(dhi.data[POST_CONSTRAINT], "%", "%20%26");
149  try {
150  bdds->get_ce().parse_constraint(ce, *dds);
151  }
152  catch (const Error &e) {
153  throw BESDapError("Failed to parse the constraint expression: " + e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
154  }
155  catch (...) {
156  throw BESInternalError("Failed to parse the constraint expression: Unknown exception caught", __FILE__, __LINE__);
157  }
158 
159  // now we need to read the data
160  BESDEBUG(MODULE, "GeoTiffTransmitter::send_data - reading data into DataDDS" << endl);
161 
162  try {
163  // Handle *functional* constraint expressions specially
164  if (bdds->get_ce().function_clauses()) {
165  BESDEBUG(MODULE, "processing a functional constraint clause(s)." << endl);
166  DDS *tmp_dds = bdds->get_ce().eval_function_clauses(*dds);
167  delete dds;
168  dds = tmp_dds;
169  bdds->set_dds(dds);
170 
171  // This next step utilizes a well known function, promote_function_output_structures()
172  // to look for one or more top level Structures whose name indicates (by way of ending
173  // with "_uwrap") that their contents should be promoted (aka moved) to the top level.
174  // This is in support of a hack around the current API where server side functions
175  // may only return a single DAP object and not a collection of objects. The name suffix
176  // "_unwrap" is used as a signal from the function to the the various response
177  // builders and transmitters that the representation needs to be altered before
178  // transmission, and that in fact is what happens in our friend
179  // promote_function_output_structures()
180  promote_function_output_structures(dds);
181 
182  }
183  else {
184  // Iterate through the variables in the DataDDS and read
185  // in the data if the variable has the send flag set.
186  for (DDS::Vars_iter i = dds->var_begin(); i != dds->var_end(); i++) {
187  if ((*i)->send_p()) {
188  (*i)->intern_data(bdds->get_ce(), *dds);
189  }
190  }
191  }
192  }
193  catch (const Error &e) {
194  throw BESDapError("Failed to read data: " + e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
195  }
196  catch (const BESError &e) {
197  throw;
198  }
199  catch (...) {
200  throw BESInternalError("Failed to read data: Unknown exception caught", __FILE__, __LINE__);
201  }
202 
203  // This closes the file when it goes out of scope. jhrg 8/25/17
204  bes::TempFile temp_file;
205  string temp_file_name = temp_file.create(GeoTiffTransmitter::temp_dir, "geotiff_");
206 
207  // transform the OPeNDAP DataDDS to the geotiff file
208  BESDEBUG(MODULE, "GeoTiffTransmitter::send_data - transforming into temporary file " << temp_file_name << endl);
209 
210  try {
211  FONgTransform ft(dds, bdds->get_ce(), temp_file_name);
212 
213  // Verify the request hasn't exceeded bes_timeout, and disable timeout if allowed.
214  RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog + "ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
216 
217  // transform() opens the temporary file, dumps data to it and closes it.
219 
220  BESDEBUG(MODULE, "GeoTiffTransmitter::send_data - transmitting temp file " << temp_file_name << endl );
221 
222  GeoTiffTransmitter::return_temp_stream(temp_file_name, strm);
223  }
224  catch (const Error &e) {
225  throw BESDapError("Failed to transform data to GeoTiff: " + e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
226  }
227  catch (const BESError &e) {
228  throw;
229  }
230  catch (...) {
231  throw BESInternalError("Fileout GeoTiff, was not able to transform to geotiff, unknown error", __FILE__, __LINE__);
232  }
233 
234  BESDEBUG(MODULE, "GeoTiffTransmitter::send_data - done transmitting to geotiff" << endl);
235 }
236 
246 void GeoTiffTransmitter::return_temp_stream(const string &filename, ostream &strm)
247 {
248  ifstream os;
249  os.open(filename.c_str(), ios::binary | ios::in);
250  if (!os)
251  throw BESInternalError("Cannot connect to data source", __FILE__, __LINE__);
252 
253  char block[4096];
254  os.read(block, sizeof block);
255  int nbytes = os.gcount();
256  if (nbytes == 0) {
257  os.close();
258  throw BESInternalError("Internal server error, got zero count on stream buffer.", __FILE__, __LINE__);
259  }
260 
261  // I think this is never used - we never run Hyrax where the BES is accessed
262  // directly by HTTP.
263  bool found = false;
264  string protocol = BESContextManager::TheManager()->get_context("transmit_protocol", found);
265  if (protocol == "HTTP") {
266  strm << "HTTP/1.0 200 OK\n";
267  strm << "Content-type: application/octet-stream\n";
268  strm << "Content-Description: " << "BES dataset" << "\n";
269  strm << "Content-Disposition: filename=" << filename << ".tif;\n\n";
270  strm << flush;
271  }
272 
273  strm.write(block, nbytes);
274 
275  while (os) {
276  os.read(block, sizeof block);
277  nbytes = os.gcount();
278  strm.write(block, nbytes);
279  }
280 
281  os.close();
282 }
283 
virtual std::string get_context(const std::string &name, bool &found)
retrieve the value of the specified context from the BES
error object created from libdap error objects and can handle those errors
Definition: BESDapError.h:59
Represents an OPeNDAP DataDDS DAP2 data object within the BES.
void set_dds(libdap::DDS *ddsIn)
Structure storing information used by the BES to handle the request.
std::map< std::string, std::string > data
the map of string data that will be required for the current request.
Base exception class for the BES with basic string message.
Definition: BESError.h:59
exception thrown if internal error encountered
Abstract base class representing a specific set of information in response to a request to the BES.
static void conditional_timeout_cancel()
Checks if the timeout alarm should be canceled based on the value of the BES key BES....
Definition: BESUtil.cc:895
Transformation object that converts an OPeNDAP DataDDS to a GeoTiff file.
Definition: FONgTransform.h:42
virtual void transform_to_geotiff()
Transforms the variables of the DataDDS to a GeoTiff file.
GeoTiffTransmitter()
Construct the GeoTiffTransmitter, adding it with name geotiff to be able to transmit a data response.
static void send_data_as_geotiff(BESResponseObject *obj, BESDataHandlerInterface &dhi)
The static method registered to transmit OPeNDAP data objects as a netcdf file.
static RequestServiceTimer * TheTimer()
Return a pointer to a singleton timer instance. If an instance does not exist it will create and init...
void throw_if_timeout_expired(const std::string &message, const std::string &file, const int line)
Checks the RequestServiceTimer to determine if the time spent servicing the request at this point has...
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: TheBESKeys.cc:340
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:71
Get a new temporary file.
Definition: TempFile.h:46
std::string create(const std::string &dir_name="/tmp/hyrax_tmp", const std::string &path_template="opendap")
Create a new temporary file.
Definition: TempFile.cc:170