Isis 3 Programmer Reference
EquatorialCylindricalShape.cpp
2 
3 #include <QDebug>
4 
5 #include <algorithm>
6 #include <cfloat>
7 #include <string>
8 #include <iomanip>
9 #include <cmath>
10 #include <vector>
11 
12 #include <SpiceUsr.h>
13 #include <SpiceZfc.h>
14 #include <SpiceZmc.h>
15 
16 #include "Cube.h"
17 #include "IException.h"
18 // #include "Geometry3D.h"
19 #include "Latitude.h"
20 // #include "LinearAlgebra.h"
21 #include "Longitude.h"
22 #include "NaifStatus.h"
23 #include "SpecialPixel.h"
24 #include "SurfacePoint.h"
25 #include "Table.h"
26 
27 using namespace std;
28 
29 #define MAX(x,y) (((x) > (y)) ? (x) : (y))
30 
31 namespace Isis {
37  EquatorialCylindricalShape::EquatorialCylindricalShape(Target *target, Pvl &pvl) :
38  DemShape(target, pvl) {
39  setName("EquatorialCylindricalShape");
40 
41  m_minRadius = NULL;
42  m_maxRadius = NULL;
43 
44  // Read in the min/max radius of the DEM file and the Scale of the DEM
45  // file in pixels/degree
46  if (!demCube()->hasTable("ShapeModelStatistics")) {
47  QString msg = "The input cube references a ShapeModel that has "
48  "not been updated for the new ray tracing algorithm. All DEM "
49  "files must now be padded at the poles and contain a "
50  "ShapeModelStatistics table defining their minimum and maximum "
51  "radii values. The demprep program should be used to prepare the "
52  "DEM before you can run this program. There is more information "
53  "available in the documentation of the demprep program.";
55  }
56 
57  // Table table("ShapeModelStatistics", demCubeFile(), *demCube()->label()));
58  Table table("ShapeModelStatistics", demCube()->fileName(), *demCube()->label());
59 
60  // Find minimum and maximum radius
61  m_minRadius = new Distance(table[0]["MinimumRadius"], Distance::Kilometers);
62  m_maxRadius = new Distance(table[0]["MaximumRadius"], Distance::Kilometers);
63  }
64 
65 
70 
71  delete m_minRadius;
72  m_minRadius = NULL;
73 
74  delete m_minRadius;
75  m_maxRadius = NULL;
76  }
77 
78 
89  bool EquatorialCylindricalShape::intersectSurface(vector<double> observerBodyFixedPos,
90  vector<double> observerLookVectorToTarget) {
91 
92  // try to intersect the surface using the DemShape method
93  // if this is successful, return
94  //
95  // otherwise, (i.e. DemShape::intersectSurface() fails) we will attempt to
96  // intersect using the following iterative method...
97  if (!DemShape::intersectSurface(observerBodyFixedPos, observerLookVectorToTarget)) {
98 
99  SpiceDouble a = targetRadii()[0].kilometers();
100 
101  double plen = 0.0;
102  SpiceDouble plat, plon, pradius;
103  SpiceDouble pB[3];
104  double maxRadiusMetersSquared = m_maxRadius->kilometers() * m_maxRadius->kilometers();
105 
106  // Separate iteration algorithms are used for different projections -
107  // use this iteration for equatorial cylindrical type projections that
108  // failed to find an intersection with the DemShape method.
109  int maxit = 100;
110  int it = 0;
111  bool done = false;
112 
113  // Normalize the look vector
114  SpiceDouble ulookB[3];
115  ulookB[0] = observerLookVectorToTarget[0];
116  ulookB[1] = observerLookVectorToTarget[1];
117  ulookB[2] = observerLookVectorToTarget[2];
118  vhat_c(ulookB, ulookB);
119 
120  // Calculate the limb viewing angle to see if the line of sight is
121  // pointing away from the planet
122  SpiceDouble observer[3];
123  observer[0] = observerBodyFixedPos[0];
124  observer[1] = observerBodyFixedPos[1];
125  observer[2] = observerBodyFixedPos[2];
126 
127  SpiceDouble negobserver[3];
128  vminus_c(observer, negobserver);
129 
130  double psi0 = vsep_c(negobserver, ulookB); // find separation angle between -obsPos and look
131 
132  // If psi0 is greater than 90 degrees, then reject data as looking
133  // away from the planet and no proper tangent point exists in the
134  // direction that the spacecraft is looking
135  if (psi0 > PI / 2.0) {
136  setHasIntersection(false);
137  return hasIntersection();
138  }
139 
140 
141  #if 0
142  // JWB - The commented code here is an alternate way of doing this...
143  // which method has less expensive computing???
144  // I believe the alternate one may be (no need for cosine after naif routines)
145  //
146  //
147  // find the vector to the point found by orthogonally projecting the observer position vector
148  // onto the line containing the look direction vector
149  SpiceDouble perp[3];
150  vperp_c(&observer, &ulookB, perp);
151  SpiceDouble v[3];
152  vsub_c(&observer, &perp, v);
153  double dot = vdot_c(ulookB, v);
154  qDebug() << "dot = " << dot;
155  if (dot > 0.0) {
156  setHasIntersection(false);
157  return hasIntersection();
158  }
159 
160  // TODO: When added to ISIS library, see LinearAlgebra/Geometry3D equivalent methods:
161  // vperp=perpendicular{find max and normalize vectors, 2 dot products}
162  // vsep=Geometry3D::separationAngle{find max and normalize vectors, dot product}
163 
164  // replace calculation of psi0 and intersection check using psi0
165  // replace calculation of tvec, observerdist, cospsi0
166  // tvec below is equivalent to perp found above
167  // observerdist*cospsi0 is equivalent to vnorm_c(v) [pretty sure about this equivalence... need to verify]
168 
169  #endif
170 
171  // Calculate the vector to the surface point
172  SpiceDouble tvec[3];
173  double observerdist = vnorm_c(observer);
174  double cospsi0 = cos(psi0);
175  tvec[0] = observer[0] + observerdist * cospsi0 * ulookB[0];
176  tvec[1] = observer[1] + observerdist * cospsi0 * ulookB[1];
177  tvec[2] = observer[2] + observerdist * cospsi0 * ulookB[2];
178 
179  double tlen = vnorm_c(tvec);
180 
181  // Calculate distance along look vector to first and last test point
182  double radiusDiff = maxRadiusMetersSquared - tlen * tlen;
183 
184  // Make sure radiusDiff makes sense
185  if (radiusDiff >= 0.0) {
186  radiusDiff = sqrt(radiusDiff);
187  }
188  else {
189  setHasIntersection(false);
190  return hasIntersection();
191  }
192 
193  double d0 = observerdist * cospsi0 - radiusDiff;
194  double dm = observerdist * cospsi0 + radiusDiff;
195 
196  // Set the properties at the first test observation point
197  double d = d0;
198  SpiceDouble g1[3];
199  g1[0] = observer[0] + d0 * ulookB[0];
200  g1[1] = observer[1] + d0 * ulookB[1];
201  g1[2] = observer[2] + d0 * ulookB[2];
202 
203  double g1len = vnorm_c(g1);
204  SpiceDouble g1lat, g1lon, g1radius;
205  reclat_c(g1, &g1radius, &g1lon, &g1lat);
206  g1lat *= RAD2DEG;
207  g1lon *= RAD2DEG;
208 
209  if (g1lon < 0.0) g1lon += 360.0;
210 
211  SpiceDouble negg1[3];
212  vminus_c(g1, negg1);
213 
214  double psi1 = vsep_c(negg1, ulookB);
215 
216  // Set dalpha to be half the grid spacing for nyquist sampling
217  //double dalpha = (PI/180.0)/(2.0*p_demScale);
218  double cmin = cos((90.0 - 1.0 / (2.0 * demScale())) * DEG2RAD);
219  double dalpha = MAX(cos(g1lat * DEG2RAD), cmin) / (2.0 * demScale() * RAD2DEG);
220 
221  // Previous Sensor version used local version of this method with lat and lon doubles. Steven said
222  // it didn't make a significant difference in speed.
223  double r1 = (localRadius(Latitude(g1lat, Angle::Degrees),
224  Longitude(g1lon, Angle::Degrees))).kilometers();
225 
226  if (Isis::IsSpecial(r1)) {
227  setHasIntersection(false);
228  return hasIntersection();
229  }
230 
231  // Set the tolerance to a fraction of the equatorial radius, a
232  double tolerance = 3E-8 * a;
233 
234  // Main iteration loop
235  // Loop from g1 to gm stepping by angles of dalpha until intersection is found
236  while (!done) {
237 
238  if (d > dm) {
239  setHasIntersection(false);
240  return hasIntersection();
241  }
242 
243  it = 0;
244 
245  // Calculate the angle between the look vector and the planet radius at the current
246  // test point
247  double psi2 = psi1 + dalpha;
248 
249  // Calculate the step size
250  double dd = g1len * sin(dalpha) / sin(PI - psi2);
251 
252  // JAA: If we are moving along the vector at a smaller increment than the pixel
253  // tolerance we will be in an infinite loop. The infinite loop is elimnated by
254  // this test. Now the algorithm produces a jagged limb in phocube. This may
255  // be a function of the very low resolution of the Vesta DEM and could
256  // improve in the future
257  if (dd < tolerance) {
258  setHasIntersection(false);
259  return hasIntersection();
260  }
261 
262  // Calculate the vector to the current test point from the planet center
263  d = d + dd;
264 
265  SpiceDouble g2[3];
266  g2[0] = observer[0] + d * ulookB[0];
267  g2[1] = observer[1] + d * ulookB[1];
268  g2[2] = observer[2] + d * ulookB[2];
269 
270  double g2len = vnorm_c(g2);
271 
272  // Determine lat,lon,radius at this point
273  SpiceDouble g2lat, g2lon, g2radius;
274  reclat_c(g2, &g2radius, &g2lon, &g2lat);
275 
276  g2lat *= RAD2DEG;
277  g2lon *= RAD2DEG;
278 
279  if (g2lon < 0.0) g2lon += 360.0;
280 
281  // Previous Sensor version used local version of this method with lat and lon doubles to save
282  // According to Steven the savings was negligible.
283  double r2 = (localRadius(Latitude(g2lat, Angle::Degrees),
284  Longitude(g2lon, Angle::Degrees))).kilometers();
285 
286 
287  if (Isis::IsSpecial(r2)) {
288  setHasIntersection(false);
289  return hasIntersection();
290  }
291 
292  // Test for intersection
293  if (r2 > g2len) {
294  // An intersection has occurred. Interpolate between g1 and g2 to get the
295  // lat,lon of the intersect point.
296 
297  // If g1 and g2 straddle a hill, then we may need to iterate a few times
298  // until we are on the linear segment.
299  while (it < maxit && !done) {
300  // Calculate the fractional distance "v" to move along the look vector
301  // to the intersection point. Check to see if there was a convergence
302  // of the solution and the tolerance was too small to detect it.
303  double palt;
304  if ((g2len * r1 / r2 - g1len) == 0.0) {
305  setHasIntersection(true);
306  plen = pradius;
307  palt = 0.0;
308  done = true;
309  }
310  else {
311  double v = (r1 - g1len) / (g2len * r1 / r2 - g1len);
312  pB[0] = g1[0] + v * dd * ulookB[0];
313  pB[1] = g1[1] + v * dd * ulookB[1];
314  pB[2] = g1[2] + v * dd * ulookB[2];
315  plen = vnorm_c(pB);
316  reclat_c(pB, &pradius, &plon, &plat);
317  plat *= RAD2DEG;
318  plon *= RAD2DEG;
319  if (plon < 0.0) plon += 360.0;
320 
321  if (plon > 360.0) plon -= 360.0;
322 
323  // Previous Sensor version used local version of this method with lat and lon doubles. ..Why Jeff???
324  pradius = (localRadius(Latitude(plat, Angle::Degrees),
325  Longitude(plon, Angle::Degrees))).kilometers();
326 
327  if (Isis::IsSpecial(pradius)) {
328  setHasIntersection(false);
329  return hasIntersection();
330  }
331  palt = plen - pradius;
332 
333  // The altitude relative to surface is +ve at the observation point,
334  // so reset g1=p and r1=pradius
335  if (palt > tolerance) {
336  it = it + 1;
337  g1[0] = pB[0];
338  g1[1] = pB[1];
339  g1[2] = pB[2];
340  g1len = plen;
341  r1 = pradius;
342  dd = dd * (1.0 - v);
343 
344  // The altitude relative to surface -ve at the observation point,
345  // so reset g2=p and r2=pradius
346  }
347  else if (palt < -tolerance) {
348  it = it + 1;
349  g2[0] = pB[0];
350  g2[1] = pB[1];
351  g2[2] = pB[2];
352  g2len = plen;
353  r2 = pradius;
354  dd = dd * v;
355 
356  // We are within the tolerance, so the solution has converged
357  }
358  else {
359  setHasIntersection(true);
360  plen = pradius;
361  palt = 0.0;
362  done = true;
363  }
364  }
365  if (!done && it >= maxit) {
366  setHasIntersection(false);
367  return hasIntersection();
368  }
369  }
370  }
371 
372  g1[0] = g2[0];
373  g1[1] = g2[1];
374  g1[2] = g2[2];
375  g1len = g2len;
376  r1 = r2;
377  psi1 = psi2;
378 
379  // TODO: Examine how dalpha is computed at the limb for Vesta. It
380  // appears that eventually it gets really small and causes the loop
381  // to be neverending. Of course this could be happening for other
382  // limb images and we just have never tested the code. For example,
383  // Messenger with a DEM could cause the problem. As a result JAA
384  // put in a test (above) for dd being smaller than the pixel
385  // convergence tolerance. If so the loop exits without an
386  // intersection
387  dalpha = MAX(cos(g2lat * DEG2RAD), cmin) / (2.0 * demScale() * RAD2DEG);
388  } // end while
389 
390  SpiceDouble intersectionPoint[3];
391  SpiceBoolean found;
392 
394  surfpt_c(&observerBodyFixedPos[0], &observerLookVectorToTarget[0], plen, plen, plen,
395  intersectionPoint, &found);
397 
398  surfaceIntersection()->FromNaifArray(intersectionPoint);
399 
400  if (!found) {
401  setHasIntersection(false);
402  return hasIntersection();
403  }
404  else {
405  return hasIntersection();
406  }
407 
408  }
409 
410  setHasIntersection(true);
411  return hasIntersection();
412  }
413  // Do nothing since the DEM intersection was already successful
414 }
~EquatorialCylindricalShape()
Destructor for Isis3 Equatorial Cylindrical shape model.
const double PI
The mathematical constant PI.
Definition: Constants.h:56
bool hasIntersection()
Returns intersection status.
Definition: ShapeModel.cpp:362
Namespace for the standard library.
This class is designed to encapsulate the concept of a Latitude.
Definition: Latitude.h:63
The distance is being specified in kilometers.
Definition: Distance.h:58
SurfacePoint * surfaceIntersection() const
Returns the surface intersection for this ShapeModel.
Definition: ShapeModel.cpp:352
double kilometers() const
Get the distance in kilometers.
Definition: Distance.cpp:118
Distance measurement, usually in meters.
Definition: Distance.h:47
std::vector< Distance > targetRadii() const
Returns the radii of the body in km.
Definition: ShapeModel.cpp:459
void setHasIntersection(bool b)
Sets the flag to indicate whether this ShapeModel has an intersection.
Definition: ShapeModel.cpp:548
This class is designed to encapsulate the concept of a Longitude.
Definition: Longitude.h:52
bool intersectSurface(std::vector< double > observerPos, std::vector< double > lookDirection)
Finds the surface intersection point.
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
Definition: Angle.h:73
Define shapes and provide utilities for targets stored as Isis3 maps.
Definition: DemShape.h:67
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
A type of error that could only have occurred due to a mistake on the user&#39;s part (e...
Definition: IException.h:142
bool IsSpecial(const double d)
Returns if the input pixel is special.
Definition: SpecialPixel.h:212
Container for cube-like labels.
Definition: Pvl.h:135
const double E
Sets some basic constants for use in ISIS programming.
Definition: Constants.h:55
This class is used to create and store valid Isis3 targets.
Definition: Target.h:76
const double DEG2RAD
Multiplier for converting from degrees to radians.
Definition: Constants.h:59
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
Cube * demCube()
Returns the cube defining the shape model.
Definition: DemShape.cpp:336
Class for storing Table blobs information.
Definition: Table.h:77
Distance localRadius(const Latitude &lat, const Longitude &lon)
Gets the radius from the DEM, if we have one.
Definition: DemShape.cpp:258
Distance * m_minRadius
Minimum radius value in DEM file.
void FromNaifArray(const double naifValues[3])
A naif array is a c-style array of size 3.
double demScale()
Return the scale of the DEM shape, in pixels per degree.
Definition: DemShape.cpp:289
static void CheckErrors(bool resetNaif=true)
This method looks for any naif errors that might have occurred.
Definition: NaifStatus.cpp:43
Isis exception class.
Definition: IException.h:107
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
bool intersectSurface(std::vector< double > observerPos, std::vector< double > lookDirection)
Find the intersection point with the DEM.
Definition: DemShape.cpp:137
void setName(QString name)
Sets the shape name.
Definition: ShapeModel.cpp:526
const double RAD2DEG
Multiplier for converting from radians to degrees.
Definition: Constants.h:60
Distance * m_maxRadius
Maximum radius value in DEM file.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
T MAX(const T &A, const T &B)
Implement templatized MAX fumnction.