Line data Source code
1 : /****************************************************************************** 2 : * 3 : * Project: GDAL 4 : * Purpose: Generic method to compute inverse coordinate transformation from 5 : * forward method 6 : * Author: Even Rouault <even dot rouault at spatialys dot com> 7 : * 8 : ****************************************************************************** 9 : * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys dot com> 10 : * 11 : * SPDX-License-Identifier: MIT 12 : ****************************************************************************/ 13 : 14 : #include <algorithm> 15 : #include <cmath> 16 : 17 : #include "gdalgenericinverse.h" 18 : #include <cstdio> 19 : 20 : /** Compute (xOut, yOut) corresponding to input (xIn, yIn) using 21 : * the provided forward transformation to emulate the reverse direction. 22 : * 23 : * Uses Newton-Raphson method, extended to 2D variables, that is using 24 : * inversion of the Jacobian 2D matrix of partial derivatives. The derivatives 25 : * are estimated numerically from the forward method evaluated at close points. 26 : * 27 : * Starts with initial guess provided by user in (guessedXOut, guessedYOut) 28 : * 29 : * It iterates at most for 15 iterations or as soon as we get below the specified 30 : * tolerance (on input coordinates) 31 : */ 32 76166 : bool GDALGenericInverse2D(double xIn, double yIn, double guessedXOut, 33 : double guessedYOut, 34 : GDALForwardCoordTransformer pfnForwardTranformer, 35 : void *pfnForwardTranformerUserData, double &xOut, 36 : double &yOut, 37 : bool computeJacobianMatrixOnlyAtFirstIter, 38 : double toleranceOnInputCoordinates, 39 : double toleranceOnOutputCoordinates) 40 : { 41 76166 : const double dfAbsValOut = std::max(fabs(guessedXOut), fabs(guessedYOut)); 42 76166 : const double dfEps = dfAbsValOut > 0 ? dfAbsValOut * 1e-6 : 1e-6; 43 76166 : if (toleranceOnInputCoordinates == 0) 44 : { 45 76166 : const double dfAbsValIn = std::max(fabs(xIn), fabs(yIn)); 46 76166 : toleranceOnInputCoordinates = 47 76166 : dfAbsValIn > 0 ? dfAbsValIn * 1e-12 : 1e-12; 48 : } 49 76166 : xOut = guessedXOut; 50 76166 : yOut = guessedYOut; 51 76166 : double deriv_lam_X = 0; 52 76166 : double deriv_lam_Y = 0; 53 76166 : double deriv_phi_X = 0; 54 76166 : double deriv_phi_Y = 0; 55 350352 : for (int i = 0; i < 15; i++) 56 : { 57 : double xApprox; 58 : double yApprox; 59 350326 : if (!pfnForwardTranformer(xOut, yOut, xApprox, yApprox, 60 : pfnForwardTranformerUserData)) 61 76140 : return false; 62 350326 : const double deltaX = xApprox - xIn; 63 350326 : const double deltaY = yApprox - yIn; 64 350326 : if (fabs(deltaX) < toleranceOnInputCoordinates && 65 90091 : fabs(deltaY) < toleranceOnInputCoordinates) 66 : { 67 76140 : return true; 68 : } 69 : 70 274186 : if (i == 0 || !computeJacobianMatrixOnlyAtFirstIter) 71 : { 72 : // Compute Jacobian matrix 73 67065 : double xTmp = xOut + dfEps; 74 67065 : double yTmp = yOut; 75 : double xTmpOut; 76 : double yTmpOut; 77 67065 : if (!pfnForwardTranformer(xTmp, yTmp, xTmpOut, yTmpOut, 78 : pfnForwardTranformerUserData)) 79 0 : return false; 80 67065 : const double deriv_X_lam = (xTmpOut - xApprox) / dfEps; 81 67065 : const double deriv_Y_lam = (yTmpOut - yApprox) / dfEps; 82 : 83 67065 : xTmp = xOut; 84 67065 : yTmp = yOut + dfEps; 85 67065 : if (!pfnForwardTranformer(xTmp, yTmp, xTmpOut, yTmpOut, 86 : pfnForwardTranformerUserData)) 87 0 : return false; 88 67065 : const double deriv_X_phi = (xTmpOut - xApprox) / dfEps; 89 67065 : const double deriv_Y_phi = (yTmpOut - yApprox) / dfEps; 90 : 91 : // Inverse of Jacobian matrix 92 67065 : const double det = 93 67065 : deriv_X_lam * deriv_Y_phi - deriv_X_phi * deriv_Y_lam; 94 67065 : if (det != 0) 95 : { 96 67065 : deriv_lam_X = deriv_Y_phi / det; 97 67065 : deriv_lam_Y = -deriv_X_phi / det; 98 67065 : deriv_phi_X = -deriv_Y_lam / det; 99 67065 : deriv_phi_Y = deriv_X_lam / det; 100 : } 101 : else 102 : { 103 0 : return false; 104 : } 105 : } 106 : 107 274186 : const double xOutDelta = deltaX * deriv_lam_X + deltaY * deriv_lam_Y; 108 274186 : const double yOutDelta = deltaX * deriv_phi_X + deltaY * deriv_phi_Y; 109 274186 : xOut -= xOutDelta; 110 274186 : yOut -= yOutDelta; 111 274186 : if (toleranceOnOutputCoordinates > 0 && 112 0 : fabs(xOutDelta) < toleranceOnOutputCoordinates && 113 0 : fabs(yOutDelta) < toleranceOnOutputCoordinates) 114 : { 115 0 : return true; 116 : } 117 : } 118 26 : return false; 119 : }