Isis 3 Programmer Reference
ShapeModelFactory.cpp
Go to the documentation of this file.
1 
24 #include "ShapeModelFactory.h"
25 
26 #include <string>
27 
28 #include "BulletShapeModel.h"
29 #include "BulletTargetShape.h"
30 #include "Cube.h"
31 #include "DemShape.h"
32 #include "EllipsoidShape.h"
33 #include "EmbreeShapeModel.h"
34 #include "EmbreeTargetManager.h"
36 #include "FileName.h"
37 #include "IException.h"
38 #include "IString.h"
39 #include "NaifDskShape.h"
40 #include "NaifStatus.h"
41 #include "PlaneShape.h"
42 #include "Projection.h"
43 #include "Preference.h"
44 #include "Pvl.h"
45 #include "PvlFlatMap.h"
46 #include "PvlGroup.h"
47 #include "PvlKeyword.h"
48 #include "Target.h"
49 
50 using namespace std;
51 
52 namespace Isis {
58  ShapeModelFactory::ShapeModelFactory() {
59  }
60 
61 
63  ShapeModelFactory::~ShapeModelFactory() {
64  }
65 
66 
79  ShapeModel *ShapeModelFactory::create(Target *target, Pvl &pvl) {
80 
81  // get kernels and instrument Pvl groups
82  PvlGroup &kernelsPvlGroup = pvl.findGroup("Kernels", Pvl::Traverse);
83 
84  // Get user preferences to determine preferred model
85  PvlFlatMap parameters(kernelsPvlGroup);
86  if ( Preference::Preferences().hasGroup("ShapeModel") ) {
87  parameters.merge(PvlFlatMap(Preference::Preferences().findGroup("ShapeModel")));
88  }
89 
90  // Do we need a sky shape model, member variable, or neither? For now treat sky as ellipsoid
91  bool skyTarget = target->isSky();
92 
93  // Determine if target is a plane??? target name has rings in it?
94  // Another keyword in label to indicate plane? What about lander/rovers?
95  // bool planeTarget = false;
96 
97  // shape model file name
98  QString shapeModelFilenames = "";
99 
100  // TODO: We differentiate between "Elevation" and "Shape" models on the
101  // labels, but we assign either one to the shapeModelFilename. Do we
102  // need a shapeModelFilename AND an elevationModelFilename?
103  // is this historical? Interchangeable?
104  if (skyTarget) {
105  // Sky targets are ellipsoid shapes
106  }
107  else if (kernelsPvlGroup.hasKeyword("ElevationModel") &&
108  !kernelsPvlGroup["ElevationModel"].isNull()) {
109  shapeModelFilenames = (QString) kernelsPvlGroup["ElevationModel"];
110  }
111  else if (kernelsPvlGroup.hasKeyword("ShapeModel") &&
112  !kernelsPvlGroup["ShapeModel"].isNull()) {
113  shapeModelFilenames = (QString) kernelsPvlGroup["ShapeModel"];
114  }
115 
116  // Create shape model
117  ShapeModel *shapeModel = NULL;
118 
119  // TODO: If there is no shape model filename, the shape model type defaults to an
120  // ellipsoid (should it?).
121 
122  // This exception will be thrown at the end of this method if no shape model is constructed.
123  // More specific exceptions will be appended before throwing this error.
124  IException finalError(IException::Programmer,
125  "Unable to create a shape model from given target and pvl.",
126  _FILEINFO_);
127 
128  if (shapeModelFilenames == "") {
129  // No file name given. If EllipsoidShape throws an error or returns null, the following
130  // exception will be appended to the finalError.
131  QString msg = "Unable to construct an Ellipsoid shape model.";
132 
133  try {
134  shapeModel = new EllipsoidShape(target);
135  }
136  catch (IException &e) {
137  // No file name given and ellipsoid fails. Append e to new exception
138  // with above message. Append this to finalError and throw.
139  finalError.append(IException(e, IException::Unknown, msg, _FILEINFO_));
140  throw finalError;
141  }
142  // in case no error was thrown, but constructor returned NULL
143  finalError.append(IException(IException::Unknown, msg, _FILEINFO_));
144  }
145  else if (shapeModelFilenames == "RingPlane") {
146  // No file name given, RingPlane indicated. If PlaneShape throws an error or returns
147  // null, the following exception will be appended to the finalError.
148  QString msg = "Unable to construct a RingPlane shape model.";
149 
150  try {
151  shapeModel = new PlaneShape(target, pvl);
152  }
153  catch (IException &e) {
154  // No file name given, RingPlane specified. Append a message to the finalError and throw it.
155  finalError.append(IException(e, IException::Unknown, msg, _FILEINFO_));
156  throw finalError;
157  }
158  // in case no error was thrown, but constructor returned NULL
159  finalError.append(IException(IException::Unknown, msg, _FILEINFO_));
160  }
161  else { // assume shape model given is a Bullet, Embree, NAIF DSK or DEM cube file name
162 
163  QString preferred = parameters.get("RayTraceEngine", "None").toLower();
164  QString onerror = parameters.get("OnError", "Continue").toLower();
165  double tolerance = toDouble(parameters.get("Tolerance", toString(DBL_MAX)));
166 
167  // A file error message will be appened to the finalError, if no shape model is constructed.
168  QString fileErrorMsg = "Invalid shape model file ["
169  + shapeModelFilenames + "] in Kernels group.";
170  IException fileError(IException::Io, fileErrorMsg, _FILEINFO_);
171 
172  //-------------- Check for bullet engine first -------------------------------//
173  if ( "bullet" == preferred ) {
174  // Check to see of ISIS cube DEMs get a pass
175  FileName v_shapefile(shapeModelFilenames);
176  QString ext = v_shapefile.extension().toLower();
177  // Cubes are not supported at this time.
178 
179  try {
180  BulletTargetShape *bullet = BulletTargetShape::load(shapeModelFilenames);
181  if ( 0 == bullet ) {
182 
183  // Bullet failed to load the kernel...test failure conditions
184  if ("cub" == ext) {
185  onerror = "fail"; // This is fatal no matter the condition
186  QString mess = "Bullet could not initialize ISIS Cube DEM";
187  throw IException(IException::Unknown, mess, _FILEINFO_);
188  }
189 
190  // Always throw an error in this case
191  QString b_msg = "Bullet could not initialize DEM!";
192  throw IException(IException::Unknown, b_msg, _FILEINFO_);
193  }
194  else {
195 
196  // Allocate the real shape model
197  BulletShapeModel *b_model = new BulletShapeModel(bullet, target, pvl);
198  b_model->setTolerance(tolerance);
199 
200  // Do this here, otherwise default behavior will ensue from here on out
201  kernelsPvlGroup.addKeyword(PvlKeyword("RayTraceEngine", preferred), PvlContainer::Replace);
202  kernelsPvlGroup.addKeyword(PvlKeyword("OnError", onerror), PvlContainer::Replace);
203  kernelsPvlGroup.addKeyword(PvlKeyword("Tolerance", toString(tolerance)),
204  PvlContainer::Replace);
205 
206  return ( b_model );
207  }
208  } catch (IException &ie) {
209  fileError.append(ie);
210  QString mess = "Unable to create preferred BulletShapeModel";
211  fileError.append(IException(IException::Unknown, mess, _FILEINFO_));
212  if ("fail" == onerror) throw fileError;
213  }
214 
215  // Don't have ShapeModel yet - invoke pre-exising behavior (2017-03-23)
216  }
217 
218  //-------------- Check for Embree engine -------------------------------//
219  if ( "embree" == preferred ) {
220 
221  // Check to see of ISIS cube DEMs get a pass
222  FileName v_shapefile(shapeModelFilenames);
223  QString ext = v_shapefile.extension().toLower();
224  // Cubes are not supported at this time
225 
226  try {
227 
228  // Allocate the shape model
229  EmbreeTargetManager *targetManager = EmbreeTargetManager::getInstance();
230  EmbreeShapeModel *embreeModel = new EmbreeShapeModel(target, shapeModelFilenames,
231  targetManager);
232  embreeModel->setTolerance(tolerance);
233 
234  // Do this here, otherwise default behavior will ensue from here on out
235  kernelsPvlGroup.addKeyword(PvlKeyword("RayTraceEngine", preferred), PvlContainer::Replace);
236  kernelsPvlGroup.addKeyword(PvlKeyword("OnError", onerror), PvlContainer::Replace);
237  kernelsPvlGroup.addKeyword(PvlKeyword("Tolerance", toString(tolerance)),
238  PvlContainer::Replace);
239 
240  return ( embreeModel );
241 
242  } catch (IException &ie) {
243  fileError.append(ie);
244  QString mess = "Unable to create preferred EmbreeShapeModel";
245  fileError.append(IException(IException::Unknown, mess, _FILEINFO_));
246  if ("fail" == onerror) throw fileError;
247  }
248  }
249 
250  //-------------- Is the shape model a NAIF DSK? ------------------------------//
251 
252  // If NaifDskShape throws an error or returns null and DEM construction is
253  // unsuccessful, the following exception will be appended to the fileError.
254  QString msg = "The given shape model file is not a valid NAIF DSK file. "
255  "Unable to construct a NAIF DSK shape model.";
256  IException dskError(IException::Unknown, msg, _FILEINFO_);
257 
258  try {
259  // try to create a NaifDskShape object
260  shapeModel = new NaifDskShape(target, pvl);
261  }
262  catch (IException &e) {
263  // append a message to the fileError, but don't throw it.
264  // We will make sure it's not a DEM before throwing the error.
265  dskError.append(e);
266  }
267 
268  if (shapeModel == NULL) {
269 
270  // in case no error was thrown, but constructor returned NULL
271  fileError.append(dskError);
272 
273  //-------------- Is the shape model an ISIS DEM? ------------------------------//
274  // TODO Deal with stacks -- this could be a list of DEMs
275  Isis::Cube* shapeModelCube = new Isis::Cube;
276  try {
277  // first, try to open the shape model file as an Isis3 cube
278  shapeModelCube->open(FileName(shapeModelFilenames).expanded(), "r" );
279  }
280  catch (IException &e) {
281  // The file is neither a valid DSK nor an ISIS cube. Append a message and throw the error.
282  QString msg = "The given shape model file is not a valid ISIS DEM. "
283  "Unable to open as an ISIS cube.";
284  fileError.append(IException(e, IException::Unknown, msg, _FILEINFO_));
285  finalError.append(fileError);
286  throw finalError;
287  }
288 
289  Projection *projection = NULL;
290  try {
291  // get projection of shape model cube
292  projection = shapeModelCube->projection();
293  }
294  catch (IException &e) {
295  // The file is neither a valid DSK nor a valid ISIS DEM. Append message and throw the error.
296  QString msg = "The given shape model file is not a valid ISIS DEM cube. "
297  "It is not map-projected.";
298  fileError.append(IException(e, IException::Unknown, msg, _FILEINFO_));
299  finalError.append(fileError);
300  throw finalError;
301  }
302 
303  if (projection->IsEquatorialCylindrical()) {
304  // If the EquatorialCylindricalShape constructor throws an error or returns null, the
305  // following exception will be appended to the fileError. (Later added to the finalError)
306  QString msg = "Unable to construct a DEM shape model from the given "
307  "EquatorialCylindrical projected ISIS cube.";
308 
309  try {
310  shapeModel = new EquatorialCylindricalShape(target, pvl);
311  }
312  catch (IException &e) {
313  // The file is an equatorial cylindrical ISIS cube. Append fileError and throw.
314  fileError.append(IException(e, IException::Unknown, msg, _FILEINFO_));
315  finalError.append(fileError);
316  throw finalError;
317  }
318  // in case no error was thrown, but constructor returned NULL
319  fileError.append(IException(IException::Unknown, msg, _FILEINFO_));
320  }
321  else {
322  // If the DemShape constructor throws an error or returns null, the following
323  // exception will be appended to the fileError. (Later added to the finalError)
324  QString msg = "Unable to construct a DEM shape model "
325  "from the given projected ISIS cube file.";
326 
327  try {
328  shapeModel = new DemShape(target, pvl);
329  }
330  catch (IException &e) {
331  // The file is projected ISIS cube (assumed to be DEM). Append fileError and throw.
332  fileError.append(IException(e, IException::Unknown, msg, _FILEINFO_));
333  finalError.append(fileError);
334  throw finalError;
335  }
336  // in case no error was thrown, but constructor returned NULL
337  fileError.append(IException(IException::Unknown, msg, _FILEINFO_));
338  }
339 
340  delete shapeModelCube;
341 
342  }
343 
344  // in case no error was thrown, but DSK, Equatorial, or DEM constructor returned NULL
345  finalError.append(fileError);
346  }
347 
348  // TODO Add Naif DSK shape and stack?
349 
350  if (shapeModel == NULL) {
351  throw finalError;
352  }
353 
354  return shapeModel;
355  }
356 } // end namespace isis
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
Define plane shape model.
Definition: PlaneShape.h:60
PvlGroupIterator findGroup(const QString &name, PvlGroupIterator beg, PvlGroupIterator end)
Find a group with the specified name, within these indexes.
Definition: PvlObject.h:141
File name manipulation and expansion.
Definition: FileName.h:116
void append(const IException &exceptionSource)
Appends the given exception (and its list of previous exceptions) to this exception&#39;s causational exc...
Definition: IException.cpp:425
Namespace for the standard library.
void setTolerance(const double &tolerance)
Set the tolerance used when checking if the stored surface point is visible.
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:226
void addKeyword(const PvlKeyword &keyword, const InsertMode mode=Append)
Add a keyword to the container.
Projection * projection()
Definition: Cube.cpp:1439
double toDouble(const QString &string)
Global function to convert from a string to a double.
Definition: IString.cpp:164
Define shapes and provide utilities for Isis3 targets.
Class for managing the construction and destruction of EmbreeTargetShapes.
bool isSky() const
Return if our target is the sky.
Definition: Target.cpp:180
Define shapes and provide utilities for shapes stored as Isis3 EquatorialCylindrical map...
Base class for Map Projections.
Definition: Projection.h:171
QString get(const QString &key, const int &index=0) const
Gets the value of a keyword in the PvlFlatMap.
Definition: PvlFlatMap.cpp:593
Provides a flat map of PvlKeywords.
Definition: PvlFlatMap.h:236
Define shapes and provide utilities for targets stored as Isis3 maps.
Definition: DemShape.h:67
Contains multiple PvlContainers.
Definition: PvlGroup.h:57
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
Provides support for NAIF&#39;s Digital Shape Kernel (DSK)
Definition: NaifDskShape.h:58
A single keyword-value pair.
Definition: PvlKeyword.h:98
void open(const QString &cfile, QString access="r")
This method will open an isis cube for reading or reading/writing.
Definition: Cube.cpp:544
Shape model that uses the Bullet library to perform ray tracing.
Container for cube-like labels.
Definition: Pvl.h:135
This class is used to create and store valid Isis3 targets.
Definition: Target.h:76
int merge(const PvlFlatMap &other)
Adds the keywords from another PvlFlatMap.
Definition: PvlFlatMap.cpp:697
Define shapes and provide utilities for Isis3 targets.
Definition: ShapeModel.h:78
Isis exception class.
Definition: IException.h:107
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
QString extension() const
Returns the last extension of the file name.
Definition: FileName.cpp:194
virtual bool IsEquatorialCylindrical()
This method returns true if the projection is equatorial cylindrical.
Definition: Projection.cpp:237
void setTolerance(const double &tolerance)
Sets the occlusion tolerance.
Bullet Target Shape for planetary bodies.
General purpose Embree ray tracing model.
IO Handler for Isis Cubes.
Definition: Cube.h:170