1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2015-2017, Oracle and/or its affiliates.
5 // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
6 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
8 // Licensed under the Boost Software License version 1.0.
9 // http://www.boost.org/users/license.html
11 #ifndef BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP
12 #define BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP
14 #include <boost/geometry/core/assert.hpp>
15 #include <boost/geometry/core/cs.hpp>
16 #include <boost/geometry/util/math.hpp>
19 namespace boost { namespace geometry
25 #ifndef DOXYGEN_NO_DETAIL
29 // CoordinateType, radian, true
30 template <typename CoordinateType, typename Units, bool IsEquatorial = true>
31 struct constants_on_spheroid
33 static inline CoordinateType period()
35 return math::two_pi<CoordinateType>();
38 static inline CoordinateType half_period()
40 return math::pi<CoordinateType>();
43 static inline CoordinateType quarter_period()
45 static CoordinateType const
46 pi_half = math::pi<CoordinateType>() / CoordinateType(2);
50 static inline CoordinateType min_longitude()
52 static CoordinateType const minus_pi = -math::pi<CoordinateType>();
56 static inline CoordinateType max_longitude()
58 return math::pi<CoordinateType>();
61 static inline CoordinateType min_latitude()
63 static CoordinateType const minus_half_pi
64 = -math::half_pi<CoordinateType>();
68 static inline CoordinateType max_latitude()
70 return math::half_pi<CoordinateType>();
74 template <typename CoordinateType>
75 struct constants_on_spheroid<CoordinateType, radian, false>
76 : constants_on_spheroid<CoordinateType, radian, true>
78 static inline CoordinateType min_latitude()
80 return CoordinateType(0);
83 static inline CoordinateType max_latitude()
85 return math::pi<CoordinateType>();
89 template <typename CoordinateType>
90 struct constants_on_spheroid<CoordinateType, degree, true>
92 static inline CoordinateType period()
94 return CoordinateType(360.0);
97 static inline CoordinateType half_period()
99 return CoordinateType(180.0);
102 static inline CoordinateType quarter_period()
104 return CoordinateType(90.0);
107 static inline CoordinateType min_longitude()
109 return CoordinateType(-180.0);
112 static inline CoordinateType max_longitude()
114 return CoordinateType(180.0);
117 static inline CoordinateType min_latitude()
119 return CoordinateType(-90.0);
122 static inline CoordinateType max_latitude()
124 return CoordinateType(90.0);
128 template <typename CoordinateType>
129 struct constants_on_spheroid<CoordinateType, degree, false>
130 : constants_on_spheroid<CoordinateType, degree, true>
132 static inline CoordinateType min_latitude()
134 return CoordinateType(0);
137 static inline CoordinateType max_latitude()
139 return CoordinateType(180.0);
144 } // namespace detail
145 #endif // DOXYGEN_NO_DETAIL
148 template <typename Units, typename CoordinateType>
149 inline CoordinateType latitude_convert_ep(CoordinateType const& lat)
151 typedef math::detail::constants_on_spheroid
157 return constants::quarter_period() - lat;
161 template <typename Units, bool IsEquatorial, typename T>
162 static bool is_latitude_pole(T const& lat)
164 typedef math::detail::constants_on_spheroid
170 return math::equals(math::abs(IsEquatorial
172 : math::latitude_convert_ep<Units>(lat)),
173 constants::quarter_period());
178 template <typename Units, typename T>
179 static bool is_longitude_antimeridian(T const& lon)
181 typedef math::detail::constants_on_spheroid
187 return math::equals(math::abs(lon), constants::half_period());
192 #ifndef DOXYGEN_NO_DETAIL
197 template <typename Units, bool IsEquatorial>
198 struct latitude_convert_if_polar
200 template <typename T>
201 static inline void apply(T & lat) {}
204 template <typename Units>
205 struct latitude_convert_if_polar<Units, false>
207 template <typename T>
208 static inline void apply(T & lat)
210 lat = latitude_convert_ep<Units>(lat);
215 template <typename Units, typename CoordinateType, bool IsEquatorial = true>
216 class normalize_spheroidal_coordinates
218 typedef constants_on_spheroid<CoordinateType, Units> constants;
221 static inline CoordinateType normalize_up(CoordinateType const& value)
224 math::mod(value + constants::half_period(), constants::period())
225 - constants::half_period();
228 static inline CoordinateType normalize_down(CoordinateType const& value)
231 math::mod(value - constants::half_period(), constants::period())
232 + constants::half_period();
236 static inline void apply(CoordinateType& longitude)
238 // normalize longitude
239 if (math::equals(math::abs(longitude), constants::half_period()))
241 longitude = constants::half_period();
243 else if (longitude > constants::half_period())
245 longitude = normalize_up(longitude);
246 if (math::equals(longitude, -constants::half_period()))
248 longitude = constants::half_period();
251 else if (longitude < -constants::half_period())
253 longitude = normalize_down(longitude);
257 static inline void apply(CoordinateType& longitude,
258 CoordinateType& latitude,
259 bool normalize_poles = true)
261 latitude_convert_if_polar<Units, IsEquatorial>::apply(latitude);
263 #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE
264 // normalize latitude
265 if (math::larger(latitude, constants::half_period()))
267 latitude = normalize_up(latitude);
269 else if (math::smaller(latitude, -constants::half_period()))
271 latitude = normalize_down(latitude);
274 // fix latitude range
275 if (latitude < constants::min_latitude())
277 latitude = -constants::half_period() - latitude;
278 longitude -= constants::half_period();
280 else if (latitude > constants::max_latitude())
282 latitude = constants::half_period() - latitude;
283 longitude -= constants::half_period();
285 #endif // BOOST_GEOMETRY_NORMALIZE_LATITUDE
287 // normalize longitude
290 // finally normalize poles
293 if (math::equals(math::abs(latitude), constants::max_latitude()))
295 // for the north and south pole we set the longitude to 0
296 // (works for both radians and degrees)
297 longitude = CoordinateType(0);
301 latitude_convert_if_polar<Units, IsEquatorial>::apply(latitude);
303 #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE
304 BOOST_GEOMETRY_ASSERT(! math::larger(constants::min_latitude(), latitude));
305 BOOST_GEOMETRY_ASSERT(! math::larger(latitude, constants::max_latitude()));
306 #endif // BOOST_GEOMETRY_NORMALIZE_LATITUDE
308 BOOST_GEOMETRY_ASSERT(math::smaller(constants::min_longitude(), longitude));
309 BOOST_GEOMETRY_ASSERT(! math::larger(longitude, constants::max_longitude()));
314 } // namespace detail
315 #endif // DOXYGEN_NO_DETAIL
319 \brief Short utility to normalize the coordinates on a spheroid
320 \tparam Units The units of the coordindate system in the spheroid
321 \tparam CoordinateType The type of the coordinates
322 \param longitude Longitude
323 \param latitude Latitude
326 template <typename Units, typename CoordinateType>
327 inline void normalize_spheroidal_coordinates(CoordinateType& longitude,
328 CoordinateType& latitude)
330 detail::normalize_spheroidal_coordinates
332 Units, CoordinateType
333 >::apply(longitude, latitude);
336 template <typename Units, bool IsEquatorial, typename CoordinateType>
337 inline void normalize_spheroidal_coordinates(CoordinateType& longitude,
338 CoordinateType& latitude)
340 detail::normalize_spheroidal_coordinates
342 Units, CoordinateType, IsEquatorial
343 >::apply(longitude, latitude);
347 \brief Short utility to normalize the longitude on a spheroid.
348 Note that in general both coordinates should be normalized at once.
349 This utility is suitable e.g. for normalization of the difference of longitudes.
350 \tparam Units The units of the coordindate system in the spheroid
351 \tparam CoordinateType The type of the coordinates
352 \param longitude Longitude
355 template <typename Units, typename CoordinateType>
356 inline void normalize_longitude(CoordinateType& longitude)
358 detail::normalize_spheroidal_coordinates
360 Units, CoordinateType
366 \brief Short utility to calculate difference between two longitudes
367 normalized in range (-180, 180].
368 \tparam Units The units of the coordindate system in the spheroid
369 \tparam CoordinateType The type of the coordinates
370 \param longitude1 Longitude 1
371 \param longitude2 Longitude 2
374 template <typename Units, typename CoordinateType>
375 inline CoordinateType longitude_distance_signed(CoordinateType const& longitude1,
376 CoordinateType const& longitude2)
378 CoordinateType diff = longitude2 - longitude1;
379 math::normalize_longitude<Units, CoordinateType>(diff);
385 \brief Short utility to calculate difference between two longitudes
386 normalized in range [0, 360).
387 \tparam Units The units of the coordindate system in the spheroid
388 \tparam CoordinateType The type of the coordinates
389 \param longitude1 Longitude 1
390 \param longitude2 Longitude 2
393 template <typename Units, typename CoordinateType>
394 inline CoordinateType longitude_distance_unsigned(CoordinateType const& longitude1,
395 CoordinateType const& longitude2)
397 typedef math::detail::constants_on_spheroid
399 CoordinateType, Units
402 CoordinateType const c0 = 0;
403 CoordinateType diff = longitude_distance_signed<Units>(longitude1, longitude2);
404 if (diff < c0) // (-180, 180] -> [0, 360)
406 diff += constants::period();
412 \brief The abs difference between longitudes in range [0, 180].
413 \tparam Units The units of the coordindate system in the spheroid
414 \tparam CoordinateType The type of the coordinates
415 \param longitude1 Longitude 1
416 \param longitude2 Longitude 2
419 template <typename Units, typename CoordinateType>
420 inline CoordinateType longitude_difference(CoordinateType const& longitude1,
421 CoordinateType const& longitude2)
423 return math::abs(math::longitude_distance_signed<Units>(longitude1, longitude2));
426 template <typename Units, typename CoordinateType>
427 inline CoordinateType longitude_interval_distance_signed(CoordinateType const& longitude_a1,
428 CoordinateType const& longitude_a2,
429 CoordinateType const& longitude_b)
431 CoordinateType const c0 = 0;
432 CoordinateType dist_a12 = longitude_distance_signed<Units>(longitude_a1, longitude_a2);
433 CoordinateType dist_a1b = longitude_distance_signed<Units>(longitude_a1, longitude_b);
436 dist_a12 = -dist_a12;
437 dist_a1b = -dist_a1b;
440 return dist_a1b < c0 ? dist_a1b
441 : dist_a1b > dist_a12 ? dist_a1b - dist_a12
449 }} // namespace boost::geometry
451 #endif // BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP