Isis 3 Programmer Reference
Orthographic.cpp
1
6/* SPDX-License-Identifier: CC0-1.0 */
7#include "Orthographic.h"
8
9#include <cfloat>
10#include <cmath>
11#include <iomanip>
12
13#include <QDebug>
14#include "Constants.h"
15#include "IException.h"
16#include "TProjection.h"
17#include "Pvl.h"
18#include "PvlGroup.h"
19#include "PvlKeyword.h"
20
21using namespace std;
22namespace Isis {
39 Orthographic::Orthographic(Pvl &label, bool allowDefaults) :
40 TProjection::TProjection(label) {
41 try {
42 // Try to read the mapping group
43 PvlGroup &mapGroup = label.findGroup("Mapping", Pvl::Traverse);
44
45 /*
46 * Compute and write the default center longitude if allowed and
47 * necessary.
48 */
49 if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLongitude"))) {
50 double lon = (m_minimumLongitude + m_maximumLongitude) / 2.0;
51 mapGroup += PvlKeyword("CenterLongitude", toString(lon));
52 }
53
54 /*
55 * Compute and write the default center latitude if allowed and
56 * necessary
57 */
58 if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLatitude"))) {
59 double lat = (m_minimumLatitude + m_maximumLatitude) / 2.0;
60 mapGroup += PvlKeyword("CenterLatitude", toString(lat));
61 }
62
63 // Get the center longitude & latitude
64 m_centerLongitude = mapGroup["CenterLongitude"];
65 m_centerLatitude = mapGroup["CenterLatitude"];
66 if (IsPlanetocentric()) {
68 }
69
70 //Restrict center longitude to avoid converting between domains.
71 if ((m_centerLongitude < -360.0) || (m_centerLongitude > 360.0)) {
72 QString msg = "The center longitude cannot exceed [-360, 360]. "
73 "[" + toString(m_centerLongitude) + "] is not valid";
74 throw IException(IException::User, msg, _FILEINFO_);
75 }
76
77 // convert to radians, adjust for longitude direction
78 m_centerLongitude *= PI / 180.0;
79 m_centerLatitude *= PI / 180.0;
81
82 // Calculate sine & cosine of center latitude
85
86 /*
87 * This projection has a limited lat/lon range (can't do the world)
88 * So let's make sure that our lat/lon range is properly restricted!
89 * The equation: m_sinph0 * sinphi + m_cosph0 * cosphi * coslon tells us
90 * if we are inside of the projection.
91 *
92 * m_sinph0 = sin(center lat)
93 * sinphi = sin(lat)
94 * m_cosph0 = cos(center lat)
95 * cosphi = cos(lat)
96 * coslon = cos(lon - centerLon)
97 *
98 * Let's apply this equation at the extremes to minimize our lat/lon range
99 */
100 double sinphi, cosphi, coslon;
101
102 /* Can we project at the minlat, center lon? If not, then we should move
103 * up the min lat to be inside the image.
104 */
105 sinphi = sin(m_minimumLatitude * PI / 180.0);
106 cosphi = cos(m_minimumLatitude * PI / 180.0);
107 coslon = 1.0; // at lon=centerLon: cos(lon-centerLon) = cos(0) = 1
108 if (m_sinph0 * sinphi + m_cosph0 * cosphi * coslon < 1E-10) {
109 /*solve for x: a * sin(x) + b * cos(x) * 1 = 0
110 * a * sin(x) + b * cos(x) = 0
111 * a * sin(x) = - b * cos(x)
112 * -(a * sin(x)) / b = cos(x)
113 * -(a / b) = cos(x) / sin(x)
114 * -(b / a) = sin(x) / cos(x)
115 * -(b / a) = tan(x)
116 * arctan(-(b / a)) = x
117 * arctan(-(m_cosph0 / m_sinph0)) = x
118 */
119 double newMin = atan2(- m_cosph0, m_sinph0) * 180.0 / PI;
120 if (newMin > m_minimumLatitude) {
121 m_minimumLatitude = newMin;
122 } // else something else is off (i.e. longitude range)
123 }
124
125 //Restrict the longitude range to 360 degrees to simplify comparisons.
126 if ((m_maximumLongitude - m_minimumLongitude) > 360.0) {
127 QString msg = "The longitude range cannot exceed 360 degrees.";
128 throw IException(IException::User, msg, _FILEINFO_);
129 }
130
131 sinphi = sin(m_minimumLatitude * PI / 180.0);
132 cosphi = cos(m_minimumLatitude * PI / 180.0);
133
134 /* Can we project at the maxlat, center lon? If not, then we should move
135 * down the max lat to be inside the image.
136 */
137 sinphi = sin(m_maximumLatitude * PI / 180.0);
138 cosphi = cos(m_maximumLatitude * PI / 180.0);
139 coslon = 1.0; // at lon=centerLon: cos(lon-centerLon) = cos(0) = 1
140 if (m_sinph0 * sinphi + m_cosph0 * cosphi * coslon < 1E-10) {
141 // see above equations for latitude
142 double newMax = atan2(- m_cosph0, m_sinph0) * 180.8 / PI;
143 if (newMax < m_maximumLatitude && newMax > m_minimumLatitude) {
144 m_maximumLatitude = newMax;
145 } // else something else is off (i.e. longitude range)
146 }
147 }
148 catch(IException &e) {
149 QString message = "Invalid label group [Mapping]";
150 throw IException(e, IException::Io, message, _FILEINFO_);
151 }
152 }
153
157
167 if (!Projection::operator==(proj)) return false;
168 // dont do the below it is a recusive plunge
169 Orthographic *ortho = (Orthographic *) &proj;
170 if ((ortho->m_centerLongitude != m_centerLongitude) ||
171 (ortho->m_centerLatitude != m_centerLatitude)) return false;
172 return true;
173 }
174
180 QString Orthographic::Name() const {
181 return "Orthographic";
182 }
183
190 QString Orthographic::Version() const {
191 return "1.0";
192 }
193
204 //Snyder pg. 45
205 // no distortion at center of projection (centerLatitude, centerLongitude)
206 return m_centerLatitude * 180.0 / PI;// Change to true scale
207 }
208
221 bool Orthographic::SetGround(const double lat, const double lon) {
222 // Convert longitude to radians & clean up
223 m_longitude = lon;
224 double lonRadians = lon * PI / 180.0;
225 if (m_longitudeDirection == PositiveWest) lonRadians *= -1.0;
226
227 // Now convert latitude to radians & clean up ... it must be planetographic
228 m_latitude = lat;
229 double latRadians = lat;
230 if (IsPlanetocentric()) latRadians = ToPlanetographic(latRadians);
231 latRadians *= PI / 180.0;
232
233 // Compute helper variables
234 double deltaLon = (lonRadians - m_centerLongitude);
235 double sinphi = sin(latRadians);
236 double cosphi = cos(latRadians);
237 double coslon = cos(deltaLon);
238
239 // Lat/Lon cannot be projected
240 double g = m_sinph0 * sinphi + m_cosph0 * cosphi * coslon;
241 if ((g <= 0.0) && (fabs(g) > 1.0e-10)) {
242 m_good = false;
243 return m_good;
244 }
245
246 // Compute the coordinates
247 double x = m_equatorialRadius * cosphi * sin(deltaLon);
248 double y = m_equatorialRadius * (m_cosph0 * sinphi - m_sinph0 * cosphi * coslon);
249
250 SetComputedXY(x, y);
251 m_good = true;
252 return m_good;
253 }
254
268 bool Orthographic::SetCoordinate(const double x, const double y) {
269 // Save the coordinate
270 SetXY(x, y);
271
272 // Declare instance variables and calculate rho
273 double rho, con, z, sinz, cosz;
274 const double epsilon = 1.0e-10;
275 rho = sqrt(GetX() * GetX() + GetY() * GetY());
276
277 /* Error calculating rho - should be less than equatorial radius.
278 *
279 * This if statement included "fabs(rho - m_equatorialRadius) < 1E-10 ||"
280 * to make the method more stable for limbs but this caused a false failure
281 * for some images when rho == m_equatorialRadius.
282 */
283 if (rho > m_equatorialRadius) {
284 m_good = false;
285 return m_good;
286 }
287
288 // Calculate the latitude and longitude
290 if (fabs(rho) <= epsilon) {
292 }
293 else {
294 con = rho / m_equatorialRadius;
295 if (con > 1.0) con = 1.0;
296 if (con < -1.0) con = -1.0;
297 z = asin(con);
298 sinz = sin(z);
299 cosz = cos(z);
300 con = cosz * m_sinph0 + GetY() * sinz * m_cosph0 / rho;
301 if (con > 1.0) con = 1.0;
302 if (con < -1.0) con = -1.0;
303 m_latitude = asin(con);
304 con = fabs(m_centerLatitude) - HALFPI;
305 if (fabs(con) <= epsilon) {
306 if (m_centerLatitude >= 0.0) {
307 m_longitude += atan2(GetX(), -GetY());
308 }
309 else {
310 m_longitude += atan2(GetX(), GetY());
311 }
312 }
313 else {
314 con = cosz - m_sinph0 * sin(m_latitude);
315 if ((fabs(con) >= epsilon) || (fabs(GetX()) >= epsilon)) {
316 m_longitude += atan2(GetX() * sinz * m_cosph0, con * rho);
317 }
318 }
319 }
320
321 // Convert to degrees
322 m_latitude *= 180.0 / PI;
323 m_longitude *= 180.0 / PI;
324
325 // Cleanup the longitude
327
328 /*
329 * When the longitude range is 0 to 360 and the seam is within the 180 displayable degrees,
330 * the longitude needs to be converted to its 360 lon domain counterpart. However, if the
331 * range is shifted out of the 0 to 360 range, the conversion is not necessary. For example,
332 * if the specified range is -180 to 180 and the clon is 0, the lon -90 is valid but will
333 * be converted to 270, which does not work with the comparison. The same idea applies if
334 * the range is 200 - 500 and the clon is 360. We want to display 270 to 450 (270 - 360 and
335 * 0 - 90). However, if 450 is converted to the 360 domain it becomes 90 which is no longer
336 * within the original 200 to 500 range.
337 */
338 // These need to be done for circular type projections
341
342 // Cleanup the latitude
343 if (IsPlanetocentric())
345
346 m_good = true;
347 return m_good;
348 }
349
373 bool Orthographic::XYRange(double &minX, double &maxX,
374 double &minY, double &maxY) {
375 double lat, lon;
376
377 //Restrict lon range to be between -360 and 360
378 double adjustedLon;
379 double adjustedMinLon = To360Domain(MinimumLongitude());
380 double adjustedMaxLon = To360Domain(MaximumLongitude());
381 bool correctedMinLon = false;
382
383 if (adjustedMinLon >= adjustedMaxLon) {
384 adjustedMinLon -= 360;
385 correctedMinLon = true;
386 }
387
388 // Check the corners of the lat/lon range
393
394 // Walk top and bottom edges
395 for (lat = m_minimumLatitude; lat <= m_maximumLatitude; lat += 0.01) {
396 lon = m_minimumLongitude;
397 XYRangeCheck(lat, lon);
398
399 lon = m_maximumLongitude;
400 XYRangeCheck(lat, lon);
401 }
402
403 // Walk left and right edges
404 for (lon = m_minimumLongitude; lon <= m_maximumLongitude; lon += 0.01) {
405 lat = m_minimumLatitude;
406 XYRangeCheck(lat, lon);
407
408 lat = m_maximumLatitude;
409 XYRangeCheck(lat, lon);
410 }
411
412 /*
413 * Walk the limb.
414 * When the pair is valid and within the correct range, reassign min/max x/y.
415 *
416 * , - ~ ~ ~ - ,
417 * , ' ' ,
418 * , _______ ,
419 * , | | , <----- Walking the limb would not affect an
420 * , | | , image that does not extend to the
421 * , |_______| , limits of the projection.
422 * , ________________________,_
423 * , | , |
424 * ,| , |
425 * |,___________________,_'___| <-- This corner would wrap around.
426 * ' - , _ _ _ , ' The image would rotate because the
427 * center lat was closer to the pole.
428 * This would allow for a more completely
429 * longitude range.
430 * _o__o__
431 * o| |o <------------This is the rotated view. If the image
432 * o |_______| o limits extend over the pole (due to the
433 * o * o offset of the center lat) it is possible
434 * o o to have a lon range that is larger than
435 * o o 180 degrees.
436 *
437 */
438
439 for (double angle = 0.0; angle <= 360.0; angle += 0.01) {
440 double x = m_equatorialRadius * cos(angle * PI / 180.0);
441 double y = m_equatorialRadius * sin(angle * PI / 180.0);
442
443 if (SetCoordinate(x, y)){
444
445 adjustedLon = To360Domain(m_longitude);
446 if (adjustedLon > To360Domain(MinimumLongitude()) && correctedMinLon) {
447 adjustedLon -= 360;
448 }
450 adjustedLon <= adjustedMaxLon &&
452 adjustedLon >= adjustedMinLon) {
453 m_minimumX = qMin(x, m_minimumX);
454 m_maximumX = qMax(x, m_maximumX);
455 m_minimumY = qMin(y, m_minimumY);
456 m_maximumY = qMax(y, m_maximumY);
457 XYRangeCheck(m_latitude, adjustedLon);
458 }
459 }
460 }
461 // Make sure everything is ordered
462 if (m_minimumX >= m_maximumX ||
464 return false;
465
466 // Return X/Y min/maxs
467 minX = m_minimumX;
468 maxX = m_maximumX;
469 minY = m_minimumY;
470 maxY = m_maximumY;
471
472 return true;
473 }
474
475
482 PvlGroup mapping = TProjection::Mapping();
483
484 mapping += m_mappingGrp["CenterLatitude"];
485 mapping += m_mappingGrp["CenterLongitude"];
486
487 return mapping;
488 }
489
497 mapping += m_mappingGrp["CenterLatitude"];
498
499 return mapping;
500 }
501
509
510 mapping += m_mappingGrp["CenterLongitude"];
511
512 return mapping;
513 }
514} // end namespace isis
515
529extern "C" Isis::Projection *OrthographicPlugin(Isis::Pvl &lab,
530 bool allowDefaults) {
531 return new Isis::Orthographic(lab, allowDefaults);
532}
Isis exception class.
Definition IException.h:91
@ User
A type of error that could only have occurred due to a mistake on the user's part (e....
Definition IException.h:126
@ Io
A type of error that occurred when performing an actual I/O operation.
Definition IException.h:155
Orthographic Map Projection.
PvlGroup MappingLatitudes()
This function returns the latitude keywords that this projection uses.
bool SetGround(const double lat, const double lon)
This method is used to set the latitude/longitude (assumed to be of the correct LatitudeType,...
PvlGroup Mapping()
This function returns the keywords that this projection uses.
double m_sinph0
Sine of the center latitude.
~Orthographic()
Destroys the Orthographic object.
QString Name() const
Returns the name of the map projection, "Orthographic".
QString Version() const
Returns the version of the map projection.
double m_centerLongitude
The center longitude for the map projection.
double m_centerLatitude
The center latitude for the map projection.
bool SetCoordinate(const double x, const double y)
This method is used to set the projection x/y.
Orthographic(Pvl &label, bool allowDefaults=false)
Constructs an Orthographic object.
double TrueScaleLatitude() const
Returns the center latitude, in degrees.
bool operator==(const Projection &proj)
Compares two Projection objects to see if they are equal.
bool XYRange(double &minX, double &maxX, double &minY, double &maxY)
This method is used to determine the x/y range which completely covers the area of interest specified...
PvlGroup MappingLongitudes()
This function returns the longitude keywords that this projection uses.
double m_cosph0
Cosine of the center latitude.
Base class for Map Projections.
Definition Projection.h:155
double m_maximumX
See minimumX description.
Definition Projection.h:326
double GetX() const
Calculates the unrotated form of current x value.
bool m_good
Indicates if the contents of m_x, m_y, m_latitude, and m_longitude are valid.
Definition Projection.h:300
double m_minimumX
The data elements m_minimumX, m_minimumY, m_maximumX, and m_maximumY are convience data elements when...
Definition Projection.h:317
PvlGroup m_mappingGrp
Mapping group that created this projection.
Definition Projection.h:329
double m_minimumY
See minimumX description.
Definition Projection.h:327
double GetY() const
Calculates the unrotated form of the current y value.
void SetXY(double x, double y)
This protected method is a helper for derived classes.
double m_maximumY
See minimumX description.
Definition Projection.h:328
void SetComputedXY(double x, double y)
This protected method is a helper for derived classes.
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
PvlGroupIterator findGroup(const QString &name, PvlGroupIterator beg, PvlGroupIterator end)
Find a group with the specified name, within these indexes.
Definition PvlObject.h:129
Base class for Map TProjections.
double m_longitude
This contains the currently set longitude value.
bool IsPlanetocentric() const
This indicates if the latitude type is planetocentric (as opposed to planetographic).
double m_minimumLatitude
Contains the minimum latitude for the entire ground range.
double m_maximumLongitude
Contains the maximum longitude for the entire ground range.
static double To180Domain(const double lon)
This method converts a longitude into the -180 to 180 domain.
double m_equatorialRadius
Polar radius of the target.
LongitudeDirection m_longitudeDirection
An enumerated type indicating the LongitudeDirection read from the labels.
virtual PvlGroup MappingLongitudes()
This function returns the longitude keywords that this projection uses.
int m_longitudeDomain
This integer is either 180 or 360 and is read from the labels.
double ToPlanetocentric(const double lat) const
This method converts a planetographic latitude to a planetocentric latitude.
virtual double MinimumLongitude() const
This returns the minimum longitude of the area of interest.
void XYRangeCheck(const double latitude, const double longitude)
This convience function is established to assist in the development of the XYRange virtual method.
virtual PvlGroup MappingLatitudes()
This function returns the latitude keywords that this projection uses.
double m_minimumLongitude
Contains the minimum longitude for the entire ground range.
@ PositiveWest
Longitude values increase in the westerly direction.
double m_maximumLatitude
Contains the maximum latitude for the entire ground range.
static double To360Domain(const double lon)
This method converts a longitude into the 0 to 360 domain.
virtual double MaximumLongitude() const
This returns the maximum longitude of the area of interest.
virtual PvlGroup Mapping()
This function returns the keywords that this projection uses.
double ToPlanetographic(const double lat) const
This method converts a planetocentric latitude to a planetographic latitude.
double m_latitude
This contains the currently set latitude value.
const double E
Sets some basic constants for use in ISIS programming.
Definition Constants.h:39
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
const double HALFPI
The mathematical constant PI/2.
Definition Constants.h:41
const double PI
The mathematical constant PI.
Definition Constants.h:40
Namespace for the standard library.