]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/geometry/srs/projections/proj/qsc.hpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / boost / geometry / srs / projections / proj / qsc.hpp
CommitLineData
92f5a8d4 1// Boost.Geometry - gis-projections (based on PROJ4)
11fdf7f2
TL
2
3// Copyright (c) 2008-2015 Barend Gehrels, Amsterdam, the Netherlands.
4
92f5a8d4
TL
5// This file was modified by Oracle on 2017, 2018, 2019.
6// Modifications copyright (c) 2017-2019, Oracle and/or its affiliates.
11fdf7f2
TL
7// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle.
8
9// Use, modification and distribution is subject to the Boost Software License,
10// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
11// http://www.boost.org/LICENSE_1_0.txt)
12
13// This file is converted from PROJ4, http://trac.osgeo.org/proj
14// PROJ4 is originally written by Gerald Evenden (then of the USGS)
15// PROJ4 is maintained by Frank Warmerdam
16// PROJ4 is converted to Boost.Geometry by Barend Gehrels
17
92f5a8d4 18// Last updated version of proj: 5.0.0
11fdf7f2
TL
19
20// Original copyright notice:
21
22// This implements the Quadrilateralized Spherical Cube (QSC) projection.
23// Copyright (c) 2011, 2012 Martin Lambers <marlam@marlam.de>
92f5a8d4
TL
24
25// Permission is hereby granted, free of charge, to any person obtaining a
26// copy of this software and associated documentation files (the "Software"),
27// to deal in the Software without restriction, including without limitation
28// the rights to use, copy, modify, merge, publish, distribute, sublicense,
29// and/or sell copies of the Software, and to permit persons to whom the
30// Software is furnished to do so, subject to the following conditions:
31
32// The above copyright notice and this permission notice shall be included
33// in all copies or substantial portions of the Software.
34
35// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
36// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
38// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
40// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
41// DEALINGS IN THE SOFTWARE.
42
11fdf7f2
TL
43// The QSC projection was introduced in:
44// [OL76]
45// E.M. O'Neill and R.E. Laubscher, "Extended Studies of a Quadrilateralized
46// Spherical Cube Earth Data Base", Naval Environmental Prediction Research
47// Facility Tech. Report NEPRF 3-76 (CSC), May 1976.
92f5a8d4 48
11fdf7f2
TL
49// The preceding shift from an ellipsoid to a sphere, which allows to apply
50// this projection to ellipsoids as used in the Ellipsoidal Cube Map model,
51// is described in
52// [LK12]
53// M. Lambers and A. Kolb, "Ellipsoidal Cube Maps for Accurate Rendering of
54// Planetary-Scale Terrain Data", Proc. Pacfic Graphics (Short Papers), Sep.
55// 2012
92f5a8d4 56
11fdf7f2
TL
57// You have to choose one of the following projection centers,
58// corresponding to the centers of the six cube faces:
59// phi0 = 0.0, lam0 = 0.0 ("front" face)
60// phi0 = 0.0, lam0 = 90.0 ("right" face)
61// phi0 = 0.0, lam0 = 180.0 ("back" face)
62// phi0 = 0.0, lam0 = -90.0 ("left" face)
63// phi0 = 90.0 ("top" face)
64// phi0 = -90.0 ("bottom" face)
65// Other projection centers will not work!
92f5a8d4 66
11fdf7f2
TL
67// In the projection code below, each cube face is handled differently.
68// See the computation of the face parameter in the ENTRY0(qsc) function
69// and the handling of different face values (FACE_*) in the forward and
70// inverse projections.
92f5a8d4 71
11fdf7f2
TL
72// Furthermore, the projection is originally only defined for theta angles
73// between (-1/4 * PI) and (+1/4 * PI) on the current cube face. This area
74// of definition is named AREA_0 in the projection code below. The other
75// three areas of a cube face are handled by rotation of AREA_0.
76
92f5a8d4
TL
77#ifndef BOOST_GEOMETRY_PROJECTIONS_QSC_HPP
78#define BOOST_GEOMETRY_PROJECTIONS_QSC_HPP
11fdf7f2
TL
79
80#include <boost/core/ignore_unused.hpp>
81#include <boost/geometry/util/math.hpp>
82
83#include <boost/geometry/srs/projections/impl/base_static.hpp>
84#include <boost/geometry/srs/projections/impl/base_dynamic.hpp>
85#include <boost/geometry/srs/projections/impl/projects.hpp>
86#include <boost/geometry/srs/projections/impl/factory_entry.hpp>
87
88namespace boost { namespace geometry
89{
90
11fdf7f2
TL
91namespace projections
92{
93 #ifndef DOXYGEN_NO_DETAIL
94 namespace detail { namespace qsc
95 {
92f5a8d4
TL
96
97 /* The six cube faces. */
98 enum face_type {
99 face_front = 0,
100 face_right = 1,
101 face_back = 2,
102 face_left = 3,
103 face_top = 4,
104 face_bottom = 5
105 };
11fdf7f2
TL
106
107 template <typename T>
108 struct par_qsc
109 {
92f5a8d4
TL
110 T a_squared;
111 T b;
112 T one_minus_f;
113 T one_minus_f_squared;
114 face_type face;
11fdf7f2
TL
115 };
116
92f5a8d4 117 static const double epsilon10 = 1.e-10;
11fdf7f2
TL
118
119 /* The four areas on a cube face. AREA_0 is the area of definition,
120 * the other three areas are counted counterclockwise. */
92f5a8d4
TL
121 enum area_type {
122 area_0 = 0,
123 area_1 = 1,
124 area_2 = 2,
125 area_3 = 3
126 };
11fdf7f2
TL
127
128 /* Helper function for forward projection: compute the theta angle
129 * and determine the area number. */
130 template <typename T>
92f5a8d4 131 inline T qsc_fwd_equat_face_theta(T const& phi, T const& y, T const& x, area_type *area)
11fdf7f2 132 {
92f5a8d4
TL
133 static const T fourth_pi = detail::fourth_pi<T>();
134 static const T half_pi = detail::half_pi<T>();
135 static const T pi = detail::pi<T>();
136
137 T theta;
138 if (phi < epsilon10) {
139 *area = area_0;
140 theta = 0.0;
141 } else {
142 theta = atan2(y, x);
143 if (fabs(theta) <= fourth_pi) {
144 *area = area_0;
145 } else if (theta > fourth_pi && theta <= half_pi + fourth_pi) {
146 *area = area_1;
147 theta -= half_pi;
148 } else if (theta > half_pi + fourth_pi || theta <= -(half_pi + fourth_pi)) {
149 *area = area_2;
150 theta = (theta >= 0.0 ? theta - pi : theta + pi);
11fdf7f2 151 } else {
92f5a8d4
TL
152 *area = area_3;
153 theta += half_pi;
11fdf7f2 154 }
92f5a8d4
TL
155 }
156 return theta;
11fdf7f2
TL
157 }
158
159 /* Helper function: shift the longitude. */
160 template <typename T>
161 inline T qsc_shift_lon_origin(T const& lon, T const& offset)
162 {
92f5a8d4
TL
163 static const T pi = detail::pi<T>();
164 static const T two_pi = detail::two_pi<T>();
11fdf7f2
TL
165
166 T slon = lon + offset;
92f5a8d4
TL
167 if (slon < -pi) {
168 slon += two_pi;
169 } else if (slon > +pi) {
170 slon -= two_pi;
11fdf7f2
TL
171 }
172 return slon;
173 }
174
175 /* Forward projection, ellipsoid */
176
92f5a8d4
TL
177 template <typename T, typename Parameters>
178 struct base_qsc_ellipsoid
11fdf7f2 179 {
92f5a8d4 180 par_qsc<T> m_proj_parm;
11fdf7f2
TL
181
182 // FORWARD(e_forward)
183 // Project coordinates from geographic (lon, lat) to cartesian (x, y)
92f5a8d4 184 inline void fwd(Parameters const& par, T const& lp_lon, T const& lp_lat, T& xy_x, T& xy_y) const
11fdf7f2 185 {
92f5a8d4
TL
186 static const T fourth_pi = detail::fourth_pi<T>();
187 static const T half_pi = detail::half_pi<T>();
188 static const T pi = detail::pi<T>();
189
190 T lat, lon;
191 T theta, phi;
192 T t, mu; /* nu; */
193 area_type area;
11fdf7f2
TL
194
195 /* Convert the geodetic latitude to a geocentric latitude.
196 * This corresponds to the shift from the ellipsoid to the sphere
197 * described in [LK12]. */
92f5a8d4 198 if (par.es != 0.0) {
11fdf7f2
TL
199 lat = atan(this->m_proj_parm.one_minus_f_squared * tan(lp_lat));
200 } else {
201 lat = lp_lat;
202 }
203
204 /* Convert the input lat, lon into theta, phi as used by QSC.
205 * This depends on the cube face and the area on it.
206 * For the top and bottom face, we can compute theta and phi
207 * directly from phi, lam. For the other faces, we must use
208 * unit sphere cartesian coordinates as an intermediate step. */
209 lon = lp_lon;
92f5a8d4
TL
210 if (this->m_proj_parm.face == face_top) {
211 phi = half_pi - lat;
212 if (lon >= fourth_pi && lon <= half_pi + fourth_pi) {
213 area = area_0;
214 theta = lon - half_pi;
215 } else if (lon > half_pi + fourth_pi || lon <= -(half_pi + fourth_pi)) {
216 area = area_1;
217 theta = (lon > 0.0 ? lon - pi : lon + pi);
218 } else if (lon > -(half_pi + fourth_pi) && lon <= -fourth_pi) {
219 area = area_2;
220 theta = lon + half_pi;
221 } else {
222 area = area_3;
223 theta = lon;
224 }
225 } else if (this->m_proj_parm.face == face_bottom) {
226 phi = half_pi + lat;
227 if (lon >= fourth_pi && lon <= half_pi + fourth_pi) {
228 area = area_0;
229 theta = -lon + half_pi;
230 } else if (lon < fourth_pi && lon >= -fourth_pi) {
231 area = area_1;
232 theta = -lon;
233 } else if (lon < -fourth_pi && lon >= -(half_pi + fourth_pi)) {
234 area = area_2;
235 theta = -lon - half_pi;
236 } else {
237 area = area_3;
238 theta = (lon > 0.0 ? -lon + pi : -lon - pi);
239 }
240 } else {
241 T q, r, s;
242 T sinlat, coslat;
243 T sinlon, coslon;
244
245 if (this->m_proj_parm.face == face_right) {
246 lon = qsc_shift_lon_origin(lon, +half_pi);
247 } else if (this->m_proj_parm.face == face_back) {
248 lon = qsc_shift_lon_origin(lon, +pi);
249 } else if (this->m_proj_parm.face == face_left) {
250 lon = qsc_shift_lon_origin(lon, -half_pi);
11fdf7f2
TL
251 }
252 sinlat = sin(lat);
253 coslat = cos(lat);
254 sinlon = sin(lon);
255 coslon = cos(lon);
256 q = coslat * coslon;
257 r = coslat * sinlon;
258 s = sinlat;
92f5a8d4
TL
259
260 if (this->m_proj_parm.face == face_front) {
261 phi = acos(q);
262 theta = qsc_fwd_equat_face_theta(phi, s, r, &area);
263 } else if (this->m_proj_parm.face == face_right) {
264 phi = acos(r);
265 theta = qsc_fwd_equat_face_theta(phi, s, -q, &area);
266 } else if (this->m_proj_parm.face == face_back) {
267 phi = acos(-q);
268 theta = qsc_fwd_equat_face_theta(phi, s, -r, &area);
269 } else if (this->m_proj_parm.face == face_left) {
270 phi = acos(-r);
271 theta = qsc_fwd_equat_face_theta(phi, s, q, &area);
11fdf7f2 272 } else {
92f5a8d4
TL
273 /* Impossible */
274 phi = theta = 0.0;
275 area = area_0;
11fdf7f2
TL
276 }
277 }
278
279 /* Compute mu and nu for the area of definition.
280 * For mu, see Eq. (3-21) in [OL76], but note the typos:
281 * compare with Eq. (3-14). For nu, see Eq. (3-38). */
92f5a8d4
TL
282 mu = atan((12.0 / pi) * (theta + acos(sin(theta) * cos(fourth_pi)) - half_pi));
283 // TODO: (cos(mu) * cos(mu)) could be replaced with sqr(cos(mu))
11fdf7f2
TL
284 t = sqrt((1.0 - cos(phi)) / (cos(mu) * cos(mu)) / (1.0 - cos(atan(1.0 / cos(theta)))));
285 /* nu = atan(t); We don't really need nu, just t, see below. */
286
287 /* Apply the result to the real area. */
92f5a8d4
TL
288 if (area == area_1) {
289 mu += half_pi;
290 } else if (area == area_2) {
291 mu += pi;
292 } else if (area == area_3) {
293 mu += half_pi + pi;
11fdf7f2
TL
294 }
295
296 /* Now compute x, y from mu and nu */
297 /* t = tan(nu); */
298 xy_x = t * cos(mu);
299 xy_y = t * sin(mu);
11fdf7f2
TL
300 }
301 /* Inverse projection, ellipsoid */
302
303 // INVERSE(e_inverse)
304 // Project coordinates from cartesian (x, y) to geographic (lon, lat)
92f5a8d4 305 inline void inv(Parameters const& par, T const& xy_x, T const& xy_y, T& lp_lon, T& lp_lat) const
11fdf7f2 306 {
92f5a8d4
TL
307 static const T half_pi = detail::half_pi<T>();
308 static const T pi = detail::pi<T>();
11fdf7f2 309
92f5a8d4
TL
310 T mu, nu, cosmu, tannu;
311 T tantheta, theta, cosphi, phi;
312 T t;
11fdf7f2
TL
313 int area;
314
315 /* Convert the input x, y to the mu and nu angles as used by QSC.
316 * This depends on the area of the cube face. */
317 nu = atan(sqrt(xy_x * xy_x + xy_y * xy_y));
318 mu = atan2(xy_y, xy_x);
319 if (xy_x >= 0.0 && xy_x >= fabs(xy_y)) {
92f5a8d4 320 area = area_0;
11fdf7f2 321 } else if (xy_y >= 0.0 && xy_y >= fabs(xy_x)) {
92f5a8d4
TL
322 area = area_1;
323 mu -= half_pi;
11fdf7f2 324 } else if (xy_x < 0.0 && -xy_x >= fabs(xy_y)) {
92f5a8d4
TL
325 area = area_2;
326 mu = (mu < 0.0 ? mu + pi : mu - pi);
11fdf7f2 327 } else {
92f5a8d4
TL
328 area = area_3;
329 mu += half_pi;
11fdf7f2
TL
330 }
331
332 /* Compute phi and theta for the area of definition.
333 * The inverse projection is not described in the original paper, but some
334 * good hints can be found here (as of 2011-12-14):
335 * http://fits.gsfc.nasa.gov/fitsbits/saf.93/saf.9302
336 * (search for "Message-Id: <9302181759.AA25477 at fits.cv.nrao.edu>") */
92f5a8d4 337 t = (pi / 12.0) * tan(mu);
11fdf7f2
TL
338 tantheta = sin(t) / (cos(t) - (1.0 / sqrt(2.0)));
339 theta = atan(tantheta);
340 cosmu = cos(mu);
341 tannu = tan(nu);
342 cosphi = 1.0 - cosmu * cosmu * tannu * tannu * (1.0 - cos(atan(1.0 / cos(theta))));
343 if (cosphi < -1.0) {
344 cosphi = -1.0;
345 } else if (cosphi > +1.0) {
346 cosphi = +1.0;
347 }
348
349 /* Apply the result to the real area on the cube face.
350 * For the top and bottom face, we can compute phi and lam directly.
351 * For the other faces, we must use unit sphere cartesian coordinates
352 * as an intermediate step. */
92f5a8d4 353 if (this->m_proj_parm.face == face_top) {
11fdf7f2 354 phi = acos(cosphi);
92f5a8d4
TL
355 lp_lat = half_pi - phi;
356 if (area == area_0) {
357 lp_lon = theta + half_pi;
358 } else if (area == area_1) {
359 lp_lon = (theta < 0.0 ? theta + pi : theta - pi);
360 } else if (area == area_2) {
361 lp_lon = theta - half_pi;
11fdf7f2
TL
362 } else /* area == AREA_3 */ {
363 lp_lon = theta;
364 }
92f5a8d4 365 } else if (this->m_proj_parm.face == face_bottom) {
11fdf7f2 366 phi = acos(cosphi);
92f5a8d4
TL
367 lp_lat = phi - half_pi;
368 if (area == area_0) {
369 lp_lon = -theta + half_pi;
370 } else if (area == area_1) {
11fdf7f2 371 lp_lon = -theta;
92f5a8d4
TL
372 } else if (area == area_2) {
373 lp_lon = -theta - half_pi;
374 } else /* area == area_3 */ {
375 lp_lon = (theta < 0.0 ? -theta - pi : -theta + pi);
11fdf7f2
TL
376 }
377 } else {
378 /* Compute phi and lam via cartesian unit sphere coordinates. */
92f5a8d4 379 T q, r, s;
11fdf7f2
TL
380 q = cosphi;
381 t = q * q;
382 if (t >= 1.0) {
383 s = 0.0;
384 } else {
385 s = sqrt(1.0 - t) * sin(theta);
386 }
387 t += s * s;
388 if (t >= 1.0) {
389 r = 0.0;
390 } else {
391 r = sqrt(1.0 - t);
392 }
393 /* Rotate q,r,s into the correct area. */
92f5a8d4 394 if (area == area_1) {
11fdf7f2
TL
395 t = r;
396 r = -s;
397 s = t;
92f5a8d4 398 } else if (area == area_2) {
11fdf7f2
TL
399 r = -r;
400 s = -s;
92f5a8d4 401 } else if (area == area_3) {
11fdf7f2
TL
402 t = r;
403 r = s;
404 s = -t;
405 }
406 /* Rotate q,r,s into the correct cube face. */
92f5a8d4 407 if (this->m_proj_parm.face == face_right) {
11fdf7f2
TL
408 t = q;
409 q = -r;
410 r = t;
92f5a8d4 411 } else if (this->m_proj_parm.face == face_back) {
11fdf7f2
TL
412 q = -q;
413 r = -r;
92f5a8d4 414 } else if (this->m_proj_parm.face == face_left) {
11fdf7f2
TL
415 t = q;
416 q = r;
417 r = -t;
418 }
419 /* Now compute phi and lam from the unit sphere coordinates. */
92f5a8d4 420 lp_lat = acos(-s) - half_pi;
11fdf7f2 421 lp_lon = atan2(r, q);
92f5a8d4
TL
422 if (this->m_proj_parm.face == face_right) {
423 lp_lon = qsc_shift_lon_origin(lp_lon, -half_pi);
424 } else if (this->m_proj_parm.face == face_back) {
425 lp_lon = qsc_shift_lon_origin(lp_lon, -pi);
426 } else if (this->m_proj_parm.face == face_left) {
427 lp_lon = qsc_shift_lon_origin(lp_lon, +half_pi);
11fdf7f2
TL
428 }
429 }
430
431 /* Apply the shift from the sphere to the ellipsoid as described
432 * in [LK12]. */
92f5a8d4 433 if (par.es != 0.0) {
11fdf7f2 434 int invert_sign;
92f5a8d4 435 T tanphi, xa;
11fdf7f2
TL
436 invert_sign = (lp_lat < 0.0 ? 1 : 0);
437 tanphi = tan(lp_lat);
438 xa = this->m_proj_parm.b / sqrt(tanphi * tanphi + this->m_proj_parm.one_minus_f_squared);
92f5a8d4 439 lp_lat = atan(sqrt(par.a * par.a - xa * xa) / (this->m_proj_parm.one_minus_f * xa));
11fdf7f2
TL
440 if (invert_sign) {
441 lp_lat = -lp_lat;
442 }
443 }
444 }
445
446 static inline std::string get_name()
447 {
448 return "qsc_ellipsoid";
449 }
450
451 };
452
453 // Quadrilateralized Spherical Cube
454 template <typename Parameters, typename T>
92f5a8d4 455 inline void setup_qsc(Parameters const& par, par_qsc<T>& proj_parm)
11fdf7f2 456 {
92f5a8d4
TL
457 static const T fourth_pi = detail::fourth_pi<T>();
458 static const T half_pi = detail::half_pi<T>();
459
460 /* Determine the cube face from the center of projection. */
461 if (par.phi0 >= half_pi - fourth_pi / 2.0) {
462 proj_parm.face = face_top;
463 } else if (par.phi0 <= -(half_pi - fourth_pi / 2.0)) {
464 proj_parm.face = face_bottom;
465 } else if (fabs(par.lam0) <= fourth_pi) {
466 proj_parm.face = face_front;
467 } else if (fabs(par.lam0) <= half_pi + fourth_pi) {
468 proj_parm.face = (par.lam0 > 0.0 ? face_right : face_left);
469 } else {
470 proj_parm.face = face_back;
471 }
472 /* Fill in useful values for the ellipsoid <-> sphere shift
473 * described in [LK12]. */
474 if (par.es != 0.0) {
475 proj_parm.a_squared = par.a * par.a;
476 proj_parm.b = par.a * sqrt(1.0 - par.es);
477 proj_parm.one_minus_f = 1.0 - (par.a - proj_parm.b) / par.a;
478 proj_parm.one_minus_f_squared = proj_parm.one_minus_f * proj_parm.one_minus_f;
479 }
11fdf7f2
TL
480 }
481
482 }} // namespace detail::qsc
483 #endif // doxygen
484
485 /*!
486 \brief Quadrilateralized Spherical Cube projection
487 \ingroup projections
488 \tparam Geographic latlong point type
489 \tparam Cartesian xy point type
490 \tparam Parameters parameter type
491 \par Projection characteristics
492 - Azimuthal
493 - Spheroid
494 \par Example
495 \image html ex_qsc.gif
496 */
92f5a8d4
TL
497 template <typename T, typename Parameters>
498 struct qsc_ellipsoid : public detail::qsc::base_qsc_ellipsoid<T, Parameters>
11fdf7f2 499 {
92f5a8d4
TL
500 template <typename Params>
501 inline qsc_ellipsoid(Params const& , Parameters const& par)
11fdf7f2 502 {
92f5a8d4 503 detail::qsc::setup_qsc(par, this->m_proj_parm);
11fdf7f2
TL
504 }
505 };
506
507 #ifndef DOXYGEN_NO_DETAIL
508 namespace detail
509 {
510
511 // Static projection
92f5a8d4 512 BOOST_GEOMETRY_PROJECTIONS_DETAIL_STATIC_PROJECTION_FI(srs::spar::proj_qsc, qsc_ellipsoid)
11fdf7f2
TL
513
514 // Factory entry(s)
92f5a8d4 515 BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_ENTRY_FI(qsc_entry, qsc_ellipsoid)
11fdf7f2 516
92f5a8d4 517 BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_BEGIN(qsc_init)
11fdf7f2 518 {
92f5a8d4 519 BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_ENTRY(qsc, qsc_entry)
11fdf7f2
TL
520 }
521
522 } // namespace detail
523 #endif // doxygen
524
525} // namespace projections
526
527}} // namespace boost::geometry
528
529#endif // BOOST_GEOMETRY_PROJECTIONS_QSC_HPP
530