1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
5 // Copyright (c) 2015-2020, Oracle and/or its affiliates.
7 // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
8 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
9 // Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program
11 // Licensed under the Boost Software License version 1.0.
12 // http://www.boost.org/users/license.html
14 #ifndef BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP
15 #define BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP
17 #include <boost/geometry/core/assert.hpp>
18 #include <boost/geometry/core/cs.hpp>
19 #include <boost/geometry/util/math.hpp>
22 namespace boost { namespace geometry
28 #ifndef DOXYGEN_NO_DETAIL
32 // CoordinateType, radian, true
33 template <typename CoordinateType, typename Units, bool IsEquatorial = true>
34 struct constants_on_spheroid
36 static inline CoordinateType period()
38 return math::two_pi<CoordinateType>();
41 static inline CoordinateType half_period()
43 return math::pi<CoordinateType>();
46 static inline CoordinateType quarter_period()
48 static CoordinateType const
49 pi_half = math::pi<CoordinateType>() / CoordinateType(2);
53 static inline CoordinateType min_longitude()
55 static CoordinateType const minus_pi = -math::pi<CoordinateType>();
59 static inline CoordinateType max_longitude()
61 return math::pi<CoordinateType>();
64 static inline CoordinateType min_latitude()
66 static CoordinateType const minus_half_pi
67 = -math::half_pi<CoordinateType>();
71 static inline CoordinateType max_latitude()
73 return math::half_pi<CoordinateType>();
77 template <typename CoordinateType>
78 struct constants_on_spheroid<CoordinateType, radian, false>
79 : constants_on_spheroid<CoordinateType, radian, true>
81 static inline CoordinateType min_latitude()
83 return CoordinateType(0);
86 static inline CoordinateType max_latitude()
88 return math::pi<CoordinateType>();
92 template <typename CoordinateType>
93 struct constants_on_spheroid<CoordinateType, degree, true>
95 static inline CoordinateType period()
97 return CoordinateType(360.0);
100 static inline CoordinateType half_period()
102 return CoordinateType(180.0);
105 static inline CoordinateType quarter_period()
107 return CoordinateType(90.0);
110 static inline CoordinateType min_longitude()
112 return CoordinateType(-180.0);
115 static inline CoordinateType max_longitude()
117 return CoordinateType(180.0);
120 static inline CoordinateType min_latitude()
122 return CoordinateType(-90.0);
125 static inline CoordinateType max_latitude()
127 return CoordinateType(90.0);
131 template <typename CoordinateType>
132 struct constants_on_spheroid<CoordinateType, degree, false>
133 : constants_on_spheroid<CoordinateType, degree, true>
135 static inline CoordinateType min_latitude()
137 return CoordinateType(0);
140 static inline CoordinateType max_latitude()
142 return CoordinateType(180.0);
147 } // namespace detail
148 #endif // DOXYGEN_NO_DETAIL
151 template <typename Units, typename CoordinateType>
152 inline CoordinateType latitude_convert_ep(CoordinateType const& lat)
154 typedef math::detail::constants_on_spheroid
160 return constants::quarter_period() - lat;
164 template <typename Units, bool IsEquatorial, typename T>
165 static bool is_latitude_pole(T const& lat)
167 typedef math::detail::constants_on_spheroid
173 return math::equals(math::abs(IsEquatorial
175 : math::latitude_convert_ep<Units>(lat)),
176 constants::quarter_period());
181 template <typename Units, typename T>
182 static bool is_longitude_antimeridian(T const& lon)
184 typedef math::detail::constants_on_spheroid
190 return math::equals(math::abs(lon), constants::half_period());
195 #ifndef DOXYGEN_NO_DETAIL
200 template <typename Units, bool IsEquatorial>
201 struct latitude_convert_if_polar
203 template <typename T>
204 static inline void apply(T & /*lat*/) {}
207 template <typename Units>
208 struct latitude_convert_if_polar<Units, false>
210 template <typename T>
211 static inline void apply(T & lat)
213 lat = latitude_convert_ep<Units>(lat);
218 template <typename Units, typename CoordinateType, bool IsEquatorial = true>
219 class normalize_spheroidal_coordinates
221 typedef constants_on_spheroid<CoordinateType, Units> constants;
224 static inline CoordinateType normalize_up(CoordinateType const& value)
227 math::mod(value + constants::half_period(), constants::period())
228 - constants::half_period();
231 static inline CoordinateType normalize_down(CoordinateType const& value)
234 math::mod(value - constants::half_period(), constants::period())
235 + constants::half_period();
239 static inline void apply(CoordinateType& longitude)
241 // normalize longitude
242 if (math::equals(math::abs(longitude), constants::half_period()))
244 longitude = constants::half_period();
246 else if (longitude > constants::half_period())
248 longitude = normalize_up(longitude);
249 if (math::equals(longitude, -constants::half_period()))
251 longitude = constants::half_period();
254 else if (longitude < -constants::half_period())
256 longitude = normalize_down(longitude);
260 static inline void apply(CoordinateType& longitude,
261 CoordinateType& latitude,
262 bool normalize_poles = true)
264 latitude_convert_if_polar<Units, IsEquatorial>::apply(latitude);
266 #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE
267 // normalize latitude
268 if (math::larger(latitude, constants::half_period()))
270 latitude = normalize_up(latitude);
272 else if (math::smaller(latitude, -constants::half_period()))
274 latitude = normalize_down(latitude);
277 // fix latitude range
278 if (latitude < constants::min_latitude())
280 latitude = -constants::half_period() - latitude;
281 longitude -= constants::half_period();
283 else if (latitude > constants::max_latitude())
285 latitude = constants::half_period() - latitude;
286 longitude -= constants::half_period();
288 #endif // BOOST_GEOMETRY_NORMALIZE_LATITUDE
290 // normalize longitude
293 // finally normalize poles
296 if (math::equals(math::abs(latitude), constants::max_latitude()))
298 // for the north and south pole we set the longitude to 0
299 // (works for both radians and degrees)
300 longitude = CoordinateType(0);
304 latitude_convert_if_polar<Units, IsEquatorial>::apply(latitude);
306 #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE
307 BOOST_GEOMETRY_ASSERT(! math::larger(constants::min_latitude(), latitude));
308 BOOST_GEOMETRY_ASSERT(! math::larger(latitude, constants::max_latitude()));
309 #endif // BOOST_GEOMETRY_NORMALIZE_LATITUDE
311 BOOST_GEOMETRY_ASSERT(math::smaller(constants::min_longitude(), longitude));
312 BOOST_GEOMETRY_ASSERT(! math::larger(longitude, constants::max_longitude()));
317 template <typename Units, typename CoordinateType>
318 inline void normalize_angle_loop(CoordinateType& angle)
320 typedef constants_on_spheroid<CoordinateType, Units> constants;
321 CoordinateType const pi = constants::half_period();
322 CoordinateType const two_pi = constants::period();
329 template <typename Units, typename CoordinateType>
330 inline void normalize_angle_cond(CoordinateType& angle)
332 typedef constants_on_spheroid<CoordinateType, Units> constants;
333 CoordinateType const pi = constants::half_period();
334 CoordinateType const two_pi = constants::period();
337 else if (angle <= -pi)
342 } // namespace detail
343 #endif // DOXYGEN_NO_DETAIL
347 \brief Short utility to normalize the coordinates on a spheroid
348 \tparam Units The units of the coordindate system in the spheroid
349 \tparam CoordinateType The type of the coordinates
350 \param longitude Longitude
351 \param latitude Latitude
354 template <typename Units, typename CoordinateType>
355 inline void normalize_spheroidal_coordinates(CoordinateType& longitude,
356 CoordinateType& latitude)
358 detail::normalize_spheroidal_coordinates
360 Units, CoordinateType
361 >::apply(longitude, latitude);
364 template <typename Units, bool IsEquatorial, typename CoordinateType>
365 inline void normalize_spheroidal_coordinates(CoordinateType& longitude,
366 CoordinateType& latitude)
368 detail::normalize_spheroidal_coordinates
370 Units, CoordinateType, IsEquatorial
371 >::apply(longitude, latitude);
375 \brief Short utility to normalize the longitude on a spheroid.
376 Note that in general both coordinates should be normalized at once.
377 This utility is suitable e.g. for normalization of the difference of longitudes.
378 \tparam Units The units of the coordindate system in the spheroid
379 \tparam CoordinateType The type of the coordinates
380 \param longitude Longitude
383 template <typename Units, typename CoordinateType>
384 inline void normalize_longitude(CoordinateType& longitude)
386 detail::normalize_spheroidal_coordinates
388 Units, CoordinateType
393 \brief Short utility to normalize the azimuth on a spheroid
394 in the range (-180, 180].
395 \tparam Units The units of the coordindate system in the spheroid
396 \tparam CoordinateType The type of the coordinates
400 template <typename Units, typename CoordinateType>
401 inline void normalize_azimuth(CoordinateType& angle)
403 normalize_longitude<Units, CoordinateType>(angle);
407 \brief Normalize the given values.
408 \tparam ValueType The type of the values
411 TODO: adl1995 - Merge this function with
412 formulas/vertex_longitude.hpp
414 template<typename ValueType>
415 inline void normalize_unit_vector(ValueType& x, ValueType& y)
417 ValueType h = boost::math::hypot(x, y);
419 BOOST_GEOMETRY_ASSERT(h > 0);
425 \brief Short utility to calculate difference between two longitudes
426 normalized in range (-180, 180].
427 \tparam Units The units of the coordindate system in the spheroid
428 \tparam CoordinateType The type of the coordinates
429 \param longitude1 Longitude 1
430 \param longitude2 Longitude 2
433 template <typename Units, typename CoordinateType>
434 inline CoordinateType longitude_distance_signed(CoordinateType const& longitude1,
435 CoordinateType const& longitude2)
437 CoordinateType diff = longitude2 - longitude1;
438 math::normalize_longitude<Units, CoordinateType>(diff);
444 \brief Short utility to calculate difference between two longitudes
445 normalized in range [0, 360).
446 \tparam Units The units of the coordindate system in the spheroid
447 \tparam CoordinateType The type of the coordinates
448 \param longitude1 Longitude 1
449 \param longitude2 Longitude 2
452 template <typename Units, typename CoordinateType>
453 inline CoordinateType longitude_distance_unsigned(CoordinateType const& longitude1,
454 CoordinateType const& longitude2)
456 typedef math::detail::constants_on_spheroid
458 CoordinateType, Units
461 CoordinateType const c0 = 0;
462 CoordinateType diff = longitude_distance_signed<Units>(longitude1, longitude2);
463 if (diff < c0) // (-180, 180] -> [0, 360)
465 diff += constants::period();
471 \brief The abs difference between longitudes in range [0, 180].
472 \tparam Units The units of the coordindate system in the spheroid
473 \tparam CoordinateType The type of the coordinates
474 \param longitude1 Longitude 1
475 \param longitude2 Longitude 2
478 template <typename Units, typename CoordinateType>
479 inline CoordinateType longitude_difference(CoordinateType const& longitude1,
480 CoordinateType const& longitude2)
482 return math::abs(math::longitude_distance_signed<Units>(longitude1, longitude2));
485 template <typename Units, typename CoordinateType>
486 inline CoordinateType longitude_interval_distance_signed(CoordinateType const& longitude_a1,
487 CoordinateType const& longitude_a2,
488 CoordinateType const& longitude_b)
490 CoordinateType const c0 = 0;
491 CoordinateType dist_a12 = longitude_distance_signed<Units>(longitude_a1, longitude_a2);
492 CoordinateType dist_a1b = longitude_distance_signed<Units>(longitude_a1, longitude_b);
495 dist_a12 = -dist_a12;
496 dist_a1b = -dist_a1b;
499 return dist_a1b < c0 ? dist_a1b
500 : dist_a1b > dist_a12 ? dist_a1b - dist_a12
508 }} // namespace boost::geometry
510 #endif // BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP