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,
49 TProjection::TProjection(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.
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.