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

U.S. Department of the Interior | U.S. Geological Survey
ISIS | Privacy & Disclaimers | Astrogeology Research Program
To contact us, please post comments and questions on the USGS Astrogeology Discussion Board
To report a bug, or suggest a feature go to: ISIS Github
File Modified: 07/13/2023 15:16:25