Coverage Report

Created: 2025-08-26 07:08

/src/PROJ/src/projections/vandg.cpp
Line
Count
Source (jump to first uncovered line)
1
// Changes to handle +over are: Copyright 2011-2014 Morelli Informatik
2
3
#include "proj.h"
4
#include "proj_internal.h"
5
6
PROJ_HEAD(vandg, "van der Grinten (I)") "\n\tMisc Sph";
7
8
101k
#define TOL 1.e-10
9
0
#define THIRD .33333333333333333333
10
0
#define C2_27 .07407407407407407407   // 2/27
11
0
#define PI4_3 4.18879020478639098458  // 4*pi/3
12
0
#define PISQ 9.86960440108935861869   // pi^2
13
0
#define TPISQ 19.73920880217871723738 // 2*pi^2
14
0
#define HPISQ 4.93480220054467930934  // pi^2/2
15
16
16.9k
static PJ_XY vandg_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */
17
16.9k
    PJ_XY xy = {0.0, 0.0};
18
16.9k
    double al, al2, g, g2, p2;
19
    // Comments tie this formulation to Snyder (1987), p. 241.
20
16.9k
    p2 = fabs(lp.phi / M_HALFPI); // sin(theta) from (29-6)
21
16.9k
    if ((p2 - TOL) > 1.) {
22
0
        proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
23
0
        return xy;
24
0
    }
25
16.9k
    int sign = 1;
26
16.9k
    if (P->over && fabs(lp.lam) > M_PI)
27
0
        sign = -1;
28
16.9k
    if (p2 > 1.)
29
0
        p2 = 1.;
30
16.9k
    if (fabs(lp.phi) <= TOL) {
31
0
        xy.x = lp.lam;
32
0
        xy.y = 0.;
33
16.9k
    } else if (fabs(lp.lam) <= TOL || fabs(p2 - 1.) < TOL) {
34
0
        xy.x = 0.;
35
0
        xy.y = M_PI * tan(.5 * asin(p2));
36
0
        if (lp.phi < 0.)
37
0
            xy.y = -xy.y;
38
16.9k
    } else {
39
16.9k
        al = .5 * sign * fabs(M_PI / lp.lam - lp.lam / M_PI); // A from (29-3)
40
16.9k
        al2 = al * al;                                        // A^2
41
16.9k
        g = sqrt(1. - p2 * p2);                               // cos(theta)
42
16.9k
        g = g / (p2 + g - 1.);                                // G from (29-4)
43
16.9k
        g2 = g * g;                                           // G^2
44
16.9k
        p2 = g * (2. / p2 - 1.);                              // P from (29-5)
45
16.9k
        p2 = p2 * p2;                                         // P^2
46
16.9k
        {
47
            // Force floating-point computations done on 80 bit on
48
            // i386 to go back to 64 bit. This is to avoid the test failures
49
            // of
50
            // https://github.com/OSGeo/PROJ/issues/1906#issuecomment-583168348
51
            // The choice of p2 is completely arbitrary.
52
16.9k
            volatile double p2_tmp = p2;
53
            // cppcheck-suppress redundantAssignment
54
16.9k
            p2 = p2_tmp;
55
16.9k
        }
56
16.9k
        xy.x = g - p2; // G - P^2
57
16.9k
        g = p2 + al2;  // P^2 + A^2
58
        // (29-1)
59
16.9k
        xy.x = M_PI *
60
16.9k
               fabs(al * xy.x + sqrt(al2 * xy.x * xy.x - g * (g2 - p2))) / g;
61
16.9k
        if (lp.lam < 0.)
62
16.2k
            xy.x = -xy.x;
63
16.9k
        xy.y = fabs(xy.x / M_PI);
64
        // y from (29-2) has been expressed in terms of x here
65
16.9k
        xy.y = 1. - xy.y * (xy.y + 2. * al);
66
16.9k
        if (xy.y < -TOL) {
67
0
            proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
68
0
            return xy;
69
0
        }
70
16.9k
        if (xy.y < 0.)
71
0
            xy.y = 0.;
72
16.9k
        else
73
16.9k
            xy.y = sqrt(xy.y) * (lp.phi < 0. ? -M_PI : M_PI);
74
16.9k
    }
75
76
16.9k
    return xy;
77
16.9k
}
78
79
0
static PJ_LP vandg_s_inverse(PJ_XY xy, PJ *P) { /* Spheroidal, inverse */
80
0
    PJ_LP lp = {0.0, 0.0};
81
0
    double t, c0, c1, c2, c3, al, r2, r, m, d, ay, x2, y2;
82
    // Comments tie this formulation to Snyder (1987), p. 242.
83
0
    x2 = xy.x * xy.x; // pi^2 * X^2
84
0
    if ((ay = fabs(xy.y)) < TOL) {
85
0
        lp.phi = 0.;
86
0
        t = x2 * x2 + TPISQ * (x2 + HPISQ);
87
0
        lp.lam = fabs(xy.x) <= TOL ? 0. : .5 * (x2 - PISQ + sqrt(t)) / xy.x;
88
0
        return (lp);
89
0
    }
90
0
    y2 = xy.y * xy.y;             // pi^2 * Y^2
91
0
    r = x2 + y2;                  // pi^2 * (X^2+Y^2)
92
0
    r2 = r * r;                   // pi^4 * (X^2+Y^2)^2
93
0
    c1 = -M_PI * ay * (r + PISQ); // pi^4 * c1 (29-11)
94
    // pi^4 * c3 (29-13)
95
0
    c3 = r2 + M_TWOPI * (ay * r + M_PI * (y2 + M_PI * (ay + M_HALFPI)));
96
0
    c2 = c1 + PISQ * (r - 3. * y2); // pi^4 * c2 (29-12)
97
0
    c0 = M_PI * ay;                 // pi^2 * Y
98
0
    c2 /= c3;                       // c2/c3
99
0
    al = c1 / c3 - THIRD * c2 * c2; // a1 (29-15)
100
0
    m = 2. * sqrt(-THIRD * al);     // m1 (29-16)
101
0
    d = C2_27 * c2 * c2 * c2 + (c0 * c0 - THIRD * c2 * c1) / c3; // d (29-14)
102
0
    const double al_mul_m = al * m;                              // a1*m1
103
0
    if (fabs(al_mul_m) < 1e-16) {
104
0
        proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
105
0
        return proj_coord_error().lp;
106
0
    }
107
0
    d = 3. * d / al_mul_m; // cos(3*theta1) (29-17)
108
0
    t = fabs(d);
109
0
    if ((t - TOL) <= 1.) {
110
0
        d = t > 1. ? (d > 0. ? 0. : M_PI) : acos(d); // 3*theta1 (29-17)
111
0
        if (r > PISQ) {
112
            // This code path is triggered for coordinates generated in the
113
            // forward path when |long|>180deg and +over
114
0
            d = M_TWOPI - d;
115
0
        }
116
        // (29-18) but change pi/3 to 4*pi/3 to flip sign of cos
117
0
        lp.phi = M_PI * (m * cos(d * THIRD + PI4_3) - THIRD * c2);
118
0
        if (xy.y < 0.)
119
0
            lp.phi = -lp.phi;
120
0
        t = r2 + TPISQ * (x2 - y2 + HPISQ);
121
0
        lp.lam = fabs(xy.x) <= TOL
122
0
                     ? 0.
123
0
                     : .5 * (r - PISQ + (t <= 0. ? 0. : sqrt(t))) / xy.x;
124
0
    } else {
125
0
        proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
126
0
        return lp;
127
0
    }
128
129
0
    return lp;
130
0
}
131
132
217
PJ *PJ_PROJECTION(vandg) {
133
217
    P->es = 0.;
134
217
    P->inv = vandg_s_inverse;
135
217
    P->fwd = vandg_s_forward;
136
137
217
    return P;
138
217
}
139
140
#undef TOL
141
#undef THIRD
142
#undef C2_27
143
#undef PI4_3
144
#undef PISQ
145
#undef TPISQ
146
#undef HPISQ