/src/proj/src/projections/geos.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | ** libproj -- library of cartographic projections |
3 | | ** |
4 | | ** Copyright (c) 2004 Gerald I. Evenden |
5 | | ** Copyright (c) 2012 Martin Raspaud |
6 | | ** |
7 | | ** See also (section 4.4.3.2): |
8 | | ** https://www.cgms-info.org/documents/pdf_cgms_03.pdf |
9 | | ** |
10 | | ** Permission is hereby granted, free of charge, to any person obtaining |
11 | | ** a copy of this software and associated documentation files (the |
12 | | ** "Software"), to deal in the Software without restriction, including |
13 | | ** without limitation the rights to use, copy, modify, merge, publish, |
14 | | ** distribute, sublicense, and/or sell copies of the Software, and to |
15 | | ** permit persons to whom the Software is furnished to do so, subject to |
16 | | ** the following conditions: |
17 | | ** |
18 | | ** The above copyright notice and this permission notice shall be |
19 | | ** included in all copies or substantial portions of the Software. |
20 | | ** |
21 | | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
22 | | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
23 | | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
24 | | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
25 | | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
26 | | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
27 | | ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
28 | | */ |
29 | | |
30 | | #include <errno.h> |
31 | | #include <math.h> |
32 | | #include <stddef.h> |
33 | | |
34 | | #include "proj.h" |
35 | | #include "proj_internal.h" |
36 | | #include <math.h> |
37 | | |
38 | | namespace { // anonymous namespace |
39 | | struct pj_geos_data { |
40 | | double h; |
41 | | double radius_p; |
42 | | double radius_p2; |
43 | | double radius_p_inv2; |
44 | | double radius_g; |
45 | | double radius_g_1; |
46 | | double C; |
47 | | int flip_axis; |
48 | | }; |
49 | | } // anonymous namespace |
50 | | |
51 | | PROJ_HEAD(geos, "Geostationary Satellite View") "\n\tAzi, Sph&Ell\n\th="; |
52 | | |
53 | 0 | static PJ_XY geos_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */ |
54 | 0 | PJ_XY xy = {0.0, 0.0}; |
55 | 0 | struct pj_geos_data *Q = static_cast<struct pj_geos_data *>(P->opaque); |
56 | 0 | double Vx, Vy, Vz, tmp; |
57 | | |
58 | | /* Calculation of the three components of the vector from satellite to |
59 | | ** position on earth surface (long,lat).*/ |
60 | 0 | tmp = cos(lp.phi); |
61 | 0 | Vx = cos(lp.lam) * tmp; |
62 | 0 | Vy = sin(lp.lam) * tmp; |
63 | 0 | Vz = sin(lp.phi); |
64 | | |
65 | | /* Check visibility*/ |
66 | | |
67 | | /* Calculation based on view angles from satellite.*/ |
68 | 0 | tmp = Q->radius_g - Vx; |
69 | |
|
70 | 0 | if (Q->flip_axis) { |
71 | 0 | xy.x = Q->radius_g_1 * atan(Vy / hypot(Vz, tmp)); |
72 | 0 | xy.y = Q->radius_g_1 * atan(Vz / tmp); |
73 | 0 | } else { |
74 | 0 | xy.x = Q->radius_g_1 * atan(Vy / tmp); |
75 | 0 | xy.y = Q->radius_g_1 * atan(Vz / hypot(Vy, tmp)); |
76 | 0 | } |
77 | |
|
78 | 0 | return xy; |
79 | 0 | } |
80 | | |
81 | 0 | static PJ_XY geos_e_forward(PJ_LP lp, PJ *P) { /* Ellipsoidal, forward */ |
82 | 0 | PJ_XY xy = {0.0, 0.0}; |
83 | 0 | struct pj_geos_data *Q = static_cast<struct pj_geos_data *>(P->opaque); |
84 | 0 | double r, Vx, Vy, Vz, tmp; |
85 | | |
86 | | /* Calculation of geocentric latitude. */ |
87 | 0 | lp.phi = atan(Q->radius_p2 * tan(lp.phi)); |
88 | | |
89 | | /* Calculation of the three components of the vector from satellite to |
90 | | ** position on earth surface (long,lat).*/ |
91 | 0 | r = (Q->radius_p) / hypot(Q->radius_p * cos(lp.phi), sin(lp.phi)); |
92 | 0 | Vx = r * cos(lp.lam) * cos(lp.phi); |
93 | 0 | Vy = r * sin(lp.lam) * cos(lp.phi); |
94 | 0 | Vz = r * sin(lp.phi); |
95 | | |
96 | | /* Check visibility. */ |
97 | 0 | if (((Q->radius_g - Vx) * Vx - Vy * Vy - Vz * Vz * Q->radius_p_inv2) < 0.) { |
98 | 0 | proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
99 | 0 | return xy; |
100 | 0 | } |
101 | | |
102 | | /* Calculation based on view angles from satellite. */ |
103 | 0 | tmp = Q->radius_g - Vx; |
104 | |
|
105 | 0 | if (Q->flip_axis) { |
106 | 0 | xy.x = Q->radius_g_1 * atan(Vy / hypot(Vz, tmp)); |
107 | 0 | xy.y = Q->radius_g_1 * atan(Vz / tmp); |
108 | 0 | } else { |
109 | 0 | xy.x = Q->radius_g_1 * atan(Vy / tmp); |
110 | 0 | xy.y = Q->radius_g_1 * atan(Vz / hypot(Vy, tmp)); |
111 | 0 | } |
112 | |
|
113 | 0 | return xy; |
114 | 0 | } |
115 | | |
116 | 0 | static PJ_LP geos_s_inverse(PJ_XY xy, PJ *P) { /* Spheroidal, inverse */ |
117 | 0 | PJ_LP lp = {0.0, 0.0}; |
118 | 0 | struct pj_geos_data *Q = static_cast<struct pj_geos_data *>(P->opaque); |
119 | 0 | double Vx, Vy, Vz, a, b, k; |
120 | | |
121 | | /* Setting three components of vector from satellite to position.*/ |
122 | 0 | Vx = -1.0; |
123 | 0 | if (Q->flip_axis) { |
124 | 0 | Vz = tan(xy.y / Q->radius_g_1); |
125 | 0 | Vy = tan(xy.x / Q->radius_g_1) * sqrt(1.0 + Vz * Vz); |
126 | 0 | } else { |
127 | 0 | Vy = tan(xy.x / Q->radius_g_1); |
128 | 0 | Vz = tan(xy.y / Q->radius_g_1) * sqrt(1.0 + Vy * Vy); |
129 | 0 | } |
130 | | |
131 | | /* Calculation of terms in cubic equation and determinant.*/ |
132 | 0 | a = Vy * Vy + Vz * Vz + Vx * Vx; |
133 | 0 | b = 2 * Q->radius_g * Vx; |
134 | 0 | const double det = (b * b) - 4 * a * Q->C; |
135 | 0 | if (det < 0.) { |
136 | 0 | proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
137 | 0 | return lp; |
138 | 0 | } |
139 | | |
140 | | /* Calculation of three components of vector from satellite to position.*/ |
141 | 0 | k = (-b - sqrt(det)) / (2 * a); |
142 | 0 | Vx = Q->radius_g + k * Vx; |
143 | 0 | Vy *= k; |
144 | 0 | Vz *= k; |
145 | | |
146 | | /* Calculation of longitude and latitude.*/ |
147 | 0 | lp.lam = atan2(Vy, Vx); |
148 | 0 | lp.phi = atan(Vz * cos(lp.lam) / Vx); |
149 | |
|
150 | 0 | return lp; |
151 | 0 | } |
152 | | |
153 | 0 | static PJ_LP geos_e_inverse(PJ_XY xy, PJ *P) { /* Ellipsoidal, inverse */ |
154 | 0 | PJ_LP lp = {0.0, 0.0}; |
155 | 0 | struct pj_geos_data *Q = static_cast<struct pj_geos_data *>(P->opaque); |
156 | 0 | double Vx, Vy, Vz, a, b, k; |
157 | | |
158 | | /* Setting three components of vector from satellite to position.*/ |
159 | 0 | Vx = -1.0; |
160 | |
|
161 | 0 | if (Q->flip_axis) { |
162 | 0 | Vz = tan(xy.y / Q->radius_g_1); |
163 | 0 | Vy = tan(xy.x / Q->radius_g_1) * hypot(1.0, Vz); |
164 | 0 | } else { |
165 | 0 | Vy = tan(xy.x / Q->radius_g_1); |
166 | 0 | Vz = tan(xy.y / Q->radius_g_1) * hypot(1.0, Vy); |
167 | 0 | } |
168 | | |
169 | | /* Calculation of terms in cubic equation and determinant.*/ |
170 | 0 | a = Vz / Q->radius_p; |
171 | 0 | a = Vy * Vy + a * a + Vx * Vx; |
172 | 0 | b = 2 * Q->radius_g * Vx; |
173 | 0 | const double det = (b * b) - 4 * a * Q->C; |
174 | 0 | if (det < 0.) { |
175 | 0 | proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
176 | 0 | return lp; |
177 | 0 | } |
178 | | |
179 | | /* Calculation of three components of vector from satellite to position.*/ |
180 | 0 | k = (-b - sqrt(det)) / (2. * a); |
181 | 0 | Vx = Q->radius_g + k * Vx; |
182 | 0 | Vy *= k; |
183 | 0 | Vz *= k; |
184 | | |
185 | | /* Calculation of longitude and latitude.*/ |
186 | 0 | lp.lam = atan2(Vy, Vx); |
187 | 0 | lp.phi = atan(Vz * cos(lp.lam) / Vx); |
188 | 0 | lp.phi = atan(Q->radius_p_inv2 * tan(lp.phi)); |
189 | |
|
190 | 0 | return lp; |
191 | 0 | } |
192 | | |
193 | 185 | PJ *PJ_PROJECTION(geos) { |
194 | 185 | char *sweep_axis; |
195 | 185 | struct pj_geos_data *Q = static_cast<struct pj_geos_data *>( |
196 | 185 | calloc(1, sizeof(struct pj_geos_data))); |
197 | 185 | if (nullptr == Q) |
198 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
199 | 185 | P->opaque = Q; |
200 | | |
201 | 185 | Q->h = pj_param(P->ctx, P->params, "dh").f; |
202 | | |
203 | 185 | sweep_axis = pj_param(P->ctx, P->params, "ssweep").s; |
204 | 185 | if (sweep_axis == nullptr) |
205 | 113 | Q->flip_axis = 0; |
206 | 72 | else { |
207 | 72 | if ((sweep_axis[0] != 'x' && sweep_axis[0] != 'y') || |
208 | 72 | sweep_axis[1] != '\0') { |
209 | 9 | proj_log_error( |
210 | 9 | P, _("Invalid value for sweep: it should be equal to x or y.")); |
211 | 9 | return pj_default_destructor(P, |
212 | 9 | PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
213 | 9 | } |
214 | | |
215 | 63 | if (sweep_axis[0] == 'x') |
216 | 16 | Q->flip_axis = 1; |
217 | 47 | else |
218 | 47 | Q->flip_axis = 0; |
219 | 63 | } |
220 | | |
221 | 176 | Q->radius_g_1 = Q->h / P->a; |
222 | 176 | if (Q->radius_g_1 <= 0 || Q->radius_g_1 > 1e10) { |
223 | 16 | proj_log_error(P, _("Invalid value for h.")); |
224 | 16 | return pj_default_destructor(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
225 | 16 | } |
226 | 160 | Q->radius_g = 1. + Q->radius_g_1; |
227 | 160 | Q->C = Q->radius_g * Q->radius_g - 1.0; |
228 | 160 | if (P->es != 0.0) { |
229 | 88 | Q->radius_p = sqrt(P->one_es); |
230 | 88 | Q->radius_p2 = P->one_es; |
231 | 88 | Q->radius_p_inv2 = P->rone_es; |
232 | 88 | P->inv = geos_e_inverse; |
233 | 88 | P->fwd = geos_e_forward; |
234 | 88 | } else { |
235 | 72 | Q->radius_p = Q->radius_p2 = Q->radius_p_inv2 = 1.0; |
236 | 72 | P->inv = geos_s_inverse; |
237 | 72 | P->fwd = geos_s_forward; |
238 | 72 | } |
239 | | |
240 | 160 | return P; |
241 | 176 | } |