Coverage Report

Created: 2025-06-13 06:29

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