1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 
3 // Copyright (c) 2015-2016, Oracle and/or its affiliates.
4 
5 // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
6 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
7 
8 // Licensed under the Boost Software License version 1.0.
9 // http://www.boost.org/users/license.html
10 
11 #ifndef BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP
12 #define BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP
13 
14 #include <boost/geometry/core/assert.hpp>
15 #include <boost/geometry/core/cs.hpp>
16 #include <boost/geometry/util/math.hpp>
17 
18 
19 namespace boost { namespace geometry
20 {
21 
22 namespace math
23 {
24 
25 #ifndef DOXYGEN_NO_DETAIL
26 namespace detail
27 {
28 
29 
30 template <typename CoordinateType, typename Units>
31 struct constants_on_spheroid
32 {
periodboost::geometry::math::detail::constants_on_spheroid33     static inline CoordinateType period()
34     {
35         return math::two_pi<CoordinateType>();
36     }
37 
half_periodboost::geometry::math::detail::constants_on_spheroid38     static inline CoordinateType half_period()
39     {
40         return math::pi<CoordinateType>();
41     }
42 
min_longitudeboost::geometry::math::detail::constants_on_spheroid43     static inline CoordinateType min_longitude()
44     {
45         static CoordinateType const minus_pi = -math::pi<CoordinateType>();
46         return minus_pi;
47     }
48 
max_longitudeboost::geometry::math::detail::constants_on_spheroid49     static inline CoordinateType max_longitude()
50     {
51         return math::pi<CoordinateType>();
52     }
53 
min_latitudeboost::geometry::math::detail::constants_on_spheroid54     static inline CoordinateType min_latitude()
55     {
56         static CoordinateType const minus_half_pi
57             = -math::half_pi<CoordinateType>();
58         return minus_half_pi;
59     }
60 
max_latitudeboost::geometry::math::detail::constants_on_spheroid61     static inline CoordinateType max_latitude()
62     {
63         return math::half_pi<CoordinateType>();
64     }
65 };
66 
67 template <typename CoordinateType>
68 struct constants_on_spheroid<CoordinateType, degree>
69 {
periodboost::geometry::math::detail::constants_on_spheroid70     static inline CoordinateType period()
71     {
72         return CoordinateType(360.0);
73     }
74 
half_periodboost::geometry::math::detail::constants_on_spheroid75     static inline CoordinateType half_period()
76     {
77         return CoordinateType(180.0);
78     }
79 
min_longitudeboost::geometry::math::detail::constants_on_spheroid80     static inline CoordinateType min_longitude()
81     {
82         return CoordinateType(-180.0);
83     }
84 
max_longitudeboost::geometry::math::detail::constants_on_spheroid85     static inline CoordinateType max_longitude()
86     {
87         return CoordinateType(180.0);
88     }
89 
min_latitudeboost::geometry::math::detail::constants_on_spheroid90     static inline CoordinateType min_latitude()
91     {
92         return CoordinateType(-90.0);
93     }
94 
max_latitudeboost::geometry::math::detail::constants_on_spheroid95     static inline CoordinateType max_latitude()
96     {
97         return CoordinateType(90.0);
98     }
99 };
100 
101 
102 template <typename Units, typename CoordinateType>
103 class normalize_spheroidal_coordinates
104 {
105     typedef constants_on_spheroid<CoordinateType, Units> constants;
106 
107 protected:
normalize_up(CoordinateType const & value)108     static inline CoordinateType normalize_up(CoordinateType const& value)
109     {
110         return
111             math::mod(value + constants::half_period(), constants::period())
112             - constants::half_period();
113     }
114 
normalize_down(CoordinateType const & value)115     static inline CoordinateType normalize_down(CoordinateType const& value)
116     {
117         return
118             math::mod(value - constants::half_period(), constants::period())
119             + constants::half_period();
120     }
121 
122 public:
apply(CoordinateType & longitude)123     static inline void apply(CoordinateType& longitude)
124     {
125         // normalize longitude
126         if (math::equals(math::abs(longitude), constants::half_period()))
127         {
128             longitude = constants::half_period();
129         }
130         else if (longitude > constants::half_period())
131         {
132             longitude = normalize_up(longitude);
133             if (math::equals(longitude, -constants::half_period()))
134             {
135                 longitude = constants::half_period();
136             }
137         }
138         else if (longitude < -constants::half_period())
139         {
140             longitude = normalize_down(longitude);
141         }
142     }
143 
apply(CoordinateType & longitude,CoordinateType & latitude,bool normalize_poles=true)144     static inline void apply(CoordinateType& longitude,
145                              CoordinateType& latitude,
146                              bool normalize_poles = true)
147     {
148 #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE
149         // normalize latitude
150         if (math::larger(latitude, constants::half_period()))
151         {
152             latitude = normalize_up(latitude);
153         }
154         else if (math::smaller(latitude, -constants::half_period()))
155         {
156             latitude = normalize_down(latitude);
157         }
158 
159         // fix latitude range
160         if (latitude < constants::min_latitude())
161         {
162             latitude = -constants::half_period() - latitude;
163             longitude -= constants::half_period();
164         }
165         else if (latitude > constants::max_latitude())
166         {
167             latitude = constants::half_period() - latitude;
168             longitude -= constants::half_period();
169         }
170 #endif // BOOST_GEOMETRY_NORMALIZE_LATITUDE
171 
172         // normalize longitude
173         apply(longitude);
174 
175         // finally normalize poles
176         if (normalize_poles)
177         {
178             if (math::equals(math::abs(latitude), constants::max_latitude()))
179             {
180                 // for the north and south pole we set the longitude to 0
181                 // (works for both radians and degrees)
182                 longitude = CoordinateType(0);
183             }
184         }
185 
186 #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE
187         BOOST_GEOMETRY_ASSERT(! math::larger(constants::min_latitude(), latitude));
188         BOOST_GEOMETRY_ASSERT(! math::larger(latitude, constants::max_latitude()));
189 #endif // BOOST_GEOMETRY_NORMALIZE_LATITUDE
190 
191         BOOST_GEOMETRY_ASSERT(math::smaller(constants::min_longitude(), longitude));
192         BOOST_GEOMETRY_ASSERT(! math::larger(longitude, constants::max_longitude()));
193     }
194 };
195 
196 
197 } // namespace detail
198 #endif // DOXYGEN_NO_DETAIL
199 
200 
201 /*!
202 \brief Short utility to normalize the coordinates on a spheroid
203 \tparam Units The units of the coordindate system in the spheroid
204 \tparam CoordinateType The type of the coordinates
205 \param longitude Longitude
206 \param latitude Latitude
207 \ingroup utility
208 */
209 template <typename Units, typename CoordinateType>
normalize_spheroidal_coordinates(CoordinateType & longitude,CoordinateType & latitude)210 inline void normalize_spheroidal_coordinates(CoordinateType& longitude,
211                                              CoordinateType& latitude)
212 {
213     detail::normalize_spheroidal_coordinates
214         <
215             Units, CoordinateType
216         >::apply(longitude, latitude);
217 }
218 
219 
220 /*!
221 \brief Short utility to normalize the longitude on a spheroid.
222        Note that in general both coordinates should be normalized at once.
223        This utility is suitable e.g. for normalization of the difference of longitudes.
224 \tparam Units The units of the coordindate system in the spheroid
225 \tparam CoordinateType The type of the coordinates
226 \param longitude Longitude
227 \ingroup utility
228 */
229 template <typename Units, typename CoordinateType>
normalize_longitude(CoordinateType & longitude)230 inline void normalize_longitude(CoordinateType& longitude)
231 {
232     detail::normalize_spheroidal_coordinates
233         <
234             Units, CoordinateType
235         >::apply(longitude);
236 }
237 
238 
239 /*!
240 \brief Short utility to calculate difference between two longitudes
241        normalized in range (-180, 180].
242 \tparam Units The units of the coordindate system in the spheroid
243 \tparam CoordinateType The type of the coordinates
244 \param longitude1 Longitude 1
245 \param longitude2 Longitude 2
246 \ingroup utility
247 */
248 template <typename Units, typename CoordinateType>
longitude_distance_signed(CoordinateType const & longitude1,CoordinateType const & longitude2)249 inline CoordinateType longitude_distance_signed(CoordinateType const& longitude1,
250                                                 CoordinateType const& longitude2)
251 {
252     CoordinateType diff = longitude2 - longitude1;
253     math::normalize_longitude<Units, CoordinateType>(diff);
254     return diff;
255 }
256 
257 
258 /*!
259 \brief Short utility to calculate difference between two longitudes
260        normalized in range [0, 360).
261 \tparam Units The units of the coordindate system in the spheroid
262 \tparam CoordinateType The type of the coordinates
263 \param longitude1 Longitude 1
264 \param longitude2 Longitude 2
265 \ingroup utility
266 */
267 template <typename Units, typename CoordinateType>
longitude_distance_unsigned(CoordinateType const & longitude1,CoordinateType const & longitude2)268 inline CoordinateType longitude_distance_unsigned(CoordinateType const& longitude1,
269                                                   CoordinateType const& longitude2)
270 {
271     typedef math::detail::constants_on_spheroid
272         <
273             CoordinateType, Units
274         > constants;
275 
276     CoordinateType const c0 = 0;
277     CoordinateType diff = longitude_distance_signed<Units>(longitude1, longitude2);
278     if (diff < c0) // (-180, 180] -> [0, 360)
279     {
280         diff += constants::period();
281     }
282     return diff;
283 }
284 
285 /*!
286 \brief The abs difference between longitudes in range [0, 180].
287 \tparam Units The units of the coordindate system in the spheroid
288 \tparam CoordinateType The type of the coordinates
289 \param longitude1 Longitude 1
290 \param longitude2 Longitude 2
291 \ingroup utility
292 */
293 template <typename Units, typename CoordinateType>
longitude_difference(CoordinateType const & longitude1,CoordinateType const & longitude2)294 inline CoordinateType longitude_difference(CoordinateType const& longitude1,
295                                            CoordinateType const& longitude2)
296 {
297     return math::abs(math::longitude_distance_signed<Units>(longitude1, longitude2));
298 }
299 
300 template <typename Units, typename CoordinateType>
longitude_interval_distance_signed(CoordinateType const & longitude_a1,CoordinateType const & longitude_a2,CoordinateType const & longitude_b)301 inline CoordinateType longitude_interval_distance_signed(CoordinateType const& longitude_a1,
302                                                          CoordinateType const& longitude_a2,
303                                                          CoordinateType const& longitude_b)
304 {
305     CoordinateType const c0 = 0;
306     CoordinateType dist_a12 = longitude_distance_signed<Units>(longitude_a1, longitude_a2);
307     CoordinateType dist_a1b = longitude_distance_signed<Units>(longitude_a1, longitude_b);
308     if (dist_a12 < c0)
309     {
310         dist_a12 = -dist_a12;
311         dist_a1b = -dist_a1b;
312     }
313 
314     return dist_a1b < c0 ? dist_a1b
315          : dist_a1b > dist_a12 ? dist_a1b - dist_a12
316          : c0;
317 }
318 
319 } // namespace math
320 
321 
322 }} // namespace boost::geometry
323 
324 #endif // BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP
325