Coverage Report

Created: 2025-06-22 06:59

/src/proj/src/projections/natearth.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
The Natural Earth projection was designed by Tom Patterson, US National Park
3
Service, in 2007, using Flex Projector. The shape of the original projection
4
was defined at every 5 degrees and piece-wise cubic spline interpolation was
5
used to compute the complete graticule.
6
The code here uses polynomial functions instead of cubic splines and
7
is therefore much simpler to program. The polynomial approximation was
8
developed by Bojan Savric, in collaboration with Tom Patterson and Bernhard
9
Jenny, Institute of Cartography, ETH Zurich. It slightly deviates from
10
Patterson's original projection by adding additional curvature to meridians
11
where they meet the horizontal pole line. This improvement is by intention
12
and designed in collaboration with Tom Patterson.
13
Port to PROJ.4 by Bernhard Jenny, 6 June 2011
14
*/
15
16
#include <math.h>
17
18
#include "proj.h"
19
#include "proj_internal.h"
20
21
PROJ_HEAD(natearth, "Natural Earth") "\n\tPCyl, Sph";
22
23
0
#define A0 0.8707
24
0
#define A1 -0.131979
25
0
#define A2 -0.013791
26
0
#define A3 0.003971
27
0
#define A4 -0.001529
28
0
#define B0 1.007226
29
0
#define B1 0.015085
30
0
#define B2 -0.044475
31
0
#define B3 0.028874
32
0
#define B4 -0.005916
33
0
#define C0 B0
34
0
#define C1 (3 * B1)
35
0
#define C2 (7 * B2)
36
0
#define C3 (9 * B3)
37
0
#define C4 (11 * B4)
38
0
#define EPS 1e-11
39
0
#define MAX_Y (0.8707 * 0.52 * M_PI)
40
/* Not sure at all of the appropriate number for MAX_ITER... */
41
0
#define MAX_ITER 100
42
43
0
static PJ_XY natearth_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */
44
0
    PJ_XY xy = {0.0, 0.0};
45
0
    double phi2, phi4;
46
0
    (void)P;
47
48
0
    phi2 = lp.phi * lp.phi;
49
0
    phi4 = phi2 * phi2;
50
0
    xy.x = lp.lam *
51
0
           (A0 + phi2 * (A1 + phi2 * (A2 + phi4 * phi2 * (A3 + phi2 * A4))));
52
0
    xy.y = lp.phi * (B0 + phi2 * (B1 + phi4 * (B2 + B3 * phi2 + B4 * phi4)));
53
0
    return xy;
54
0
}
55
56
0
static PJ_LP natearth_s_inverse(PJ_XY xy, PJ *P) { /* Spheroidal, inverse */
57
0
    PJ_LP lp = {0.0, 0.0};
58
0
    double yc, y2, y4, f, fder;
59
0
    int i;
60
0
    (void)P;
61
62
    /* make sure y is inside valid range */
63
0
    if (xy.y > MAX_Y) {
64
0
        xy.y = MAX_Y;
65
0
    } else if (xy.y < -MAX_Y) {
66
0
        xy.y = -MAX_Y;
67
0
    }
68
69
    /* latitude */
70
0
    yc = xy.y;
71
0
    for (i = MAX_ITER; i; --i) { /* Newton-Raphson */
72
0
        y2 = yc * yc;
73
0
        y4 = y2 * y2;
74
0
        f = (yc * (B0 + y2 * (B1 + y4 * (B2 + B3 * y2 + B4 * y4)))) - xy.y;
75
0
        fder = C0 + y2 * (C1 + y4 * (C2 + C3 * y2 + C4 * y4));
76
0
        const double tol = f / fder;
77
0
        yc -= tol;
78
0
        if (fabs(tol) < EPS) {
79
0
            break;
80
0
        }
81
0
    }
82
0
    if (i == 0)
83
0
        proj_context_errno_set(
84
0
            P->ctx, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
85
0
    lp.phi = yc;
86
87
    /* longitude */
88
0
    y2 = yc * yc;
89
0
    lp.lam =
90
0
        xy.x / (A0 + y2 * (A1 + y2 * (A2 + y2 * y2 * y2 * (A3 + y2 * A4))));
91
92
0
    return lp;
93
0
}
94
95
18
PJ *PJ_PROJECTION(natearth) {
96
18
    P->es = 0;
97
18
    P->inv = natearth_s_inverse;
98
18
    P->fwd = natearth_s_forward;
99
100
18
    return P;
101
18
}
102
103
#undef A0
104
#undef A1
105
#undef A2
106
#undef A3
107
#undef A4
108
#undef A5
109
#undef B0
110
#undef B1
111
#undef B2
112
#undef B3
113
#undef C0
114
#undef C1
115
#undef C2
116
#undef C3
117
#undef EPS
118
#undef MAX_Y
119
#undef MAX_ITER