aboutsummaryrefslogtreecommitdiff
path: root/src/projections
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2020-09-26 18:45:25 +0200
committerEven Rouault <even.rouault@spatialys.com>2020-09-26 19:07:55 +0200
commitb04894819ea4e4d9d93e03015f0c7c9aa84642fe (patch)
tree670e2978530923bc9eb87bea6ba190918af0dd74 /src/projections
parent3d88b6fc89f95803bfd4d59c47eef1c05bda710c (diff)
downloadPROJ-b04894819ea4e4d9d93e03015f0c7c9aa84642fe.tar.gz
PROJ-b04894819ea4e4d9d93e03015f0c7c9aa84642fe.zip
Ortho ellipsoidal inverse: add non iterative implementations for polar and equatorial
Diffstat (limited to 'src/projections')
-rw-r--r--src/projections/ortho.cpp60
1 files changed, 60 insertions, 0 deletions
diff --git a/src/projections/ortho.cpp b/src/projections/ortho.cpp
index 0594205b..ac54d88a 100644
--- a/src/projections/ortho.cpp
+++ b/src/projections/ortho.cpp
@@ -160,6 +160,66 @@ static PJ_LP ortho_e_inverse (PJ_XY xy, PJ *P) { /* Ellipsoidal, inver
PJ_LP lp;
struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ if( Q->mode == N_POLE || Q->mode == S_POLE )
+ {
+ // Polar case. Forward case equations can be simplified as:
+ // xy.x = nu * cosphi * sinlam
+ // xy.y = nu * -cosphi * coslam * sign(phi0)
+ // ==> lam = atan2(xy.x, -xy.y * sign(phi0))
+ // ==> xy.x^2 + xy.y^2 = nu^2 * cosphi^2
+ // rh^2 = cosphi^2 / (1 - es * sinphi^2)
+ // ==> sinphi^2 = (1 - rh^2) / (1 - es * rh^2)
+
+ const double rh2 = xy.x * xy.x + xy.y * xy.y;
+ if (rh2 >= 1. - 1e-15) {
+ if ((rh2 - 1.) > EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ proj_log_trace(P, "Point (%.3f, %.3f) is outside the projection boundary");
+ lp.lam = HUGE_VAL; lp.phi = HUGE_VAL;
+ return lp;
+ }
+ lp.phi = 0;
+ }
+ else {
+ lp.phi = asin(sqrt((1 - rh2) / (1 - P->es * rh2))) * (Q->mode == N_POLE ? 1 : -1);
+ }
+ lp.lam = atan2(xy.x, xy.y * (Q->mode == N_POLE ? -1 : 1));
+ return lp;
+ }
+
+ if( Q->mode == EQUIT )
+ {
+ // Equatorial case. Forward case equations can be simplified as:
+ // xy.x = nu * cosphi * sinlam
+ // xy.y = nu * sinphi * (1 - P->es)
+ // x^2 * (1 - es * sinphi^2) = (1 - sinphi^2) * sinlam^2
+ // y^2 / ((1 - es)^2 + y^2 * es) = sinphi^2
+
+ const auto SQ = [](double x) { return x*x; };
+
+ // Equation of the ellipse
+ if( SQ(xy.x) + SQ(xy.y * (P->a / P->b)) > 1 + 1e-11 ) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ proj_log_trace(P, "Point (%.3f, %.3f) is outside the projection boundary");
+ lp.lam = HUGE_VAL; lp.phi = HUGE_VAL;
+ return lp;
+ }
+
+ const double sinphi2 = xy.y == 0 ? 0 : 1.0 / (SQ((1 - P->es) / xy.y) + P->es);
+ if( sinphi2 > 1 - 1e-11 ) {
+ lp.phi = M_PI_2 * (xy.y > 0 ? 1 : -1);
+ lp.lam = 0;
+ return lp;
+ }
+ lp.phi = asin(sqrt(sinphi2)) * (xy.y > 0 ? 1 : -1);
+ const double sinlam = xy.x * sqrt((1 - P->es * sinphi2) / (1 - sinphi2));
+ if( fabs(sinlam) - 1 > -1e-15 )
+ lp.lam = M_PI_2 * (xy.x > 0 ? 1: -1);
+ else
+ lp.lam = asin(sinlam);
+ return lp;
+ }
+
// From EPSG guidance note 7.2
// It suggests as initial guess: