Coverage Report

Created: 2024-02-25 06:14

/src/PROJ/src/projections/nsper.cpp
Line
Count
Source (jump to first uncovered line)
1
2
#include "proj.h"
3
#include "proj_internal.h"
4
#include <errno.h>
5
#include <math.h>
6
7
/* Note: EPSG Guidance 7-2 describes a Vertical Perspective method (EPSG::9838),
8
 * that extends 'nsper' with ellipsoidal development, and a ellipsoidal height
9
 * of topocentric origin for the projection plan.
10
 */
11
12
namespace pj_nsper_ns {
13
enum Mode { N_POLE = 0, S_POLE = 1, EQUIT = 2, OBLIQ = 3 };
14
}
15
16
namespace { // anonymous namespace
17
struct pj_nsper_data {
18
    double height;
19
    double sinph0;
20
    double cosph0;
21
    double p;
22
    double rp;
23
    double pn1;
24
    double pfact;
25
    double h;
26
    double cg;
27
    double sg;
28
    double sw;
29
    double cw;
30
    enum pj_nsper_ns::Mode mode;
31
    int tilt;
32
};
33
} // anonymous namespace
34
35
PROJ_HEAD(nsper, "Near-sided perspective") "\n\tAzi, Sph\n\th=";
36
PROJ_HEAD(tpers, "Tilted perspective") "\n\tAzi, Sph\n\ttilt= azi= h=";
37
38
83
#define EPS10 1.e-10
39
40
0
static PJ_XY nsper_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */
41
0
    PJ_XY xy = {0.0, 0.0};
42
0
    struct pj_nsper_data *Q = static_cast<struct pj_nsper_data *>(P->opaque);
43
0
    double coslam, cosphi, sinphi;
44
45
0
    sinphi = sin(lp.phi);
46
0
    cosphi = cos(lp.phi);
47
0
    coslam = cos(lp.lam);
48
0
    switch (Q->mode) {
49
0
    case pj_nsper_ns::OBLIQ:
50
0
        xy.y = Q->sinph0 * sinphi + Q->cosph0 * cosphi * coslam;
51
0
        break;
52
0
    case pj_nsper_ns::EQUIT:
53
0
        xy.y = cosphi * coslam;
54
0
        break;
55
0
    case pj_nsper_ns::S_POLE:
56
0
        xy.y = -sinphi;
57
0
        break;
58
0
    case pj_nsper_ns::N_POLE:
59
0
        xy.y = sinphi;
60
0
        break;
61
0
    }
62
0
    if (xy.y < Q->rp) {
63
0
        proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
64
0
        return xy;
65
0
    }
66
0
    xy.y = Q->pn1 / (Q->p - xy.y);
67
0
    xy.x = xy.y * cosphi * sin(lp.lam);
68
0
    switch (Q->mode) {
69
0
    case pj_nsper_ns::OBLIQ:
70
0
        xy.y *= (Q->cosph0 * sinphi - Q->sinph0 * cosphi * coslam);
71
0
        break;
72
0
    case pj_nsper_ns::EQUIT:
73
0
        xy.y *= sinphi;
74
0
        break;
75
0
    case pj_nsper_ns::N_POLE:
76
0
        coslam = -coslam;
77
0
        PROJ_FALLTHROUGH;
78
0
    case pj_nsper_ns::S_POLE:
79
0
        xy.y *= cosphi * coslam;
80
0
        break;
81
0
    }
82
0
    if (Q->tilt) {
83
0
        double yt, ba;
84
85
0
        yt = xy.y * Q->cg + xy.x * Q->sg;
86
0
        ba = 1. / (yt * Q->sw * Q->h + Q->cw);
87
0
        xy.x = (xy.x * Q->cg - xy.y * Q->sg) * Q->cw * ba;
88
0
        xy.y = yt * ba;
89
0
    }
90
0
    return xy;
91
0
}
92
93
0
static PJ_LP nsper_s_inverse(PJ_XY xy, PJ *P) { /* Spheroidal, inverse */
94
0
    PJ_LP lp = {0.0, 0.0};
95
0
    struct pj_nsper_data *Q = static_cast<struct pj_nsper_data *>(P->opaque);
96
0
    double rh;
97
98
0
    if (Q->tilt) {
99
0
        double bm, bq, yt;
100
101
0
        yt = 1. / (Q->pn1 - xy.y * Q->sw);
102
0
        bm = Q->pn1 * xy.x * yt;
103
0
        bq = Q->pn1 * xy.y * Q->cw * yt;
104
0
        xy.x = bm * Q->cg + bq * Q->sg;
105
0
        xy.y = bq * Q->cg - bm * Q->sg;
106
0
    }
107
0
    rh = hypot(xy.x, xy.y);
108
0
    if (fabs(rh) <= EPS10) {
109
0
        lp.lam = 0.;
110
0
        lp.phi = P->phi0;
111
0
    } else {
112
0
        double cosz, sinz;
113
0
        sinz = 1. - rh * rh * Q->pfact;
114
0
        if (sinz < 0.) {
115
0
            proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
116
0
            return lp;
117
0
        }
118
0
        sinz = (Q->p - sqrt(sinz)) / (Q->pn1 / rh + rh / Q->pn1);
119
0
        cosz = sqrt(1. - sinz * sinz);
120
0
        switch (Q->mode) {
121
0
        case pj_nsper_ns::OBLIQ:
122
0
            lp.phi = asin(cosz * Q->sinph0 + xy.y * sinz * Q->cosph0 / rh);
123
0
            xy.y = (cosz - Q->sinph0 * sin(lp.phi)) * rh;
124
0
            xy.x *= sinz * Q->cosph0;
125
0
            break;
126
0
        case pj_nsper_ns::EQUIT:
127
0
            lp.phi = asin(xy.y * sinz / rh);
128
0
            xy.y = cosz * rh;
129
0
            xy.x *= sinz;
130
0
            break;
131
0
        case pj_nsper_ns::N_POLE:
132
0
            lp.phi = asin(cosz);
133
0
            xy.y = -xy.y;
134
0
            break;
135
0
        case pj_nsper_ns::S_POLE:
136
0
            lp.phi = -asin(cosz);
137
0
            break;
138
0
        }
139
0
        lp.lam = atan2(xy.x, xy.y);
140
0
    }
141
0
    return lp;
142
0
}
143
144
43
static PJ *nsper_setup(PJ *P) {
145
43
    struct pj_nsper_data *Q = static_cast<struct pj_nsper_data *>(P->opaque);
146
147
43
    Q->height = pj_param(P->ctx, P->params, "dh").f;
148
149
43
    if (fabs(fabs(P->phi0) - M_HALFPI) < EPS10)
150
3
        Q->mode = P->phi0 < 0. ? pj_nsper_ns::S_POLE : pj_nsper_ns::N_POLE;
151
40
    else if (fabs(P->phi0) < EPS10)
152
30
        Q->mode = pj_nsper_ns::EQUIT;
153
10
    else {
154
10
        Q->mode = pj_nsper_ns::OBLIQ;
155
10
        Q->sinph0 = sin(P->phi0);
156
10
        Q->cosph0 = cos(P->phi0);
157
10
    }
158
43
    Q->pn1 = Q->height / P->a; /* normalize by radius */
159
43
    if (Q->pn1 <= 0 || Q->pn1 > 1e10) {
160
17
        proj_log_error(P, _("Invalid value for h"));
161
17
        return pj_default_destructor(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
162
17
    }
163
26
    Q->p = 1. + Q->pn1;
164
26
    Q->rp = 1. / Q->p;
165
26
    Q->h = 1. / Q->pn1;
166
26
    Q->pfact = (Q->p + 1.) * Q->h;
167
26
    P->inv = nsper_s_inverse;
168
26
    P->fwd = nsper_s_forward;
169
26
    P->es = 0.;
170
171
26
    return P;
172
43
}
173
174
29
PJ *PJ_PROJECTION(nsper) {
175
29
    struct pj_nsper_data *Q = static_cast<struct pj_nsper_data *>(
176
29
        calloc(1, sizeof(struct pj_nsper_data)));
177
29
    if (nullptr == Q)
178
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
179
29
    P->opaque = Q;
180
181
29
    Q->tilt = 0;
182
183
29
    return nsper_setup(P);
184
29
}
185
186
14
PJ *PJ_PROJECTION(tpers) {
187
14
    double omega, gamma;
188
189
14
    struct pj_nsper_data *Q = static_cast<struct pj_nsper_data *>(
190
14
        calloc(1, sizeof(struct pj_nsper_data)));
191
14
    if (nullptr == Q)
192
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
193
14
    P->opaque = Q;
194
195
14
    omega = pj_param(P->ctx, P->params, "rtilt").f;
196
14
    gamma = pj_param(P->ctx, P->params, "razi").f;
197
14
    Q->tilt = 1;
198
14
    Q->cg = cos(gamma);
199
14
    Q->sg = sin(gamma);
200
14
    Q->cw = cos(omega);
201
14
    Q->sw = sin(omega);
202
203
14
    return nsper_setup(P);
204
14
}
205
206
#undef EPS10