7#include "UpturnedEllipsoidTransverseAzimuthal.h"
17#include "IException.h"
19#include "TProjection.h"
22#include "PvlKeyword.h"
23#include "SpecialPixel.h"
47 UpturnedEllipsoidTransverseAzimuthal::UpturnedEllipsoidTransverseAzimuthal(
Pvl &label,
57 PvlGroup &mapGroup = label.findGroup(
"Mapping", Pvl::Traverse);
61 double centerLongitude = 0.0;
62 if (!mapGroup.hasKeyword(
"CenterLongitude")) {
65 mapGroup += PvlKeyword(
"CenterLongitude",
toString(centerLongitude),
"Degrees");
68 QString message =
"Cannot project using upturned ellipsoid Transverse Azimuthal";
69 message +=
" without [CenterLongitude] value. Keyword does not exist";
70 message +=
" in labels and defaults are not allowed.";
71 throw IException(IException::Unknown, message, _FILEINFO_);
75 centerLongitude = mapGroup[
"CenterLongitude"];
78 if (MinimumLongitude() < centerLongitude - 90.0) {
79 QString message =
"MinimumLongitude ["
81 +
"] is invalid. Must be within -90 degrees of the CenterLongitude ["
83 throw IException(IException::Unknown, message, _FILEINFO_);
85 if (MaximumLongitude() > centerLongitude + 90.0) {
86 QString message =
"MaximumLongitude ["
88 +
"] is invalid. Must be within +90 degrees of the CenterLongitude ["
90 throw IException(IException::Unknown, message, _FILEINFO_);
95 init(centerLongitude);
97 catch(IException &e) {
98 QString message =
"Invalid label group [Mapping]";
99 throw IException(e, IException::Unknown, message, _FILEINFO_);
109 UpturnedEllipsoidTransverseAzimuthal::~UpturnedEllipsoidTransverseAzimuthal() {
123 bool UpturnedEllipsoidTransverseAzimuthal::operator== (
const Projection &proj) {
128 UpturnedEllipsoidTransverseAzimuthal *tcyl = (UpturnedEllipsoidTransverseAzimuthal *) &proj;
129 if ((tcyl->m_lambda0 != m_lambda0) ||
130 (tcyl->m_a != m_a) ||
131 (tcyl->m_b != m_b))
return false;
144 QString UpturnedEllipsoidTransverseAzimuthal::Name()
const {
145 return "UpturnedEllipsoidUpturnedEllipsoidTransverseAzimuthal";
154 QString UpturnedEllipsoidTransverseAzimuthal::Version()
const {
170 void UpturnedEllipsoidTransverseAzimuthal::init(
double centerLongitude) {
173 if (IsPositiveEast()) {
174 m_lambda0 = centerLongitude *
DEG2RAD;
177 m_lambda0 = ToPositiveEast(centerLongitude, 360) *
DEG2RAD;
179 if (Has180Domain()) {
180 m_lambda0 = To180Domain(m_lambda0);
183 m_lambda0 = To360Domain(m_lambda0);
189 m_minimumX = DBL_MAX;
190 m_maximumX = -DBL_MAX;
191 m_minimumY = DBL_MAX;
192 m_maximumY = -DBL_MAX;
195 double axis1 = EquatorialRadius();
196 double axis2 = PolarRadius();
197 m_a = qMax(axis1, axis2);
198 m_b = qMin(axis1, axis2);
199 m_e = Eccentricity();
201 if (qFuzzyCompare(0.0, m_e)) {
206 double t0 = m_b / m_a;
209 double k1 = 2 * m_a * exp(m_t1 * atan(m_t1));
233 bool UpturnedEllipsoidTransverseAzimuthal::SetGround(
const double lat,
236 if (lat == Null || lon == Null) {
245 if (qFuzzyCompare(90.0, qAbs(lat)) && qAbs(lat) > 90.0) {
246 phiNorm = copysign(HALFPI, lat);
249 else if (qAbs(lat) > 90.0) {
253 else if (IsPlanetocentric()) {
258 phiNorm = ToPlanetocentric(lat) *
DEG2RAD;
264 if (IsPositiveEast()) {
265 lambdaNorm = lon *
DEG2RAD - m_lambda0;
268 lambdaNorm = ToPositiveEast(lon, 360) *
DEG2RAD - m_lambda0;
275 double cosz = cos(phiNorm) * cos(lambdaNorm);
293 else if (cosz > 0.5) {
295 double sinz = sqrt( 1 - cosz*cosz );
299 double phi =
HALFPI - atan2( sinz, m_t * cosz );
300 double sinPhi = sin(phi);
307 double rhoOverTanZ = m_k * sinPhi / ( ( 1 + sinPhi )
309 * exp( m_t1 * atan( m_t1 * sinPhi ) ) );
310 x = rhoOverTanZ * tan(lambdaNorm);
311 y = rhoOverTanZ * (sin(phiNorm) / cosz );
327 double tolerance = 0.0016;
328 double coszmax = cos(PI - tolerance);
329 double lambdaModulus = fmod(lambdaNorm, TWOPI);
330 if (cosz < coszmax) {
337 if (-PI - tolerance < lambdaModulus && lambdaModulus <= -PI) {
338 lambdaNorm = -
PI - tolerance;
344 else if (-PI < lambdaModulus && lambdaModulus <= -PI + tolerance) {
345 lambdaNorm = -
PI + tolerance;
351 else if (PI - tolerance < lambdaModulus && lambdaModulus <= PI) {
352 lambdaNorm =
PI - tolerance;
358 else if (PI < lambdaModulus && lambdaModulus < PI + tolerance) {
359 lambdaNorm =
PI + tolerance;
363 double sinz = sqrt( 1 - cosz*cosz );
369 double phi = atan2( m_t * cosz, sinz );
370 double sinPhi = sin(phi);
375 double rhoOverSinZ = m_k * cos(phi) / ( ( 1 + sinPhi )
377 * exp( m_t1 * atan(m_t1 * sinPhi ) ) );
378 x = rhoOverSinZ * cos(phiNorm) * sin(lambdaNorm);
379 y = rhoOverSinZ * sin(phiNorm);
409 bool UpturnedEllipsoidTransverseAzimuthal::SetCoordinate(
const double x,
411 if (x == Null || y == Null) {
418 if (qFuzzyCompare(x + 1.0, 1.0) && qFuzzyCompare(y + 1.0, 1.0)) {
421 m_longitude = m_lambda0 *
RAD2DEG;
449 double phi0, fphi0, fprimephi0, phi1;
450 bool converged =
false;
452 double tolerance = 10e-10;
454 while (!converged && iterations < 1000) {
455 fphi0 = m_k * cos(phi0) / ((1 + sin(phi0)) * exp(m_t1 * atan( m_t1 * sin(phi0) )))
458 fprimephi0 = -m_k * (1 + m_t1 * m_t1)
460 * exp(m_t1 * atan( m_t1 * sin(phi0) ))
461 * (1 + m_t1 * m_t1 * sin(phi0) * sin(phi0)));
462 phi1 = phi0 - fphi0 / fprimephi0;
464 if (qAbs(phi1) > HALFPI) {
465 double phiDegrees = To180Domain(phi1*RAD2DEG);
466 if (phiDegrees > 90.0) {
469 if (phiDegrees < -180.0) {
474 if (qAbs(phi0 - phi1) < tolerance) {
496 double z = atan2((1 - m_e * m_e), tan(phi));
504 double rho = sqrt(x*x + y*y);
505 double phiNorm = asin( y * sin(z) / rho );
512 double cosLambdaNorm = cos(z) / cos(phiNorm);
513 if (cosLambdaNorm > 1.0) {
516 else if (cosLambdaNorm < -1.0) {
519 else if (x >= 0 && y >= 0) {
520 lambdaNorm = acos(cosLambdaNorm);
522 else if (x < 0 && y >= 0) {
523 lambdaNorm = -acos(cosLambdaNorm);
525 else if (y < 0 && x >= 0) {
526 lambdaNorm = acos(cosLambdaNorm);
529 lambdaNorm = -acos(cosLambdaNorm);
533 m_longitude = (lambdaNorm + m_lambda0) * RAD2DEG;
534 m_latitude = phiNorm *
RAD2DEG;
538 if (IsPlanetocentric()) {
539 m_latitude = ToPlanetocentric(m_latitude);
543 if (IsPositiveWest()) {
544 m_longitude = ToPositiveWest(m_longitude, m_longitudeDomain);
547 if (Has180Domain()) {
548 m_longitude = To180Domain(m_longitude);
552 m_longitude = To360Domain(m_longitude);
596 bool UpturnedEllipsoidTransverseAzimuthal::XYRange(
double &minX,
double &maxX,
597 double &minY,
double &maxY) {
600 XYRangeCheck(m_minimumLatitude, m_minimumLongitude);
601 XYRangeCheck(m_maximumLatitude, m_minimumLongitude);
602 XYRangeCheck(m_minimumLatitude, m_maximumLongitude);
603 XYRangeCheck(m_maximumLatitude, m_maximumLongitude);
605 double centerLongitude = m_lambda0 *
RAD2DEG;
607 bool centerLongitudeInRange = TProjection::inLongitudeRange(centerLongitude);
608 bool centerLongitude90InRange = TProjection::inLongitudeRange(centerLongitude + 90.0);
609 bool centerLongitude180InRange = TProjection::inLongitudeRange(centerLongitude + 180.0);
610 bool centerLongitude270InRange = TProjection::inLongitudeRange(centerLongitude + 270.0);
612 if (centerLongitudeInRange) {
613 XYRangeCheck(m_minimumLatitude, centerLongitude);
614 XYRangeCheck(m_maximumLatitude, centerLongitude);
616 if (centerLongitude90InRange) {
617 XYRangeCheck(m_minimumLatitude, centerLongitude + 90.0);
618 XYRangeCheck(m_maximumLatitude, centerLongitude + 90.0);
620 if (centerLongitude180InRange) {
621 XYRangeCheck(m_minimumLatitude, centerLongitude + 180.0);
622 XYRangeCheck(m_maximumLatitude, centerLongitude + 180.0);
624 if (centerLongitude270InRange) {
625 XYRangeCheck(m_minimumLatitude, centerLongitude + 270.0);
626 XYRangeCheck(m_maximumLatitude, centerLongitude + 270.0);
629 if (TProjection::inLatitudeRange(0.0)) {
630 XYRangeCheck(0.0, m_minimumLongitude);
631 XYRangeCheck(0.0, m_maximumLongitude);
632 if (centerLongitudeInRange) {
633 XYRangeCheck(0.0, centerLongitude);
635 if (centerLongitude90InRange) {
636 XYRangeCheck(0.0, centerLongitude + 90.0);
638 if (centerLongitude180InRange) {
639 XYRangeCheck(0.0, centerLongitude + 180.0);
641 if (centerLongitude270InRange) {
642 XYRangeCheck(0.0, centerLongitude + 270.0);
647 if (m_minimumX >= m_maximumX)
return false;
648 if (m_minimumY >= m_maximumY)
return false;
683 PvlGroup UpturnedEllipsoidTransverseAzimuthal::Mapping() {
684 PvlGroup mapping = TProjection::Mapping();
685 mapping += PvlKeyword(
"CenterLongitude",
toString(m_lambda0 * RAD2DEG));
704 PvlGroup UpturnedEllipsoidTransverseAzimuthal::MappingLatitudes() {
705 return TProjection::MappingLatitudes();
724 PvlGroup UpturnedEllipsoidTransverseAzimuthal::MappingLongitudes() {
725 PvlGroup mapping = TProjection::MappingLongitudes();
726 mapping += PvlKeyword(
"CenterLongitude",
toString(m_lambda0 * RAD2DEG));
748 bool allowDefaults) {
Base class for Map Projections.
Container for cube-like labels.
Base class for Map TProjections.
Upturned Ellipsoid Transverse Azimuthal Map Projection.
This is free and unencumbered software released into the public domain.
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
const double DEG2RAD
Multiplier for converting from degrees to radians.
const double HALFPI
The mathematical constant PI/2.
const double RAD2DEG
Multiplier for converting from radians to degrees.
const double PI
The mathematical constant PI.
Namespace for the standard library.