bes  Updated for version 3.20.6
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 <DataDDS.h>
39 #include <BaseType.h>
40 #include <escaping.h>
41 
42 using namespace libdap;
43 
44 #include "GeoTiffTransmitter.h"
45 #include "FONgTransform.h"
46 
47 #include <BESUtil.h>
48 #include <BESInternalError.h>
49 #include <BESDapError.h>
50 #include <BESContextManager.h>
51 #include <BESDataDDSResponse.h>
52 #include <BESDapNames.h>
53 #include <BESDataNames.h>
54 #include <BESDebug.h>
55 #include <TempFile.h>
56 #include <DapFunctionUtils.h>
57 
58 #include <TheBESKeys.h>
59 
60 #define FONG_TEMP_DIR "/tmp"
61 #define FONG_GCS "WGS84"
62 
63 string GeoTiffTransmitter::temp_dir;
64 string GeoTiffTransmitter::default_gcs;
65 
83 {
84  // DATA_SERVICE == "dods"
85  add_method(DATA_SERVICE, GeoTiffTransmitter::send_data_as_geotiff);
86 
87  if (GeoTiffTransmitter::temp_dir.empty()) {
88  // Where is the temp directory for creating these files
89  bool found = false;
90  string key = "FONg.Tempdir";
91  TheBESKeys::TheKeys()->get_value(key, GeoTiffTransmitter::temp_dir, found);
92  if (!found || GeoTiffTransmitter::temp_dir.empty()) {
93  GeoTiffTransmitter::temp_dir = FONG_TEMP_DIR;
94  }
95  string::size_type len = GeoTiffTransmitter::temp_dir.length();
96  if (GeoTiffTransmitter::temp_dir[len - 1] == '/') {
97  GeoTiffTransmitter::temp_dir = GeoTiffTransmitter::temp_dir.substr(0, len - 1);
98  }
99  }
100 
101  if (GeoTiffTransmitter::default_gcs.empty()) {
102  // Use what as the default Geographic coordinate system?
103  bool found = false;
104  string key = "FONg.Default_GCS";
105  TheBESKeys::TheKeys()->get_value(key, GeoTiffTransmitter::default_gcs, found);
106  if (!found || GeoTiffTransmitter::default_gcs.empty()) {
107  GeoTiffTransmitter::default_gcs = FONG_GCS;
108  }
109  }
110 }
111 
128 {
129  BESDataDDSResponse *bdds = dynamic_cast<BESDataDDSResponse *>(obj);
130  if (!bdds)
131  throw BESInternalError("cast error", __FILE__, __LINE__);
132 
133  DDS *dds = bdds->get_dds();
134  if (!dds)
135  throw BESInternalError("No DataDDS has been created for transmit", __FILE__, __LINE__);
136 
137  ostream &strm = dhi.get_output_stream();
138  if (!strm)
139  throw BESInternalError("Output stream is not set, cannot return as", __FILE__, __LINE__);
140 
141  BESDEBUG("fong2", "GeoTiffTransmitter::send_data - parsing the constraint" << endl);
142 
143  // ticket 1248 jhrg 2/23/09
144  string ce = www2id(dhi.data[POST_CONSTRAINT], "%", "%20%26");
145  try {
146  bdds->get_ce().parse_constraint(ce, *dds);
147  }
148  catch (Error &e) {
149  throw BESDapError("Failed to parse the constraint expression: " + e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
150  }
151  catch (...) {
152  throw BESInternalError("Failed to parse the constraint expression: Unknown exception caught", __FILE__, __LINE__);
153  }
154 
155  // now we need to read the data
156  BESDEBUG("fong2", "GeoTiffTransmitter::send_data - reading data into DataDDS" << endl);
157 
158  try {
159  // Handle *functional* constraint expressions specially
160  if (bdds->get_ce().function_clauses()) {
161  BESDEBUG("fong2", "processing a functional constraint clause(s)." << endl);
162  DDS *tmp_dds = bdds->get_ce().eval_function_clauses(*dds);
163  delete dds;
164  dds = tmp_dds;
165  bdds->set_dds(dds);
166 
167  // This next step utilizes a well known function, promote_function_output_structures()
168  // to look for one or more top level Structures whose name indicates (by way of ending
169  // with "_uwrap") that their contents should be promoted (aka moved) to the top level.
170  // This is in support of a hack around the current API where server side functions
171  // may only return a single DAP object and not a collection of objects. The name suffix
172  // "_unwrap" is used as a signal from the function to the the various response
173  // builders and transmitters that the representation needs to be altered before
174  // transmission, and that in fact is what happens in our friend
175  // promote_function_output_structures()
176  promote_function_output_structures(dds);
177 
178  }
179  else {
180  // Iterate through the variables in the DataDDS and read
181  // in the data if the variable has the send flag set.
182  for (DDS::Vars_iter i = dds->var_begin(); i != dds->var_end(); i++) {
183  if ((*i)->send_p()) {
184  (*i)->intern_data(bdds->get_ce(), *dds);
185  }
186  }
187  }
188  }
189  catch (Error &e) {
190  throw BESDapError("Failed to read data: " + e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
191  }
192  catch (BESError &e) {
193  throw;
194  }
195  catch (...) {
196  throw BESInternalError("Failed to read data: Unknown exception caught", __FILE__, __LINE__);
197  }
198 
199  // This closes the file when it goes out of scope. jhrg 8/25/17
200  bes::TempFile temp_file(GeoTiffTransmitter::temp_dir + '/' + "geotiffXXXXXX");
201 #if 0
202  // Huh? Put the template for the temp file name in a char array. Use vector<char>
203  // to avoid using new/delete.
204  string temp_file_name = GeoTiffTransmitter::temp_dir + '/' + "geotiffXXXXXX";
205  vector<char> temp_file(temp_file_name.length() + 1);
206  string::size_type len = temp_file_name.copy(&temp_file[0], temp_file_name.length());
207  temp_file[len] = '\0';
208 
209  // cover the case where older versions of mkstemp() create the file using
210  // a mode of 666.
211  mode_t original_mode = umask(077);
212 
213  // Make and open (an atomic operation) the temporary file. Then reset the umask
214  int fd = mkstemp(&temp_file[0]);
215  umask(original_mode);
216 
217  if (fd == -1)
218  throw BESInternalError("Failed to open the temporary file: " + temp_file_name, __FILE__, __LINE__);
219 #endif
220  // transform the OPeNDAP DataDDS to the geotiff file
221  BESDEBUG("fong2", "GeoTiffTransmitter::send_data - transforming into temporary file " << temp_file.get_name() << endl);
222 
223  try {
224  FONgTransform ft(dds, bdds->get_ce(), temp_file.get_name());
225 
226  // Now that we are ready to start building the response data we
227  // cancel any pending timeout alarm according to the configuration.
229 
230  // transform() opens the temporary file, dumps data to it and closes it.
232 
233  BESDEBUG("fong2", "GeoTiffTransmitter::send_data - transmitting temp file " << temp_file.get_name() << endl );
234 
235  GeoTiffTransmitter::return_temp_stream(temp_file.get_name(), strm);
236  }
237  catch (Error &e) {
238 #if 0
239  close(fd);
240  (void) unlink(&temp_file[0]);
241 #endif
242  throw BESDapError("Failed to transform data to GeoTiff: " + e.get_error_message(), false, e.get_error_code(), __FILE__, __LINE__);
243  }
244  catch (BESError &e) {
245 #if 0
246  close(fd);
247  (void) unlink(&temp_file[0]);
248 #endif
249  throw;
250  }
251  catch (...) {
252 #if 0
253  close(fd);
254  (void) unlink(&temp_file[0]);
255 #endif
256  throw BESInternalError("Fileout GeoTiff, was not able to transform to geotiff, unknown error", __FILE__, __LINE__);
257  }
258 
259 #if 0
260  close(fd);
261  (void) unlink(&temp_file[0]);
262 #endif
263 
264  BESDEBUG("fong2", "GeoTiffTransmitter::send_data - done transmitting to geotiff" << endl);
265 }
266 
276 void GeoTiffTransmitter::return_temp_stream(const string &filename, ostream &strm)
277 {
278  ifstream os;
279  os.open(filename.c_str(), ios::binary | ios::in);
280  if (!os)
281  throw BESInternalError("Cannot connect to data source", __FILE__, __LINE__);
282 
283  char block[4096];
284  os.read(block, sizeof block);
285  int nbytes = os.gcount();
286  if (nbytes == 0) {
287  os.close();
288  throw BESInternalError("Internal server error, got zero count on stream buffer.", __FILE__, __LINE__);
289  }
290 
291  // I think this is never used - we never run Hyrax where the BES is accessed
292  // directly by HTTP.
293  bool found = false;
294  string protocol = BESContextManager::TheManager()->get_context("transmit_protocol", found);
295  if (protocol == "HTTP") {
296  strm << "HTTP/1.0 200 OK\n";
297  strm << "Content-type: application/octet-stream\n";
298  strm << "Content-Description: " << "BES dataset" << "\n";
299  strm << "Content-Disposition: filename=" << filename << ".tif;\n\n";
300  strm << flush;
301  }
302 
303  strm.write(block, nbytes);
304 
305  while (os) {
306  os.read(block, sizeof block);
307  nbytes = os.gcount();
308  strm.write(block, nbytes);
309  }
310 
311  os.close();
312 }
313 
FONgTransform::transform_to_geotiff
virtual void transform_to_geotiff()
Transforms the variables of the DataDDS to a GeoTiff file.
Definition: FONgTransform.cc:319
BESDataDDSResponse::set_dds
void set_dds(libdap::DDS *ddsIn)
Definition: BESDataDDSResponse.h:73
GeoTiffTransmitter::GeoTiffTransmitter
GeoTiffTransmitter()
Construct the GeoTiffTransmitter, adding it with name geotiff to be able to transmit a data response.
Definition: GeoTiffTransmitter.cc:82
BESUtil::conditional_timeout_cancel
static void conditional_timeout_cancel()
Definition: BESUtil.cc:967
libdap
Definition: BESDapFunctionResponseCache.h:35
TheBESKeys::TheKeys
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:62
BESDataHandlerInterface::data
std::map< std::string, std::string > data
the map of string data that will be required for the current request.
Definition: BESDataHandlerInterface.h:90
GeoTiffTransmitter::send_data_as_geotiff
static void send_data_as_geotiff(BESResponseObject *obj, BESDataHandlerInterface &dhi)
The static method registered to transmit OPeNDAP data objects as a netcdf file.
Definition: GeoTiffTransmitter.cc:127
BESInternalError
exception thrown if internal error encountered
Definition: BESInternalError.h:43
TheBESKeys::get_value
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: TheBESKeys.cc:272
BESTransmitter
Definition: BESTransmitter.h:47
BESDataDDSResponse
Represents an OPeNDAP DataDDS DAP2 data object within the BES.
Definition: BESDataDDSResponse.h:46
FONgTransform
Transformation object that converts an OPeNDAP DataDDS to a GeoTiff file.
Definition: FONgTransform.h:41
Error
BESContextManager::get_context
virtual std::string get_context(const std::string &name, bool &found)
retrieve the value of the specified context from the BES
Definition: BESContextManager.cc:77
BESDapError
error object created from libdap error objects and can handle those errors
Definition: BESDapError.h:59
BESDataHandlerInterface
Structure storing information used by the BES to handle the request.
Definition: BESDataHandlerInterface.h:56
BESError
Abstract exception class for the BES with basic string message.
Definition: BESError.h:58
bes::TempFile
Get a new temporary file.
Definition: TempFile.h:46
BESResponseObject
Abstract base class representing a specific set of information in response to a request to the BES.
Definition: BESResponseObject.h:45