/src/PROJ/src/projections/sconics.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 | | namespace pj_sconics_ns { |
8 | | enum Type { |
9 | | EULER = 0, |
10 | | MURD1 = 1, |
11 | | MURD2 = 2, |
12 | | MURD3 = 3, |
13 | | PCONIC = 4, |
14 | | TISSOT = 5, |
15 | | VITK1 = 6 |
16 | | }; |
17 | | } |
18 | | |
19 | | namespace { // anonymous namespace |
20 | | struct pj_sconics_data { |
21 | | double n; |
22 | | double rho_c; |
23 | | double rho_0; |
24 | | double sig; |
25 | | double c1, c2; |
26 | | enum pj_sconics_ns::Type type; |
27 | | }; |
28 | | } // anonymous namespace |
29 | | |
30 | 8 | #define EPS10 1.e-10 |
31 | 143 | #define EPS 1e-10 |
32 | | #define LINE2 "\n\tConic, Sph\n\tlat_1= and lat_2=" |
33 | | |
34 | | PROJ_HEAD(euler, "Euler") LINE2; |
35 | | PROJ_HEAD(murd1, "Murdoch I") LINE2; |
36 | | PROJ_HEAD(murd2, "Murdoch II") LINE2; |
37 | | PROJ_HEAD(murd3, "Murdoch III") LINE2; |
38 | | PROJ_HEAD(pconic, "Perspective Conic") LINE2; |
39 | | PROJ_HEAD(tissot, "Tissot") LINE2; |
40 | | PROJ_HEAD(vitk1, "Vitkovsky I") LINE2; |
41 | | |
42 | | /* get common factors for simple conics */ |
43 | 64 | static int phi12(PJ *P, double *del) { |
44 | 64 | double p1, p2; |
45 | 64 | int err = 0; |
46 | | |
47 | 64 | if (!pj_param(P->ctx, P->params, "tlat_1").i) { |
48 | 11 | proj_log_error(P, _("Missing parameter: lat_1 should be specified")); |
49 | 11 | err = PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE; |
50 | 53 | } else if (!pj_param(P->ctx, P->params, "tlat_2").i) { |
51 | 5 | proj_log_error(P, _("Missing parameter: lat_2 should be specified")); |
52 | 5 | err = PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE; |
53 | 48 | } else { |
54 | 48 | p1 = pj_param(P->ctx, P->params, "rlat_1").f; |
55 | 48 | p2 = pj_param(P->ctx, P->params, "rlat_2").f; |
56 | 48 | *del = 0.5 * (p2 - p1); |
57 | 48 | const double sig = 0.5 * (p2 + p1); |
58 | 48 | static_cast<struct pj_sconics_data *>(P->opaque)->sig = sig; |
59 | 48 | err = (fabs(*del) < EPS || fabs(sig) < EPS) |
60 | 48 | ? PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE |
61 | 48 | : 0; |
62 | 48 | if (err) { |
63 | 1 | proj_log_error( |
64 | 1 | P, _("Illegal value for lat_1 and lat_2: |lat_1 - lat_2| " |
65 | 1 | "and |lat_1 + lat_2| should be > 0")); |
66 | 1 | } |
67 | 48 | } |
68 | 64 | return err; |
69 | 64 | } |
70 | | |
71 | 0 | static PJ_XY sconics_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */ |
72 | 0 | PJ_XY xy = {0.0, 0.0}; |
73 | 0 | struct pj_sconics_data *Q = |
74 | 0 | static_cast<struct pj_sconics_data *>(P->opaque); |
75 | 0 | double rho; |
76 | |
|
77 | 0 | switch (Q->type) { |
78 | 0 | case pj_sconics_ns::MURD2: |
79 | 0 | rho = Q->rho_c + tan(Q->sig - lp.phi); |
80 | 0 | break; |
81 | 0 | case pj_sconics_ns::PCONIC: |
82 | 0 | rho = Q->c2 * (Q->c1 - tan(lp.phi - Q->sig)); |
83 | 0 | break; |
84 | 0 | default: |
85 | 0 | rho = Q->rho_c - lp.phi; |
86 | 0 | break; |
87 | 0 | } |
88 | | |
89 | 0 | xy.x = rho * sin(lp.lam *= Q->n); |
90 | 0 | xy.y = Q->rho_0 - rho * cos(lp.lam); |
91 | 0 | return xy; |
92 | 0 | } |
93 | | |
94 | | static PJ_LP |
95 | | sconics_s_inverse(PJ_XY xy, |
96 | 0 | PJ *P) { /* Spheroidal, (and ellipsoidal?) inverse */ |
97 | 0 | PJ_LP lp = {0.0, 0.0}; |
98 | 0 | struct pj_sconics_data *Q = |
99 | 0 | static_cast<struct pj_sconics_data *>(P->opaque); |
100 | 0 | double rho; |
101 | |
|
102 | 0 | xy.y = Q->rho_0 - xy.y; |
103 | 0 | rho = hypot(xy.x, xy.y); |
104 | 0 | if (Q->n < 0.) { |
105 | 0 | rho = -rho; |
106 | 0 | xy.x = -xy.x; |
107 | 0 | xy.y = -xy.y; |
108 | 0 | } |
109 | |
|
110 | 0 | lp.lam = atan2(xy.x, xy.y) / Q->n; |
111 | |
|
112 | 0 | switch (Q->type) { |
113 | 0 | case pj_sconics_ns::PCONIC: |
114 | 0 | lp.phi = atan(Q->c1 - rho / Q->c2) + Q->sig; |
115 | 0 | break; |
116 | 0 | case pj_sconics_ns::MURD2: |
117 | 0 | lp.phi = Q->sig - atan(rho - Q->rho_c); |
118 | 0 | break; |
119 | 0 | default: |
120 | 0 | lp.phi = Q->rho_c - rho; |
121 | 0 | } |
122 | 0 | return lp; |
123 | 0 | } |
124 | | |
125 | 64 | static PJ *pj_sconics_setup(PJ *P, enum pj_sconics_ns::Type type) { |
126 | 64 | double del, cs; |
127 | 64 | int err; |
128 | 64 | struct pj_sconics_data *Q = static_cast<struct pj_sconics_data *>( |
129 | 64 | calloc(1, sizeof(struct pj_sconics_data))); |
130 | 64 | if (nullptr == Q) |
131 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
132 | 64 | P->opaque = Q; |
133 | 64 | Q->type = type; |
134 | | |
135 | 64 | err = phi12(P, &del); |
136 | 64 | if (err) |
137 | 17 | return pj_default_destructor(P, err); |
138 | | |
139 | 47 | switch (Q->type) { |
140 | | |
141 | 0 | case pj_sconics_ns::TISSOT: |
142 | 0 | Q->n = sin(Q->sig); |
143 | 0 | cs = cos(del); |
144 | 0 | Q->rho_c = Q->n / cs + cs / Q->n; |
145 | 0 | Q->rho_0 = sqrt((Q->rho_c - 2 * sin(P->phi0)) / Q->n); |
146 | 0 | break; |
147 | | |
148 | 3 | case pj_sconics_ns::MURD1: |
149 | 3 | Q->rho_c = sin(del) / (del * tan(Q->sig)) + Q->sig; |
150 | 3 | Q->rho_0 = Q->rho_c - P->phi0; |
151 | 3 | Q->n = sin(Q->sig); |
152 | 3 | break; |
153 | | |
154 | 12 | case pj_sconics_ns::MURD2: |
155 | 12 | Q->rho_c = (cs = sqrt(cos(del))) / tan(Q->sig); |
156 | 12 | Q->rho_0 = Q->rho_c + tan(Q->sig - P->phi0); |
157 | 12 | Q->n = sin(Q->sig) * cs; |
158 | 12 | break; |
159 | | |
160 | 11 | case pj_sconics_ns::MURD3: |
161 | 11 | Q->rho_c = del / (tan(Q->sig) * tan(del)) + Q->sig; |
162 | 11 | Q->rho_0 = Q->rho_c - P->phi0; |
163 | 11 | Q->n = sin(Q->sig) * sin(del) * tan(del) / (del * del); |
164 | 11 | break; |
165 | | |
166 | 9 | case pj_sconics_ns::EULER: |
167 | 9 | Q->n = sin(Q->sig) * sin(del) / del; |
168 | 9 | del *= 0.5; |
169 | 9 | Q->rho_c = del / (tan(del) * tan(Q->sig)) + Q->sig; |
170 | 9 | Q->rho_0 = Q->rho_c - P->phi0; |
171 | 9 | break; |
172 | | |
173 | 8 | case pj_sconics_ns::PCONIC: |
174 | 8 | Q->n = sin(Q->sig); |
175 | 8 | Q->c2 = cos(del); |
176 | 8 | Q->c1 = 1. / tan(Q->sig); |
177 | 8 | del = P->phi0 - Q->sig; |
178 | 8 | if (fabs(del) - EPS10 >= M_HALFPI) { |
179 | 2 | proj_log_error( |
180 | 2 | P, _("Invalid value for lat_0/lat_1/lat_2: |lat_0 - 0.5 * " |
181 | 2 | "(lat_1 + lat_2)| should be < 90°")); |
182 | 2 | return pj_default_destructor(P, |
183 | 2 | PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
184 | 2 | } |
185 | 6 | Q->rho_0 = Q->c2 * (Q->c1 - tan(del)); |
186 | 6 | break; |
187 | | |
188 | 4 | case pj_sconics_ns::VITK1: |
189 | 4 | cs = tan(del); |
190 | 4 | Q->n = cs * sin(Q->sig) / del; |
191 | 4 | Q->rho_c = del / (cs * tan(Q->sig)) + Q->sig; |
192 | 4 | Q->rho_0 = Q->rho_c - P->phi0; |
193 | 4 | break; |
194 | 47 | } |
195 | | |
196 | 45 | P->inv = sconics_s_inverse; |
197 | 45 | P->fwd = sconics_s_forward; |
198 | 45 | P->es = 0; |
199 | 45 | return (P); |
200 | 47 | } |
201 | | |
202 | 10 | PJ *PJ_PROJECTION(euler) { return pj_sconics_setup(P, pj_sconics_ns::EULER); } |
203 | | |
204 | 3 | PJ *PJ_PROJECTION(tissot) { return pj_sconics_setup(P, pj_sconics_ns::TISSOT); } |
205 | | |
206 | 3 | PJ *PJ_PROJECTION(murd1) { return pj_sconics_setup(P, pj_sconics_ns::MURD1); } |
207 | | |
208 | 15 | PJ *PJ_PROJECTION(murd2) { return pj_sconics_setup(P, pj_sconics_ns::MURD2); } |
209 | | |
210 | 13 | PJ *PJ_PROJECTION(murd3) { return pj_sconics_setup(P, pj_sconics_ns::MURD3); } |
211 | | |
212 | 12 | PJ *PJ_PROJECTION(pconic) { return pj_sconics_setup(P, pj_sconics_ns::PCONIC); } |
213 | | |
214 | 8 | PJ *PJ_PROJECTION(vitk1) { return pj_sconics_setup(P, pj_sconics_ns::VITK1); } |
215 | | |
216 | | #undef EPS10 |
217 | | #undef EPS |
218 | | #undef LINE2 |