Isis 3 Programmer Reference
LambertConformal.cpp
1
6/* SPDX-License-Identifier: CC0-1.0 */
7#include "LambertConformal.h"
8
9#include <cmath>
10#include <cfloat>
11
12#include "Constants.h"
13#include "IException.h"
14#include "IString.h"
15#include "Projection.h"
16#include "Pvl.h"
17#include "PvlGroup.h"
18#include "PvlKeyword.h"
19
20using namespace std;
21namespace Isis {
41 LambertConformal::LambertConformal(Pvl &label, bool allowDefaults) :
42 TProjection::TProjection(label) {
43 try {
44 // Try to read the mapping group
45 PvlGroup &mapGroup = label.findGroup("Mapping", Pvl::Traverse);
46
47 // Compute and write the default center longitude if allowed and
48 // necessary
49 if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLongitude"))) {
50 double lon = (m_minimumLongitude + m_maximumLongitude) / 2.0;
51 mapGroup += PvlKeyword("CenterLongitude", toString(lon));
52 }
53
54 // Compute and write the default center latitude if allowed and
55 // necessary
56 if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLatitude"))) {
57 double lat = (m_minimumLatitude + m_maximumLatitude) / 2.0;
58 mapGroup += PvlKeyword("CenterLatitude", toString(lat));
59 }
60
61 // Get the center longitude & latitude
62 m_centerLongitude = mapGroup["CenterLongitude"];
63 m_centerLatitude = mapGroup["CenterLatitude"];
64 if (IsPlanetocentric()) {
66 }
67
68 // Test to make sure center longitude is valid
69 if (fabs(m_centerLongitude) > 360.0) {
70 IString message = "Central Longitude [" + IString(m_centerLongitude);
71 message += "] must be between -360 and 360";
72 throw IException(IException::Unknown, message, _FILEINFO_);
73 }
74
75 // convert to radians, adjust for longitude direction
76 m_centerLongitude *= PI / 180.0;
78
79 // Get the standard parallels & convert them to ographic
80 m_par1 = mapGroup["FirstStandardParallel"];
81 if (IsPlanetocentric()) {
83 }
84 m_par2 = mapGroup["SecondStandardParallel"];
85 if (IsPlanetocentric()) {
87 }
88
89 // Test to make sure standard parallels are valid
90 if (fabs(m_par1) > 90.0 || fabs(m_par2) > 90.0) {
91 QString message = "Standard Parallels must between -90 and 90";
92 throw IException(IException::Unknown, message, _FILEINFO_);
93 }
94 if (fabs(m_par1 + m_par2) < DBL_EPSILON) {
95 QString message = "Standard Parallels cannot be symmetric to the equator";
96 throw IException(IException::Unknown, message, _FILEINFO_);
97 }
98 // Removed because this test only works for northern hemisphere
99 // Just reorder the parallels so p1 is at the larger radius of the two
100 //if (m_par1 > m_par2) {
101 // QString message = "Standard Parallels must be ordered";
102 // throw IException::Message(IException::Projection,message,_FILEINFO_);
103 //}
104
105 // Reorder the parallels so p1 is closer to the equator than p2
106 // Therefore p2 is nearest the apex of the cone
107 if (fabs(m_par1) > fabs(m_par2)) {
108 double tmp = m_par2;
109 m_par2 = m_par1;
110 m_par1 = tmp;
111 }
112
113 // Test to make sure center latitude is valid
114 // The pole opposite the apex can not be used as the clat (i.e., origin of
115 // the projection) it projects to infinity
116 // Given: p2 is closer to the apex than p1, and p2 must be on the same
117 // side of the equator as the apex (due to the reording of p1 and p2 above)
118 // Test for cone pointed south "v"
119 if ((m_par2 < 0.0) && (fabs(90.0 - m_centerLatitude) < DBL_EPSILON)) {
120 IString message = "Center Latitude [" + IString(m_centerLatitude);
121 message += "] is not valid, it projects to infinity "
122 "for standard parallels [";
123 message += IString(m_par1) + "," + IString(m_par2) + "]";
124 throw IException(IException::Unknown, message, _FILEINFO_);
125 }
126 // Test for cone pointed north "^"
127 else if ((m_par2 > 0.0) && (fabs(-90.0 - m_centerLatitude) < DBL_EPSILON)) {
128 IString message = "Center Latitude [" + IString(m_centerLatitude);
129 message += "] is not valid, it projects to infinity "
130 "for standard parallels [";
131 message += IString(m_par1) + "," + IString(m_par2) + "]";
132 throw IException(IException::Unknown, message, _FILEINFO_);
133 }
134 // convert clat to radians
135 m_centerLatitude *= PI / 180.0;
136
137 // Convert standard parallels to radians
138 m_par1 *= PI / 180.0;
139 m_par2 *= PI / 180.0;
140
141 // Compute the Snyder's m and t values for the standard parallels and the
142 // center latitude
143 double sinpar1 = sin(m_par1);
144 double cospar1 = cos(m_par1);
145 double m1 = mCompute(sinpar1, cospar1);
146 double t1 = tCompute(m_par1, sinpar1);
147
148 double sinpar2 = sin(m_par2);
149 double cospar2 = cos(m_par2);
150 double m2 = mCompute(sinpar2, cospar2);
151 double t2 = tCompute(m_par2, sinpar2);
152
153 double sinclat = sin(m_centerLatitude);
154 double tclat = tCompute(m_centerLatitude, sinclat);
155
156 // Calculate Snyder's n, f, and rho
157 if (fabs(m_par1 - m_par2) >= DBL_EPSILON) {
158 m_n = log(m1 / m2) / log(t1 / t2);
159 }
160 else {
161 m_n = sinpar1;
162 }
163 m_f = m1 / (m_n * pow(t1, m_n));
164 m_rho = m_equatorialRadius * m_f * pow(tclat, m_n);
165 }
166 catch(IException &e) {
167 QString message = "Invalid label group [Mapping]";
168 throw IException(e, IException::Io, message, _FILEINFO_);
169 }
170 }
171
175
185 if (!Projection::operator==(proj)) return false;
186 // dont do the below it is a recusive plunge
187 // if (Projection::operator!=(proj)) return false;
188 LambertConformal *lamb = (LambertConformal *) &proj;
189 if ((lamb->m_centerLongitude != m_centerLongitude) ||
190 (lamb->m_centerLatitude != m_centerLatitude)) return false;
191 return true;
192 }
193
199 QString LambertConformal::Name() const {
200 return "LambertConformal";
201 }
202
210 return "1.0";
211 }
212
220 if (m_par1 > m_par2) return m_par2 * 180.0 / PI;
221 else return m_par1 * 180.0 / PI;
222 }
223
236 bool LambertConformal::SetGround(const double lat, const double lon) {
237 // Convert longitude to radians & clean up
238 m_longitude = lon;
239 double lonRadians = lon * PI / 180.0;
240 if (m_longitudeDirection == PositiveWest) lonRadians *= -1.0;
241
242 // Now convert latitude to radians & clean up ... it must be planetographic
243 m_latitude = lat;
244 double latRadians = lat;
245 if (IsPlanetocentric()) latRadians = ToPlanetographic(latRadians);
246 latRadians *= PI / 180.0;
247
248 // Check for special cases & calculate rh, theta, and snyder t
249 double rh;
250 if (fabs(fabs(latRadians) - HALFPI) < DBL_EPSILON) {
251 // Lat/Lon point cannot be projected
252 if (latRadians *m_n <= 0.0) {
253 m_good = false;
254 return m_good;
255 }
256 else rh = 0.0;
257 }
258 else {
259 double sinlat = sin(latRadians);
260 // Lat/Lon point cannot be projected
261 double t = tCompute(latRadians, sinlat);
262 rh = m_equatorialRadius * m_f * pow(t, m_n);
263 }
264 double theta = m_n * (lonRadians - m_centerLongitude);
265
266 // Compute the coordinate
267 double x = rh * sin(theta);
268 double y = m_rho - rh * cos(theta);
269 SetComputedXY(x, y);
270
271 m_good = true;
272 return m_good;
273 }
274
288 bool LambertConformal::SetCoordinate(const double x, const double y) {
289 // Save the coordinate
290 SetXY(x, y);
291
292 // Get the sign of Snyder's n
293 double sign;
294 if (m_n >= 0) sign = 1.0;
295 else sign = -1.0;
296
297 double temp = m_rho - GetY();
298 double rh = sign * sqrt(GetX() * GetX() + temp * temp);
299
300 double theta;
301 if (rh != 0) theta = atan2(sign * GetX(), sign * temp);
302 else theta = 0.0;
303
304 // Compute latitude and longitude
305 if (rh != 0 || m_n > 0) {
306 double t = pow(rh / (m_equatorialRadius * m_f), 1.0 / m_n);
308 }
309 else m_latitude = -HALFPI;
311
312
313 // Convert to degrees
314 m_latitude *= 180.0 / PI;
315 m_longitude *= 180.0 / PI;
316
317 // Cleanup the longitude
319 // These need to be done for circular type projections
320 // m_longitude = To360Domain (m_longitude);
321 // if (m_longitudeDomain == 180) m_longitude = To180Domain(m_longitude);
322
323 // Cleanup the latitude
325
326 m_good = true;
327 return m_good;
328 }
329
361 bool LambertConformal::XYRange(double &minX, double &maxX,
362 double &minY, double &maxY) {
363
364 // Test the four corners
369
370 // Decide which pole the apex of the cone is above
371 // Remember p1 is now closest to the equator and p2 is closest to one of the
372 // poles
373 bool north_hemi = true;
374 // Stuart Sides 2008-08-15
375 // This test was removed because the reordering of p1 and p2 in the
376 // constructor made it incorrect
377 //if (fabs(m_par1) > fabs(m_par2)) north_hemi = false;
378 if (m_par2 < 0.0) north_hemi = false;
379 if ((m_par1 == m_par2) && (m_par1 < 0.0)) north_hemi = false;
380
381 double cLonDeg = m_centerLongitude * 180.0 / PI;
382
383 // This is needed because the SetGround class applies the PositiveWest
384 // adjustment which was already done in the constructor.
385 if (m_longitudeDirection == PositiveWest) cLonDeg = cLonDeg * -1.0;
386
387 double pole_north, min_lat_north, max_lat_north, londiff;
388 // North Pole
389 if (north_hemi) {
390 m_latitude = 90.0;
391 m_longitude = cLonDeg;
392
393 //Unable to project at the pole
395 m_good = false;
396 return m_good;
397 }
398
399 pole_north = YCoord();
401
402 //Unable to project at the pole
404 m_good = false;
405 return m_good;
406 }
407
408 min_lat_north = YCoord();
409 double y = min_lat_north + 2.0 * (pole_north - min_lat_north);
410
411 //Unable to project opposite the center longitude
412 if (!SetCoordinate(XCoord(), y)) {
413 m_good = false;
414 return m_good;
415 }
416
417 londiff = fabs(cLonDeg - m_longitude) / 2.0;
418 m_longitude = cLonDeg - londiff;
419 for (int i = 0; i < 3; i++) {
424 }
425 m_longitude += londiff;
426 }
427
428 }
429 // South Pole
430 else {
431 m_latitude = -90.0;
432 m_longitude = cLonDeg;
433
434 //Unable to project at the pole
436 m_good = false;
437 return m_good;
438 }
439
440 pole_north = YCoord();
442
443 //Unable to project at the pole
445 m_good = false;
446 return m_good;
447 }
448
449 max_lat_north = YCoord();
450 double y = max_lat_north - 2.0 * (max_lat_north - pole_north);
451
452 //Unable to project opposite the center longitude
453 if (!SetCoordinate(XCoord(), y)) {
454 m_good = false;
455 return m_good;
456 }
457
458 londiff = fabs(cLonDeg - m_longitude) / 2.0;
459 m_longitude = cLonDeg - londiff;
460 for (int i = 0; i < 3; i++) {
465 }
466 m_longitude += londiff;
467 }
468 }
469
470 // Make sure everything is ordered
471 if (m_minimumX >= m_maximumX) return false;
472 if (m_minimumY >= m_maximumY) return false;
473
474 // Return X/Y min/maxs
475 minX = m_minimumX;
476 maxX = m_maximumX;
477 minY = m_minimumY;
478 maxY = m_maximumY;
479 return true;
480 }
481
482
489 PvlGroup mapping = TProjection::Mapping();
490
491 mapping += m_mappingGrp["CenterLatitude"];
492 mapping += m_mappingGrp["CenterLongitude"];
493 mapping += m_mappingGrp["FirstStandardParallel"];
494 mapping += m_mappingGrp["SecondStandardParallel"];
495
496 return mapping;
497 }
498
506
507 mapping += m_mappingGrp["CenterLatitude"];
508 mapping += m_mappingGrp["FirstStandardParallel"];
509 mapping += m_mappingGrp["SecondStandardParallel"];
510
511 return mapping;
512 }
513
521
522 mapping += m_mappingGrp["CenterLongitude"];
523
524 return mapping;
525 }
526} // end namespace isis
527
541extern "C" Isis::Projection *LambertConformalPlugin(Isis::Pvl &lab,
542 bool allowDefaults) {
543 return new Isis::LambertConformal(lab, allowDefaults);
544}
545
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
@ Io
A type of error that occurred when performing an actual I/O operation.
Definition IException.h:155
Adds specific functionality to C++ strings.
Definition IString.h:165
Lambert Conformal Map Projection.
QString Version() const
Returns the version of the map projection.
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_rho
Snyder's rho variable.
double TrueScaleLatitude() const
Returns the latitude of true scale (in the case of LambertConformal it is the smaller of the two stan...
double m_par2
The second standard parallel.
QString Name() const
Returns the name of the map projection, "LambertConformal".
double m_par1
The first standard parallel.
bool SetCoordinate(const double x, const double y)
This method is used to set the projection x/y.
double m_n
Snyder's n variable.
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...
double m_centerLongitude
The center longitude for the map projection.
double m_centerLatitude
The center latitude for the map projection.
PvlGroup MappingLatitudes()
This function returns the latitude keywords that this projection uses.
double m_f
Snyder's f variable.
PvlGroup MappingLongitudes()
This function returns the longitude keywords that this projection uses.
LambertConformal(Pvl &label, bool allowDefaults=false)
Constructs a Lambert Conformal object.
~LambertConformal()
Destroys the LambertConformal object.
Base class for Map Projections.
Definition Projection.h:155
double XCoord() const
This returns the projection X provided SetGround, SetCoordinate, SetUniversalGround,...
double m_maximumX
See minimumX description.
Definition Projection.h:326
double YCoord() const
This returns the projection Y provided SetGround, SetCoordinate, SetUniversalGround,...
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.
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.
double mCompute(const double sinphi, const double cosphi) const
A convience method to compute Snyder's m equation (14-15) for a given latitude, .
double ToPlanetocentric(const double lat) const
This method converts a planetographic latitude to a planetocentric latitude.
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 phi2Compute(const double t) const
A convience method to compute latitude angle phi2 given small t, from Syder's recursive equation (7-9...
double m_minimumLongitude
Contains the minimum longitude for the entire ground range.
double tCompute(const double phi, const double sinphi) const
A convience method to compute Snyder's t equation (15-9) for a given latitude, .
@ PositiveWest
Longitude values increase in the westerly direction.
double m_maximumLatitude
Contains the maximum latitude for the entire ground range.
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.
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.