/src/PROJ/src/generic_inverse.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: PROJ |
4 | | * Purpose: Generic method to compute inverse projection from forward method |
5 | | * Author: Even Rouault <even dot rouault at spatialys dot com> |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com> |
9 | | * |
10 | | * Permission is hereby granted, free of charge, to any person obtaining a |
11 | | * copy of this software and associated documentation files (the "Software"), |
12 | | * to deal in the Software without restriction, including without limitation |
13 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
14 | | * and/or sell copies of the Software, and to permit persons to whom the |
15 | | * Software is furnished to do so, subject to the following conditions: |
16 | | * |
17 | | * The above copyright notice and this permission notice shall be included |
18 | | * in all copies or substantial portions of the Software. |
19 | | * |
20 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
21 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
22 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
23 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
24 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
25 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
26 | | * DEALINGS IN THE SOFTWARE. |
27 | | ****************************************************************************/ |
28 | | |
29 | | #include "proj_internal.h" |
30 | | |
31 | | #include <algorithm> |
32 | | #include <cmath> |
33 | | |
34 | | /** Compute (lam, phi) corresponding to input (xy.x, xy.y) for projection P. |
35 | | * |
36 | | * Uses Newton-Raphson method, extended to 2D variables, that is using |
37 | | * inversion of the Jacobian 2D matrix of partial derivatives. The derivatives |
38 | | * are estimated numerically from the P->fwd method evaluated at close points. |
39 | | * |
40 | | * Note: thresholds used have been verified to work with adams_ws2 and wink2 |
41 | | * |
42 | | * Starts with initial guess provided by user in lpInitial |
43 | | */ |
44 | | PJ_LP pj_generic_inverse_2d(PJ_XY xy, PJ *P, PJ_LP lpInitial, |
45 | 0 | double deltaXYTolerance) { |
46 | 0 | PJ_LP lp = lpInitial; |
47 | 0 | double deriv_lam_X = 0; |
48 | 0 | double deriv_lam_Y = 0; |
49 | 0 | double deriv_phi_X = 0; |
50 | 0 | double deriv_phi_Y = 0; |
51 | 0 | for (int i = 0; i < 15; i++) { |
52 | 0 | PJ_XY xyApprox = P->fwd(lp, P); |
53 | 0 | const double deltaX = xyApprox.x - xy.x; |
54 | 0 | const double deltaY = xyApprox.y - xy.y; |
55 | 0 | if (fabs(deltaX) < deltaXYTolerance && |
56 | 0 | fabs(deltaY) < deltaXYTolerance) { |
57 | 0 | return lp; |
58 | 0 | } |
59 | | |
60 | 0 | if (i == 0 || fabs(deltaX) > 1e-6 || fabs(deltaY) > 1e-6) { |
61 | | // Compute Jacobian matrix (only if we aren't close to the final |
62 | | // result to speed things a bit) |
63 | 0 | PJ_LP lp2; |
64 | 0 | PJ_XY xy2; |
65 | 0 | const double dLam = lp.lam > 0 ? -1e-6 : 1e-6; |
66 | 0 | lp2.lam = lp.lam + dLam; |
67 | 0 | lp2.phi = lp.phi; |
68 | 0 | xy2 = P->fwd(lp2, P); |
69 | 0 | const double deriv_X_lam = (xy2.x - xyApprox.x) / dLam; |
70 | 0 | const double deriv_Y_lam = (xy2.y - xyApprox.y) / dLam; |
71 | |
|
72 | 0 | const double dPhi = lp.phi > 0 ? -1e-6 : 1e-6; |
73 | 0 | lp2.lam = lp.lam; |
74 | 0 | lp2.phi = lp.phi + dPhi; |
75 | 0 | xy2 = P->fwd(lp2, P); |
76 | 0 | const double deriv_X_phi = (xy2.x - xyApprox.x) / dPhi; |
77 | 0 | const double deriv_Y_phi = (xy2.y - xyApprox.y) / dPhi; |
78 | | |
79 | | // Inverse of Jacobian matrix |
80 | 0 | const double det = |
81 | 0 | deriv_X_lam * deriv_Y_phi - deriv_X_phi * deriv_Y_lam; |
82 | 0 | if (det != 0) { |
83 | 0 | deriv_lam_X = deriv_Y_phi / det; |
84 | 0 | deriv_lam_Y = -deriv_X_phi / det; |
85 | 0 | deriv_phi_X = -deriv_Y_lam / det; |
86 | 0 | deriv_phi_Y = deriv_X_lam / det; |
87 | 0 | } |
88 | 0 | } |
89 | | |
90 | | // Limit the amplitude of correction to avoid overshoots due to |
91 | | // bad initial guess |
92 | 0 | const double delta_lam = std::max( |
93 | 0 | std::min(deltaX * deriv_lam_X + deltaY * deriv_lam_Y, 0.3), -0.3); |
94 | 0 | lp.lam -= delta_lam; |
95 | 0 | if (lp.lam < -M_PI) |
96 | 0 | lp.lam = -M_PI; |
97 | 0 | else if (lp.lam > M_PI) |
98 | 0 | lp.lam = M_PI; |
99 | |
|
100 | 0 | const double delta_phi = std::max( |
101 | 0 | std::min(deltaX * deriv_phi_X + deltaY * deriv_phi_Y, 0.3), -0.3); |
102 | 0 | lp.phi -= delta_phi; |
103 | 0 | if (lp.phi < -M_HALFPI) |
104 | 0 | lp.phi = -M_HALFPI; |
105 | 0 | else if (lp.phi > M_HALFPI) |
106 | 0 | lp.phi = M_HALFPI; |
107 | 0 | } |
108 | 0 | proj_context_errno_set(P->ctx, |
109 | 0 | PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
110 | 0 | return lp; |
111 | 0 | } |