Coverage Report

Created: 2025-06-09 08:44

/src/gdal/alg/gdalgenericinverse.cpp
Line
Count
Source (jump to first uncovered line)
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
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
0
{
41
0
    const double dfAbsValOut = std::max(fabs(guessedXOut), fabs(guessedYOut));
42
0
    const double dfEps = dfAbsValOut > 0 ? dfAbsValOut * 1e-6 : 1e-6;
43
0
    if (toleranceOnInputCoordinates == 0)
44
0
    {
45
0
        const double dfAbsValIn = std::max(fabs(xIn), fabs(yIn));
46
0
        toleranceOnInputCoordinates =
47
0
            dfAbsValIn > 0 ? dfAbsValIn * 1e-12 : 1e-12;
48
0
    }
49
0
    xOut = guessedXOut;
50
0
    yOut = guessedYOut;
51
0
    double deriv_lam_X = 0;
52
0
    double deriv_lam_Y = 0;
53
0
    double deriv_phi_X = 0;
54
0
    double deriv_phi_Y = 0;
55
0
    for (int i = 0; i < 15; i++)
56
0
    {
57
0
        double xApprox;
58
0
        double yApprox;
59
0
        if (!pfnForwardTranformer(xOut, yOut, xApprox, yApprox,
60
0
                                  pfnForwardTranformerUserData))
61
0
            return false;
62
0
        const double deltaX = xApprox - xIn;
63
0
        const double deltaY = yApprox - yIn;
64
0
        if (fabs(deltaX) < toleranceOnInputCoordinates &&
65
0
            fabs(deltaY) < toleranceOnInputCoordinates)
66
0
        {
67
0
            return true;
68
0
        }
69
70
0
        if (i == 0 || !computeJacobianMatrixOnlyAtFirstIter)
71
0
        {
72
            // Compute Jacobian matrix
73
0
            double xTmp = xOut + dfEps;
74
0
            double yTmp = yOut;
75
0
            double xTmpOut;
76
0
            double yTmpOut;
77
0
            if (!pfnForwardTranformer(xTmp, yTmp, xTmpOut, yTmpOut,
78
0
                                      pfnForwardTranformerUserData))
79
0
                return false;
80
0
            const double deriv_X_lam = (xTmpOut - xApprox) / dfEps;
81
0
            const double deriv_Y_lam = (yTmpOut - yApprox) / dfEps;
82
83
0
            xTmp = xOut;
84
0
            yTmp = yOut + dfEps;
85
0
            if (!pfnForwardTranformer(xTmp, yTmp, xTmpOut, yTmpOut,
86
0
                                      pfnForwardTranformerUserData))
87
0
                return false;
88
0
            const double deriv_X_phi = (xTmpOut - xApprox) / dfEps;
89
0
            const double deriv_Y_phi = (yTmpOut - yApprox) / dfEps;
90
91
            // Inverse of Jacobian matrix
92
0
            const double det =
93
0
                deriv_X_lam * deriv_Y_phi - deriv_X_phi * deriv_Y_lam;
94
0
            if (det != 0)
95
0
            {
96
0
                deriv_lam_X = deriv_Y_phi / det;
97
0
                deriv_lam_Y = -deriv_X_phi / det;
98
0
                deriv_phi_X = -deriv_Y_lam / det;
99
0
                deriv_phi_Y = deriv_X_lam / det;
100
0
            }
101
0
            else
102
0
            {
103
0
                return false;
104
0
            }
105
0
        }
106
107
0
        const double xOutDelta = deltaX * deriv_lam_X + deltaY * deriv_lam_Y;
108
0
        const double yOutDelta = deltaX * deriv_phi_X + deltaY * deriv_phi_Y;
109
0
        xOut -= xOutDelta;
110
0
        yOut -= yOutDelta;
111
0
        if (toleranceOnOutputCoordinates > 0 &&
112
0
            fabs(xOutDelta) < toleranceOnOutputCoordinates &&
113
0
            fabs(yOutDelta) < toleranceOnOutputCoordinates)
114
0
        {
115
0
            return true;
116
0
        }
117
0
    }
118
0
    return false;
119
0
}