/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 |