Isis 3 Programmer Reference
TransverseMercator.cpp
1
6/* SPDX-License-Identifier: CC0-1.0 */
7#include "TransverseMercator.h"
8
9#include <cmath>
10#include <cfloat>
11
12#include "Constants.h"
13#include "IException.h"
14#include "TProjection.h"
15#include "Pvl.h"
16#include "PvlGroup.h"
17#include "PvlKeyword.h"
18
19using namespace std;
20namespace Isis {
39 TransverseMercator::TransverseMercator(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 // Compute and write the default center longitude if allowed and
46 // necessary
47 if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLongitude"))) {
48 double lon = (m_minimumLongitude + m_maximumLongitude) / 2.0;
49 mapGroup += PvlKeyword("CenterLongitude", toString(lon));
50 }
51
52 // Compute and write the default center latitude if allowed and
53 // necessary
54 if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLatitude"))) {
55 double lat = (m_minimumLatitude + m_maximumLatitude) / 2.0;
56 mapGroup += PvlKeyword("CenterLatitude", toString(lat));
57 }
58
59 // Get the center longitude & latitude
60 m_centerLongitude = mapGroup["CenterLongitude"];
61 m_centerLatitude = mapGroup["CenterLatitude"];
62
63 // make sure the center latitude value is valid
64 if (fabs(m_centerLatitude) >= 90.0) {
65 string msg = "Invalid Center Latitude Value. Must be between -90 and 90";
66 throw IException(IException::Io, msg, _FILEINFO_);
67 }
68
69 // make sure the center longitude value is valid
70 if (fabs(m_centerLongitude) > 360.0) {
71 string msg = "Invalid Center Longitude Value. Must be between -360 and 360";
72 throw IException(IException::Io, msg, _FILEINFO_);
73 }
74
75 // convert latitude to planetographic if it is planetocentric
76 if (IsPlanetocentric()) {
78 }
79
80 // convert to radians and adjust for longitude direction
82 m_centerLatitude *= PI / 180.0;
83 m_centerLongitude *= PI / 180.0;
84
85 // Compute other necessary variables. See Snyder, page 61
87 m_esp = m_eccsq;
88 m_e0 = 1.0 - 0.25 * m_eccsq * (1.0 + m_eccsq / 16.0 * (3.0 + 1.25 * m_eccsq));
89 m_e1 = 0.375 * m_eccsq * (1.0 + 0.25 * m_eccsq * (1.0 + 0.468750 * m_eccsq));
90 m_e2 = 0.058593750 * m_eccsq * m_eccsq * (1.0 + 0.750 * m_eccsq);
91 m_e3 = m_eccsq * m_eccsq * m_eccsq * (35.0 / 3072.0);
93 m_e2 * sin(4.0 * m_centerLatitude) - m_e3 * sin(6.0 * m_centerLatitude));
94
95 // Set flag for sphere or ellipsiod
96 m_sph = true; // Sphere
97 if (Eccentricity() >= .00001) {
98 m_sph = false; // Ellipsoid
99 m_esp = m_eccsq / (1.0 - m_eccsq);
100 }
101
102 // Get the scale factor
103 if ((allowDefaults) && (!mapGroup.hasKeyword("ScaleFactor"))) {
104 mapGroup += PvlKeyword("ScaleFactor", toString(1.0));
105 }
106 m_scalefactor = mapGroup["ScaleFactor"];
107 }
108 catch(IException &e) {
109 string message = "Invalid label group [Mapping]";
110 throw IException(e, IException::Io, message, _FILEINFO_);
111 }
112 }
113
117
127 if (!Projection::operator==(proj)) return false;
128 // dont do the below it is a recusive plunge
129 // if (Projection::operator!=(proj)) return false;
130 TransverseMercator *trans = (TransverseMercator *) &proj;
131 if ((trans->m_centerLongitude != m_centerLongitude) ||
132 (trans->m_centerLatitude != m_centerLatitude)) return false;
133 return true;
134 }
135
141 QString TransverseMercator::Name() const {
142 return "TransverseMercator";
143 }
144
152 return "1.0";
153 }
154
167 bool TransverseMercator::SetGround(const double lat, const double lon) {
168 // Get longitude & fix direction
169 m_longitude = lon;
171
172 double cLonDeg = m_centerLongitude * 180.0 / PI;
173 double deltaLon = m_longitude - cLonDeg;
174 while(deltaLon < -360.0) deltaLon += 360.0;
175 while(deltaLon > 360.0) deltaLon -= 360.0;
176 double deltaLonRads = deltaLon * PI / 180.0;
177
178 // Now convert latitude to radians & clean up ... it must be planetographic
179 m_latitude = lat;
180 double latRadians = m_latitude * PI / 180.0;
181 if (IsPlanetocentric()) {
182 latRadians = ToPlanetographic(m_latitude) * PI / 180.0;
183 }
184
185 // distance along the meridian fromthe Equator to the latitude phi
186 // see equation (3-21) on pages 61, 17.
187 double M = m_equatorialRadius * (m_e0 * latRadians
188 - m_e1 * sin(2.0 * latRadians)
189 + m_e2 * sin(4.0 * latRadians)
190 - m_e3 * sin(6.0 * latRadians));
191
192 // Declare variables
193 const double epsilon = 1.0e-10;
194
195 // Sphere Conversion
196 double x, y;
197 if (m_sph) {
198 double cosphi = cos(latRadians);
199 double b = cosphi * sin(deltaLonRads);
200
201 // Point projects to infinity
202 if (fabs(fabs(b) - 1.0) <= epsilon) {
203 m_good = false;
204 return m_good;
205 }
206 x = 0.5 * m_equatorialRadius * m_scalefactor * log((1.0 + b) / (1.0 - b));
207
208 // If arcosine argument is too close to 1, con=0.0 because arcosine(1)=0
209 double con = cosphi * cos(deltaLonRads) / sqrt(1.0 - b * b);
210 if (fabs(con) > 1.0) {
211 con = 0.0;
212 }
213 else {
214 con = acos(con);
215 }
216 if (m_latitude < 0.0) con = -con;
218 }
219
220 // Ellipsoid Conversion
221 else {
222 if (fabs(HALFPI - fabs(latRadians)) < epsilon) {
223 x = 0.0;
224 y = m_scalefactor * (M - m_ml0);
225 }
226 else {
227 // Define Snyder's variables for ellipsoidal projections, page61
228 double sinphi = sin(latRadians);
229 double cosphi = cos(latRadians);
230 double A = cosphi * deltaLonRads; // see equation (8-15), page 61
231 double Asquared = A * A;
232 double C = m_esp * cosphi * cosphi; // see equation (8-14), page 61
233 double tanphi = tan(latRadians);
234 double T = tanphi * tanphi; // see equation (8-13), page 61
235 double N = m_equatorialRadius / sqrt(1.0 - m_eccsq * sinphi * sinphi);
236 // see equation (4-20), page 61
237
238 x = m_scalefactor * N * A
239 * (1.0 + Asquared / 6.0 * (1.0 - T + C + Asquared / 20.0
240 *(5.0 - 18.0*T + T*T + 72.0*C - 58.0*m_esp)));
241 y = m_scalefactor
242 * (M - m_ml0 + N*tanphi*(Asquared * (0.5 + Asquared / 24.0 *
243 (5.0 - T + 9.0*C + 4.0*C*C + Asquared / 30.0
244 *(61.0 - 58.0*T + T*T + 600.0*C - 330.0*m_esp)))));
245 }
246 }
247
248 SetComputedXY(x, y);
249 m_good = true;
250 return m_good;
251 }
252
266 bool TransverseMercator::SetCoordinate(const double x, const double y) {
267 // Save the coordinate
268 SetXY(x, y);
269
270 // Declare & Initialize variables
271 double f, g, h, temp, con, phi, dphi, sinphi, cosphi, tanphi;
272 double c, cs, t, ts, n, rp, d, ds;
273 const double epsilon = 1.0e-10;
274
275 // Sphere Conversion
276 if (m_sph) {
277 f = exp(GetX() / (m_equatorialRadius * m_scalefactor));
278 g = 0.5 * (f - 1.0 / f);
280 h = cos(temp);
281 con = sqrt((1.0 - h * h) / (1.0 + g * g));
282 if (con > 1.0) con = 1.0;
283 if (con < -1.0) con = -1.0;
284 m_latitude = asin(con);
285 if (temp < 0.0) m_latitude = -m_latitude;
287 if (g != 0.0 || h != 0.0) {
288 m_longitude = atan2(g, h) + m_centerLongitude;
289 }
290 }
291
292 // Ellipsoid Conversion
293 else if (!m_sph) {
295 phi = con;
296 for (int i = 1; i < 7; i++) {
297 dphi = ((con + m_e1 * sin(2.0 * phi) - m_e2 * sin(4.0 * phi)
298 + m_e3 * sin(6.0 * phi)) / m_e0) - phi;
299 phi += dphi;
300 if (fabs(dphi) <= epsilon) break;
301 }
302
303 // Didn't converge
304 if (fabs(dphi) > epsilon) {
305 m_good = false;
306 return m_good;
307 }
308 if (fabs(phi) >= HALFPI) {
309 if (GetY() >= 0.0) m_latitude = fabs(HALFPI);
310 if (GetY() < 0.0) m_latitude = - fabs(HALFPI);
312 }
313 else {
314 sinphi = sin(phi);
315 cosphi = cos(phi);
316 tanphi = tan(phi);
317 c = m_esp * cosphi * cosphi;
318 cs = c * c;
319 t = tanphi * tanphi;
320 ts = t * t;
321 con = 1.0 - m_eccsq * sinphi * sinphi;
322 n = m_equatorialRadius / sqrt(con);
323 rp = n * (1.0 - m_eccsq) / con;
324 d = GetX() / (n * m_scalefactor);
325 ds = d * d;
326 m_latitude = phi - (n * tanphi * ds / rp) * (0.5 - ds /
327 24.0 * (5.0 + 3.0 * t + 10.0 * c - 4.0 * cs - 9.0 *
328 m_esp - ds / 30.0 * (61.0 + 90.0 * t + 298.0 * c +
329 45.0 * ts - 252.0 * m_esp - 3.0 * cs)));
330
331
332 // Latitude cannot be greater than + or - halfpi radians (or 90 degrees)
333 if (fabs(m_latitude) > HALFPI) {
334 m_good = false;
335 return m_good;
336 }
338 + (d * (1.0 - ds / 6.0 *
339 (1.0 + 2.0 * t + c - ds / 20.0 * (5.0 - 2.0 * c +
340 28.0 * t - 3.0 * cs + 8.0 * m_esp + 24.0 * ts))) / cosphi);
341 }
342 }
343
344 // Convert to Degrees
345 m_latitude *= 180.0 / PI;
346 m_longitude *= 180.0 / PI;
347
348 // Cleanup the longitude
350 // These need to be done for circular type projections
353
354 // Cleanup the latitude
356
357 m_good = true;
358 return m_good;
359 }
360
384 bool TransverseMercator::XYRange(double &minX, double &maxX,
385 double &minY, double &maxY) {
386 // Check the corners of the lat/lon range
391
392 // convert center latitude to degrees & test
393 double clat = m_centerLatitude * 180.0 / PI;
394
395 if (clat > m_minimumLatitude &&
396 clat < m_maximumLatitude) {
399 }
400
401 // convert center longitude to degrees & test
402 double clon = m_centerLongitude * 180.0 / PI;
403 if (clon > m_minimumLongitude &&
404 clon < m_maximumLongitude) {
407 }
408
409 // Make sure everything is ordered
410 if (m_minimumX >= m_maximumX) return false;
411 if (m_minimumY >= m_maximumY) return false;
412
413 // Return X/Y min/maxs
414 minX = m_minimumX;
415 maxX = m_maximumX;
416 minY = m_minimumY;
417 maxY = m_maximumY;
418 return true;
419 }
420
421
428 PvlGroup mapping = TProjection::Mapping();
429
430 mapping += m_mappingGrp["CenterLatitude"];
431 mapping += m_mappingGrp["CenterLongitude"];
432 mapping += m_mappingGrp["ScaleFactor"];
433
434 return mapping;
435 }
436
444
445 mapping += m_mappingGrp["CenterLatitude"];
446
447 return mapping;
448 }
449
457
458 mapping += m_mappingGrp["CenterLongitude"];
459
460 return mapping;
461 }
462} // end namespace isis
463
478extern "C" Isis::Projection *TransverseMercatorPlugin(Isis::Pvl &lab,
479 bool allowDefaults) {
480 return new Isis::TransverseMercator(lab, allowDefaults);
481}
482
483
Isis exception class.
Definition IException.h:91
@ Io
A type of error that occurred when performing an actual I/O operation.
Definition IException.h:155
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.
void XYRangeCheck(const double latitude, const double longitude)
This convience function is established to assist in the development of the XYRange virtual method.
double Eccentricity() const
This returns the eccentricity of the target,.
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 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.
TransverseMercator Map Projection.
~TransverseMercator()
Destroys the TransverseMercator object.
bool SetCoordinate(const double x, const double y)
This method is used to set the projection x/y.
double m_ml0
Distance along the meridian from the equator to the center latitude.
double m_eccsq
Eccentricity Squared.
double m_e2
Eccentricity Constant: e2 = 15e^4/256 * (1 + 3e^2/4)) estimates the value e2 = 15e^4/256 + 45e^6/1024...
QString Version() const
Returns the version of the map projection.
double m_scalefactor
Scale Factor for the projection.
double m_centerLatitude
The center latitude for the map projection.
PvlGroup Mapping()
This function returns the keywords that this projection uses.
PvlGroup MappingLongitudes()
This function returns the longitude keywords that this projection uses.
double m_esp
Snyder's (e')^2 variable from equation (8-12) on page.
double m_centerLongitude
The center longitude for the map projection.
bool operator==(const Projection &proj)
Compares two Projection objects to see if they are equal.
double m_e3
Eccentricity Constant: e3 = 35e^6/3072 estimates the value e3 = 35e^6/3072 + ...
TransverseMercator(Pvl &label, bool allowDefaults=false)
Constructs a TransverseMercator object.
bool m_sph
Flag set to true if sphere, and false if ellipsiod.
QString Name() const
Returns the name of the map projection, "TransverseMercator".
double m_e0
Eccentricity Constant: e0 = 1 - e^2/4 * (1 + 3e^2/16 * (3 + 5e^2/4)) estimates the value e0 = 1 - e^2...
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 MappingLatitudes()
This function returns the latitude keywords that this projection uses.
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_e1
Eccentricity Constant: e1 = 3e^2/8 * (1.0 + e^2/4 * (1.0 + 15e^2/32)) estimates the value e1 = 3e^2/8...
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.