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