1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
5 // Copyright (c) 2015-2019, 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 } // namespace detail
318 #endif // DOXYGEN_NO_DETAIL
322 \brief Short utility to normalize the coordinates on a spheroid
323 \tparam Units The units of the coordindate system in the spheroid
324 \tparam CoordinateType The type of the coordinates
325 \param longitude Longitude
326 \param latitude Latitude
329 template <typename Units, typename CoordinateType>
330 inline void normalize_spheroidal_coordinates(CoordinateType& longitude,
331 CoordinateType& latitude)
333 detail::normalize_spheroidal_coordinates
335 Units, CoordinateType
336 >::apply(longitude, latitude);
339 template <typename Units, bool IsEquatorial, typename CoordinateType>
340 inline void normalize_spheroidal_coordinates(CoordinateType& longitude,
341 CoordinateType& latitude)
343 detail::normalize_spheroidal_coordinates
345 Units, CoordinateType, IsEquatorial
346 >::apply(longitude, latitude);
350 \brief Short utility to normalize the longitude on a spheroid.
351 Note that in general both coordinates should be normalized at once.
352 This utility is suitable e.g. for normalization of the difference of longitudes.
353 \tparam Units The units of the coordindate system in the spheroid
354 \tparam CoordinateType The type of the coordinates
355 \param longitude Longitude
358 template <typename Units, typename CoordinateType>
359 inline void normalize_longitude(CoordinateType& longitude)
361 detail::normalize_spheroidal_coordinates
363 Units, CoordinateType
368 \brief Short utility to normalize the azimuth on a spheroid
369 in the range (-180, 180].
370 \tparam Units The units of the coordindate system in the spheroid
371 \tparam CoordinateType The type of the coordinates
375 template <typename Units, typename CoordinateType>
376 inline void normalize_azimuth(CoordinateType& angle)
378 normalize_longitude<Units, CoordinateType>(angle);
382 \brief Normalize the given values.
383 \tparam ValueType The type of the values
386 TODO: adl1995 - Merge this function with
387 formulas/vertex_longitude.hpp
389 template<typename ValueType>
390 inline void normalize_unit_vector(ValueType& x, ValueType& y)
392 ValueType h = boost::math::hypot(x, y);
394 BOOST_GEOMETRY_ASSERT(h > 0);
400 \brief Short utility to calculate difference between two longitudes
401 normalized in range (-180, 180].
402 \tparam Units The units of the coordindate system in the spheroid
403 \tparam CoordinateType The type of the coordinates
404 \param longitude1 Longitude 1
405 \param longitude2 Longitude 2
408 template <typename Units, typename CoordinateType>
409 inline CoordinateType longitude_distance_signed(CoordinateType const& longitude1,
410 CoordinateType const& longitude2)
412 CoordinateType diff = longitude2 - longitude1;
413 math::normalize_longitude<Units, CoordinateType>(diff);
419 \brief Short utility to calculate difference between two longitudes
420 normalized in range [0, 360).
421 \tparam Units The units of the coordindate system in the spheroid
422 \tparam CoordinateType The type of the coordinates
423 \param longitude1 Longitude 1
424 \param longitude2 Longitude 2
427 template <typename Units, typename CoordinateType>
428 inline CoordinateType longitude_distance_unsigned(CoordinateType const& longitude1,
429 CoordinateType const& longitude2)
431 typedef math::detail::constants_on_spheroid
433 CoordinateType, Units
436 CoordinateType const c0 = 0;
437 CoordinateType diff = longitude_distance_signed<Units>(longitude1, longitude2);
438 if (diff < c0) // (-180, 180] -> [0, 360)
440 diff += constants::period();
446 \brief The abs difference between longitudes in range [0, 180].
447 \tparam Units The units of the coordindate system in the spheroid
448 \tparam CoordinateType The type of the coordinates
449 \param longitude1 Longitude 1
450 \param longitude2 Longitude 2
453 template <typename Units, typename CoordinateType>
454 inline CoordinateType longitude_difference(CoordinateType const& longitude1,
455 CoordinateType const& longitude2)
457 return math::abs(math::longitude_distance_signed<Units>(longitude1, longitude2));
460 template <typename Units, typename CoordinateType>
461 inline CoordinateType longitude_interval_distance_signed(CoordinateType const& longitude_a1,
462 CoordinateType const& longitude_a2,
463 CoordinateType const& longitude_b)
465 CoordinateType const c0 = 0;
466 CoordinateType dist_a12 = longitude_distance_signed<Units>(longitude_a1, longitude_a2);
467 CoordinateType dist_a1b = longitude_distance_signed<Units>(longitude_a1, longitude_b);
470 dist_a12 = -dist_a12;
471 dist_a1b = -dist_a1b;
474 return dist_a1b < c0 ? dist_a1b
475 : dist_a1b > dist_a12 ? dist_a1b - dist_a12
483 }} // namespace boost::geometry
485 #endif // BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP