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