Official websites use .gov
A .gov website belongs to an official government organization in the United States.

Secure .gov websites use HTTPS
A lock ( ) or https:// means you’ve safely connected to the .gov website. Share sensitive information only on official, secure websites.

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