OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
ossimDemTool.cpp
Go to the documentation of this file.
1 //**************************************************************************************************
2 //
3 // OSSIM Open Source Geospatial Data Processing Library
4 // See top level LICENSE.txt file for license information
5 //
6 //**************************************************************************************************
7 
8 #include "ossimDemTool.h"
9 #include "ossimDemToolConfig.h"
18 
19 using namespace std;
20 using namespace ossim;
21 
22 #define CINFO ossimNotify(ossimNotifyLevel_INFO)
23 #define CWARN ossimNotify(ossimNotifyLevel_WARN)
24 #define CFATAL ossimNotify(ossimNotifyLevel_FATAL)
25 
26 const char* ossimDemTool::DESCRIPTION =
27  "Provides DEM generation functionality. This tool uses JSON format to "
28  "communicate requests and results.";
29 
31 : m_outputStream (0),
32  m_verbose (false),
33  m_algorithm (ALGO_UNASSIGNED),
34  m_method (METHOD_UNASSIGNED),
35  m_postSpacing (0),
36  m_postSpacingUnits (UNITS_UNASSIGNED)
37 {
38  // Read the default DEM extraction parameters:
39  Json::Value configJson;
40  ossimFilename configFilename ("omgConfig.json");
42  preferencesKWL().findKey( std::string( "ossim_share_directory" ) );
43  shareDir += "/atp"; // TODO: Consolidate tool configs
44  if (!shareDir.isDir())
45  throw ossimException("Nonexistent share drive provided for config files.");
46 
47  configFilename.setPath(shareDir);
48  ifstream configJsonStream (configFilename.string());
49  if (configJsonStream.fail())
50  {
51  ossimNotify(ossimNotifyLevel_WARN) << __FILE__ << " Bad file open or parse of config file <"
52  << configFilename << ">. Ignoring." << endl;
53  }
54  else
55  {
56  configJsonStream >> configJson;
57  loadJSON(configJson);
58  }
59 }
60 
62 {
63 }
64 
66 {
67  // Add global usage options. Don't include ossimChipProcUtil options as not appropriate.
69 
70  // Set the general usage:
72  ossimString usageString = ap.getApplicationName();
73  usageString += " dem [options] \n\n";
74  usageString +=
75  "Accesses DEM generation functionality given JSON request on stdin (or input file if\n"
76  "-i specified). The response JSON is output to stdout unless -o option is specified.\n";
77  au->setCommandLineUsage(usageString);
78 
79  // Set the command line options:
80  au->addCommandLineOption("--algorithms",
81  "List available DEM generation algorithms");
82  au->addCommandLineOption("-i <filename>",
83  "Reads request JSON from the input file specified instead of stdin.");
84  au->addCommandLineOption("-o <filename>",
85  "Outputs response JSON to the output file instead of stdout.");
86  au->addCommandLineOption("--parameters",
87  "List all algorithm parameters with default values.");
88  au->addCommandLineOption("-v",
89  "Verbose. All non-response (debug) output to stdout is enabled.");
90 }
91 
93 {
94  string ts1;
97 
98  if (!ossimTool::initialize(ap))
99  return false;
100  if (m_helpRequested)
101  return true;
102 
103  if ( ap.read("-v"))
104  m_verbose = true;
105 
106  if ( ap.read("--algorithms"))
108 
109  if ( ap.read("--parameters"))
111 
112  if ( ap.read("-i", sp1))
113  {
114  ifstream s (ts1);
115  if (s.fail())
116  {
117  CFATAL<<__FILE__<<" Could not open input file <"<<ts1<<">";
118  return false;
119  }
120  try
121  {
122  Json::Value queryJson;
123  s >> queryJson;
124  loadJSON(queryJson);
125  }
126  catch (exception& e)
127  {
128  ossimNotify(ossimNotifyLevel_FATAL)<<__FILE__<<" Could not parse input JSON <"<<ts1<<">";
129  return false;
130  }
131  }
132 
133  if ( ap.read("-o", sp1))
134  {
135 
136  ofstream* s = new ofstream (ts1);
137  if (s->fail())
138  {
139  CFATAL<<__FILE__<<" Could not open output file <"<<ts1<<">";
140  return false;
141  }
142  m_outputStream = s;
143  }
144  else
145  m_outputStream = &clog;
146 
147  return true;
148 }
149 
150 void ossimDemTool::loadJSON(const Json::Value& queryRoot)
151 {
152  ostringstream xmsg;
153  xmsg<<"ossimDemTool::loadJSON() ";
154 
155  // Fetch the desired method from the JSON if provided, otherwise rely on command line options:
156  m_method = GENERATE;
157  string method = queryRoot["method"].asString();
158  if (method == "getAlgorithms")
160  else if (method == "getParameters")
162 
163  m_outputDemFile = queryRoot["filename"].asString();
164  if (m_outputDemFile.empty())
165  {
166  m_outputDemFile = "dem-result";
169  }
170 
171  // Fetch the desired algorithm or configuration:
173  string algorithm = queryRoot["algorithm"].asString();
174  if (algorithm.empty())
175  algorithm = "asp"; // default for now
176 
177  // Assign enum data member used throughout the service:
178  if (algorithm == "asp")
179  m_algorithm = ASP;
180  else if (algorithm == "omg")
181  m_algorithm = OMG;
182  else
184 
185  if (queryRoot.isMember("postSpacing"))
186  m_postSpacing = queryRoot["postSpacing"].asDouble();
187 
188 
189  // If parameters were provided in the JSON payload, have the config override the defaults:
190  m_parameters = queryRoot["parameters"];
191  m_atpParameters = queryRoot["atpParameters"];
192  //if (!m_parameters.isNull())
193  // config.loadJSON(m_parameters);
194 
195  //if (config.diagnosticLevel(2))
196  // clog<<"\nDEM configuration after loading:\n"<<config<<endl;
197 
198  if (m_method == GENERATE)
199  {
200  // Load the active photoblock from JSON.
201  // The root JSON object can optionally contain a photoblock with image list contained. If so,
202  // use that object to load images, otherwise use the root.
203  if (queryRoot.isMember("photoblock"))
204  m_photoBlock.reset(new PhotoBlock(queryRoot["photoblock"]));
205  else
206  m_photoBlock.reset(new PhotoBlock(queryRoot));
207  }
208 }
209 
211 {
212  ostringstream xmsg;
213 
214  try
215  {
216  switch (m_method)
217  {
218  case GET_ALGO_LIST:
219  getAlgorithms();
220  break;
221  case GET_PARAMS:
222  getParameters();
223  break;
224  case GENERATE:
225  // Establish server-side output filename:
226  if (m_algorithm == ASP)
227  doASP();
228  if (m_algorithm == OMG)
229  doOMG();
230  else
231  {
232  m_responseJSON["status"] = "failed";
233  m_responseJSON["report"] = "Unsupported algorthm requested for DEM generation.";
234  }
235  break;
236  default:
237  xmsg << "Fatal: No method selected prior to execute being called. I don't know what to do!";
238  throw ossimException(xmsg.str());
239  }
240 
241  // Serialize JSON object for return:
242  if (m_outputStream)
243  (*m_outputStream) << m_responseJSON;
244  }
245  catch(ossimException &e)
246  {
247  CFATAL<<"Exception: "<<e.what()<<endl;
248  if (m_outputStream)
249  *m_outputStream<<"{ \"ERROR\": \"" << e.what() << "\" }\n"<<endl;
250  }
251 
252  // close any open file streams:
253  ofstream* so = dynamic_cast<ofstream*>(m_outputStream);
254  if (so)
255  {
256  so->close();
257  delete so;
258  }
259 
260  return true;
261 }
262 
264 {
265 }
266 
268 {
269  m_responseJSON.clear();
270 
271  Json::Value algoList;
272  algoList[0]["name"] = "asp";
273  algoList[0]["description"] = "NASA Ames Stereo Pipeline";
274  algoList[0]["name"] = "omg";
275  algoList[0]["description"] = "OSSIM/MSP Generator";
276 
277  m_responseJSON["algorithms"] = algoList;
278 }
279 
281 {
282  m_responseJSON.clear();
283  Json::Value params;
285  m_responseJSON["parameters"] = params;
286 }
287 
289 {
290  static const char* MODULE = "ossimDemTool::doASP() ";
291  ostringstream xmsg;
292  xmsg<<MODULE;
293 
294  if (!m_photoBlock || (m_photoBlock->getImageList().size() < 2))
295  {
296  xmsg << "No photoblock has been declared or it has less than two images. Cannot perform ATP.";
297  throw ossimException(xmsg.str());
298  }
299 
300  // Start the ASP command ine:
301  ostringstream cmd;
302  string cmdPath (getenv("NGTASP_BIN_DIR"));
303  cmd<<cmdPath<<"/stereo";
304 
305  // First obtain list of images from photoblock:
306  std::vector<shared_ptr<Image> >& imageList = m_photoBlock->getImageList();
307  std::vector<ossimFilename> rpcFilenameList;
308  int numImages = (int) imageList.size();
309  int numPairs = (numImages * (numImages-1))/2; // triangular number assumes all overlap
310  for (int i=0; i<numImages; i++)
311  {
312  // Establish existence of RPB, and create it if not available:
313  shared_ptr<Image> image = imageList[i];
314  ossimFilename imageFilename (image->getFilename());
315  ossimFilename rpcFilename(imageFilename);
316  rpcFilename.setExtension("RPB");
317  if (!rpcFilename.isReadable())
318  {
319  ossimRpcSolver rpcSolver;
320  if (!rpcSolver.solve(imageFilename))
321  {
322  xmsg << "Error encountered in solving for RPC coefficients..";
323  throw ossimException(xmsg.str());
324  }
325 
326  ossimRefPtr<ossimRpcModel> rpcModel = rpcSolver.getRpcModel();
327  ofstream rpbFile (rpcFilename.string());
328  if (rpbFile.fail() || !rpcModel->toRPB(rpbFile))
329  {
330  xmsg << "Error encountered writing RPC to file <"<<rpcFilename<<">.";
331  throw ossimException(xmsg.str());
332  }
333  }
334  cout<<"Generated RPC for "<<imageFilename<<endl;
335  rpcFilenameList.emplace_back(rpcFilename);
336  cmd << " " <<imageFilename;
337  }
338 
339  // Loop to add model filenames to the command line:
340  for (const auto &rpcFilename : rpcFilenameList)
341  cmd << " " <<rpcFilename;
342 
343  cmd<<" "<<m_outputDemFile<<ends;
344  cout << "\nSpawning command: "<<cmd.str()<<endl;
345  if (system(cmd.str().c_str()))
346  {
347  xmsg << "Error encountered running DEM generation command.";
348  throw ossimException(xmsg.str());
349  }
350 }
351 
353 {
354  static const char* MODULE = "ossimDemTool::doOMG() ";
355  ostringstream xmsg;
356  xmsg<<MODULE;
357 
358  if (!m_photoBlock || (m_photoBlock->getImageList().size() < 2))
359  {
360  xmsg << "No photoblock has been declared or it has less than two images. Cannot perform ATP.";
361  throw ossimException(xmsg.str());
362  }
363 
364  // OMG uses the ATP plugin:
365  ossimRefPtr<ossimTool> atpTool = ossimToolRegistry::instance()->createTool("ossimAtpTool") ;
366  if (!atpTool)
367  {
368  xmsg << "OMG algorithm requires the ATP plugin but plugin not found in registry.";
369  throw ossimException(xmsg.str());
370  }
371 
372  // Pass along any user parameter modifications to the ATP tool:
373  Json::Value atpJson;
374  atpJson["algorithm"] = "crosscorr";
375  atpJson["method"] = "generate";
376  m_photoBlock->saveJSON(atpJson["photoblock"]);
377  atpJson["parameters"] = m_atpParameters;
378  CINFO<<atpJson<<endl; //TODO REMOVE
379  atpTool->loadJSON(atpJson);
380 
381  // Generate dense tiepoint field:
382  atpTool->execute();
383  Json::Value omgJson;
384  atpTool->saveJSON(omgJson);
385 
386  // Use dense points to do intersections to generate point cloud
387  m_photoBlock->loadJSON(omgJson);
388  int num_tiepoints = m_photoBlock->getTiePointList().size();
389 
390  // Get instance of MSP tool for performing n-way intersection:
391  ossimRefPtr<ossimTool> mspTool = ossimToolRegistry::instance()->createTool("ossimMspTool") ;
392  if (!mspTool)
393  {
394  xmsg << "OMG algorithm requires the MSP plugin but plugin not found in registry.";
395  throw ossimException(xmsg.str());
396  }
397 
398  // Prepare the JSON used to communicate with the mensuration tool and kick off bulk mensuration:
399  omgJson["service"] = "mensuration";
400  omgJson["method"] = "pointExtraction";
401  omgJson["outputCoordinateSystem"] = "ecf";
402  mspTool->loadJSON(omgJson);
403  mspTool->execute();
404  Json::Value mspJson;
405  mspTool->saveJSON(mspJson);
406 
407  // Save resulting geo point to point cloud:
408  const Json::Value& observations = mspJson["mensurationReport"];
409  vector<ossimEcefPoint> pointCloud;
410  for (const auto &observation : observations)
411  {
412  ossimEcefPoint ecfPt(observation["x"].asDouble(),
413  observation["y"].asDouble(),
414  observation["z"].asDouble());
415  pointCloud.emplace_back(ecfPt);
416  }
417 
418  // Convert point cloud to DEM
421  pcih->setPointCloudHandler(pch.get());
422  ossimImageSource* lastSource = pcih.get();
423 
424  // TODO: Need to set up output projection to match desired GSD and units
425 
426  // TODO: Need smart interpolation resampling to fill in the nulls
427  // lastSource = resampler...
428 
429  // Set up the writer and output the DEM:
432  if (!writer)
433  {
434  xmsg << "Error encountered creating writer object given filename <"<<m_outputDemFile<<">.";
435  throw ossimException(xmsg.str());
436  }
437  writer->connectMyInputTo(0, lastSource);
438  if (!writer->execute())
439  {
440  xmsg << "Error encountered writing DEM file.";
441  throw ossimException(xmsg.str());
442  }
443 
444  // Need to populate response JSON with product filepath and statistics
446 }
447 
448 
449 
ossimFilename m_outputDemFile
Definition: ossimDemTool.h:58
std::string getApplicationName() const
return the application name, as specified by argv[0]
void getKwlTemplate(ossimKeywordlist &kwl) override
Assigns a template keywordlist to string for initializing derived classes.
void addCommandLineOption(const ossimString &option, const ossimString &explanation)
virtual void saveJSON(Json::Value &json) const
Fetch product as JSON object when applicable Always returns true since using exception on error...
Definition: ossimTool.h:59
std::basic_ostringstream< char > ostringstream
Class for char output memory streams.
Definition: ossimIosFwd.h:35
ossimFilename & appendTimestamp()
Convenience method to append a generic timestamp to the base-name portion of the filename.
Represents serializable keyword/value map.
bool m_helpRequested
Definition: ossimTool.h:150
virtual void loadJSON(const Json::Value &json_request)
Reads processing params from JSON object provided.
Definition: ossimTool.h:52
std::basic_ifstream< char > ifstream
Class for char input file streams.
Definition: ossimIosFwd.h:44
bool read(const std::string &str)
search for an occurance of a string in the argument list, on sucess remove that occurance from the li...
This code was derived from https://gist.github.com/mshockwave.
Definition: Barrier.h:8
Base class for maintaining parameters affecting the runtime configuration of OSSIM executables...
Definition: JsonConfig.h:64
ossimFilename & setPath(const ossimString &p)
Class used for rendering point cloud data into a raster tile.
static ossimImageWriterFactoryRegistry * instance()
bool setPointCloudHandler(ossimPointCloudHandler *pch)
Permits backdoor for setting the input point cloud handler object.
Class for representing MSP PhotoBlock.
Definition: PhotoBlock.h:25
void setCommandLineUsage(const ossimString &explanation)
Json::Value m_responseJSON
Definition: ossimDemTool.h:56
ossimApplicationUsage * getApplicationUsage()
bool isDir() const
const ossimRefPtr< ossimRpcModel > getRpcModel() const
Fetches the solved-for RPC model.
bool execute() override
Writes product to output file if applicable.
void getParameters()
virtual void saveJSON(Json::Value &params_json_node) const
Reads the params controlling the process from the JSON node named "parameters".
Definition: JsonConfig.cpp:447
virtual ossimTool * createTool(const std::string &typeName) const
void setUsage(ossimArgumentParser &ap) override
Initializes the aurgument parser with expected parameters and options.
bool solve(const ossimDrect &aoiBounds, ossimImageGeometry *geom, const double &pixel_tolerance=0.5)
Similar to the other solve methods except that the final grid size is established iteratively so that...
static ossimToolRegistry * instance()
double m_postSpacing
Definition: ossimDemTool.h:59
virtual void setUsage(ossimArgumentParser &ap)
Initializes the aurgument parser with expected parameters and options.
Definition: ossimTool.cpp:41
virtual const char * what() const
Returns the error message.
~ossimDemTool() override
Json::Value m_parameters
Definition: ossimDemTool.h:61
virtual bool initialize(ossimArgumentParser &ap)
Initializes from command line arguments.
Definition: ossimTool.cpp:58
virtual bool execute()=0
Writes product to output file if applicable.
virtual ossim_int32 connectMyInputTo(ossimConnectableObject *inputObject, bool makeOutputConnection=true, bool createEventFlag=true)
Will try to connect this objects input to the passed in object.
Method m_method
Definition: ossimDemTool.h:55
static ossimPreferences * instance()
static ossimDemToolConfig & instance()
Singleton implementation.
Algorithm m_algorithm
Definition: ossimDemTool.h:54
Json::Value m_atpParameters
Definition: ossimDemTool.h:62
#define CFATAL
This currently only support Rational poilynomial B format.
bool empty() const
Definition: ossimString.h:411
ossimImageFileWriter * createWriter(const ossimFilename &filename) const
std::basic_ofstream< char > ofstream
Class for char output file streams.
Definition: ossimIosFwd.h:47
std::ostream * m_outputStream
Definition: ossimDemTool.h:52
bool initialize(ossimArgumentParser &ap) override
Initializes from command line arguments.
ossimFilename & setExtension(const ossimString &e)
Sets the extension of a file name.
#define CINFO
void getAlgorithms()
OSSIMDLLEXPORT std::ostream & ossimNotify(ossimNotifyLevel level=ossimNotifyLevel_WARN)
void loadJSON(const Json::Value &json) override
Reads processing params from JSON object provided.
static const char * DESCRIPTION
Definition: ossimDemTool.h:25
virtual bool execute()
Calls: writeFile() writeMetaDataFiles()
std::shared_ptr< ossim::PhotoBlock > m_photoBlock
Definition: ossimDemTool.h:57
bool toRPB(std::ostream &out) const
Serialize to WorldView-style .RPB file to the stream provided.
const std::string & string() const
Definition: ossimString.h:414