1 /*
2 
3 Copyright (c) 2017, Project OSRM contributors
4 All rights reserved.
5 
6 Redistribution and use in source and binary forms, with or without modification,
7 are permitted provided that the following conditions are met:
8 
9 Redistributions of source code must retain the above copyright notice, this list
10 of conditions and the following disclaimer.
11 Redistributions in binary form must reproduce the above copyright notice, this
12 list of conditions and the following disclaimer in the documentation and/or
13 other materials provided with the distribution.
14 
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
19 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
22 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 */
27 
28 #ifndef OSRM_UTIL_COORDINATE_HPP_
29 #define OSRM_UTIL_COORDINATE_HPP_
30 
31 #include "util/alias.hpp"
32 
33 #include <boost/numeric/conversion/cast.hpp>
34 
35 #include <cstddef>
36 #include <iosfwd> //for std::ostream
37 #include <sstream>
38 #include <string>
39 #include <type_traits>
40 
41 namespace osrm
42 {
43 
44 constexpr const double COORDINATE_PRECISION = 1e6;
45 
46 namespace util
47 {
48 
49 namespace tag
50 {
51 struct latitude
52 {
53 };
54 struct longitude
55 {
56 };
57 struct unsafelatitude
58 {
59 };
60 struct unsafelongitude
61 {
62 };
63 } // namespace tag
64 
65 // Internal lon/lat types - assumed to be range safe
66 using FixedLatitude = Alias<std::int32_t, tag::latitude>;
67 using FixedLongitude = Alias<std::int32_t, tag::longitude>;
68 using FloatLatitude = Alias<double, tag::latitude>;
69 using FloatLongitude = Alias<double, tag::longitude>;
70 // Types used for external input data - conversion functions perform extra
71 // range checks on these (toFixed/toFloat, etc)
72 using UnsafeFloatLatitude = Alias<double, tag::unsafelatitude>;
73 using UnsafeFloatLongitude = Alias<double, tag::unsafelongitude>;
74 static_assert(std::is_pod<FixedLatitude>(), "FixedLatitude is not a valid alias");
75 static_assert(std::is_pod<FixedLongitude>(), "FixedLongitude is not a valid alias");
76 static_assert(std::is_pod<FloatLatitude>(), "FloatLatitude is not a valid alias");
77 static_assert(std::is_pod<FloatLongitude>(), "FloatLongitude is not a valid alias");
78 static_assert(std::is_pod<UnsafeFloatLatitude>(), "UnsafeFloatLatitude is not a valid alias");
79 static_assert(std::is_pod<UnsafeFloatLongitude>(), "UnsafeFloatLongitude is not a valid alias");
80 
81 /**
82  * Converts a typed latitude from floating to fixed representation.
83  *
84  * \param floating typed latitude in floating representation.
85  * \return typed latitude in fixed representation
86  * \see Coordinate, toFloating
87  */
toFixed(const FloatLatitude floating)88 inline FixedLatitude toFixed(const FloatLatitude floating)
89 {
90     const auto latitude = static_cast<double>(floating);
91     const auto fixed = static_cast<std::int32_t>(std::round(latitude * COORDINATE_PRECISION));
92     return FixedLatitude{fixed};
93 }
94 
95 /**
96  * Converts a typed latitude from floating to fixed representation.  Also performs an overflow check
97  * to ensure that the value fits inside the fixed representation.
98  *
99  * \param floating typed latitude in floating representation.
100  * \return typed latitude in fixed representation
101  * \see Coordinate, toFloating
102  */
toFixed(const UnsafeFloatLatitude floating)103 inline FixedLatitude toFixed(const UnsafeFloatLatitude floating)
104 {
105     const auto latitude = static_cast<double>(floating);
106     const auto fixed =
107         boost::numeric_cast<std::int32_t>(std::round(latitude * COORDINATE_PRECISION));
108     return FixedLatitude{fixed};
109 }
110 
111 /**
112  * Converts a typed longitude from floating to fixed representation.
113  *
114  * \param floating typed longitude in floating representation.
115  * \return typed latitude in fixed representation
116  * \see Coordinate, toFloating
117  */
toFixed(const FloatLongitude floating)118 inline FixedLongitude toFixed(const FloatLongitude floating)
119 {
120     const auto longitude = static_cast<double>(floating);
121     const auto fixed = static_cast<std::int32_t>(std::round(longitude * COORDINATE_PRECISION));
122     return FixedLongitude{fixed};
123 }
124 
125 /**
126  * Converts a typed longitude from floating to fixed representation.  Also performns an overflow
127  * check to ensure that the value fits inside the fixed representation.
128  *
129  * \param floating typed longitude in floating representation.
130  * \return typed latitude in fixed representation
131  * \see Coordinate, toFloating
132  */
toFixed(const UnsafeFloatLongitude floating)133 inline FixedLongitude toFixed(const UnsafeFloatLongitude floating)
134 {
135     const auto longitude = static_cast<double>(floating);
136     const auto fixed =
137         boost::numeric_cast<std::int32_t>(std::round(longitude * COORDINATE_PRECISION));
138     return FixedLongitude{fixed};
139 }
140 
141 /**
142  * Converts a typed latitude from fixed to floating representation.
143  *
144  * \param fixed typed latitude in fixed representation.
145  * \return typed latitude in floating representation
146  * \see Coordinate, toFixed
147  */
toFloating(const FixedLatitude fixed)148 inline FloatLatitude toFloating(const FixedLatitude fixed)
149 {
150     const auto latitude = static_cast<std::int32_t>(fixed);
151     const auto floating = static_cast<double>(latitude) / COORDINATE_PRECISION;
152     return FloatLatitude{floating};
153 }
154 
155 /**
156  * Converts a typed longitude from fixed to floating representation.
157  *
158  * \param fixed typed longitude in fixed representation.
159  * \return typed longitude in floating representation
160  * \see Coordinate, toFixed
161  */
toFloating(const FixedLongitude fixed)162 inline FloatLongitude toFloating(const FixedLongitude fixed)
163 {
164     const auto longitude = static_cast<std::int32_t>(fixed);
165     const auto floating = static_cast<double>(longitude) / COORDINATE_PRECISION;
166     return FloatLongitude{floating};
167 }
168 
169 // fwd. decl.
170 struct FloatCoordinate;
171 
172 /**
173  * Represents a coordinate based on longitude and latitude in fixed representation.
174  *
175  * To prevent accidental longitude and latitude flips, we provide typed longitude and latitude
176  * wrappers. You can cast these wrappers back to their underlying representation or convert them
177  * from one representation to the other.
178  *
179  * The two representation we provide are:
180  *  - Fixed point
181  *  - Floating point
182  *
183  * \see FloatCoordinate, toFixed, toFloating
184  */
185 struct Coordinate
186 {
187     FixedLongitude lon;
188     FixedLatitude lat;
189 
Coordinateosrm::util::Coordinate190     Coordinate() : lon{std::numeric_limits<int>::min()}, lat{std::numeric_limits<int>::min()} {}
191 
192     Coordinate(const FloatCoordinate &other);
193 
Coordinateosrm::util::Coordinate194     Coordinate(const FloatLongitude lon_, const FloatLatitude lat_)
195         : Coordinate(toFixed(lon_), toFixed(lat_))
196     {
197     }
198 
Coordinateosrm::util::Coordinate199     Coordinate(const UnsafeFloatLongitude lon_, const UnsafeFloatLatitude lat_)
200         : Coordinate(toFixed(lon_), toFixed(lat_))
201     {
202     }
203 
Coordinateosrm::util::Coordinate204     Coordinate(const FixedLongitude lon_, const FixedLatitude lat_) : lon(lon_), lat(lat_) {}
205 
Coordinateosrm::util::Coordinate206     template <class T> Coordinate(const T &coordinate) : lon(coordinate.lon), lat(coordinate.lat)
207     {
208         static_assert(!std::is_same<T, Coordinate>::value,
209                       "This constructor should not be used for Coordinates");
210         static_assert(std::is_same<decltype(lon), decltype(coordinate.lon)>::value,
211                       "coordinate types incompatible");
212         static_assert(std::is_same<decltype(lat), decltype(coordinate.lat)>::value,
213                       "coordinate types incompatible");
214     }
215 
216     bool IsValid() const;
217     friend bool operator==(const Coordinate lhs, const Coordinate rhs);
218     friend bool operator!=(const Coordinate lhs, const Coordinate rhs);
219 };
220 
221 /**
222  * Represents a coordinate based on longitude and latitude in floating representation.
223  *
224  * To prevent accidental longitude and latitude flips, we provide typed longitude and latitude
225  * wrappers. You can cast these wrappers back to their underlying representation or convert them
226  * from one representation to the other.
227  *
228  * The two representation we provide are:
229  *  - Fixed point
230  *  - Floating point
231  *
232  * \see Coordinate, toFixed, toFloating
233  */
234 struct FloatCoordinate
235 {
236     FloatLongitude lon;
237     FloatLatitude lat;
238 
FloatCoordinateosrm::util::FloatCoordinate239     FloatCoordinate()
240         : lon{std::numeric_limits<double>::min()}, lat{std::numeric_limits<double>::min()}
241     {
242     }
243 
FloatCoordinateosrm::util::FloatCoordinate244     FloatCoordinate(const Coordinate other)
245         : FloatCoordinate(toFloating(other.lon), toFloating(other.lat))
246     {
247     }
248 
FloatCoordinateosrm::util::FloatCoordinate249     FloatCoordinate(const FixedLongitude lon_, const FixedLatitude lat_)
250         : FloatCoordinate(toFloating(lon_), toFloating(lat_))
251     {
252     }
253 
FloatCoordinateosrm::util::FloatCoordinate254     FloatCoordinate(const FloatLongitude lon_, const FloatLatitude lat_) : lon(lon_), lat(lat_) {}
255 
256     bool IsValid() const;
257     friend bool operator==(const FloatCoordinate lhs, const FloatCoordinate rhs);
258     friend bool operator!=(const FloatCoordinate lhs, const FloatCoordinate rhs);
259 };
260 
261 bool operator==(const Coordinate lhs, const Coordinate rhs);
262 bool operator==(const FloatCoordinate lhs, const FloatCoordinate rhs);
263 
Coordinate(const FloatCoordinate & other)264 inline Coordinate::Coordinate(const FloatCoordinate &other)
265     : Coordinate(toFixed(other.lon), toFixed(other.lat))
266 {
267 }
268 } // namespace util
269 } // namespace osrm
270 
271 #endif /* COORDINATE_HPP_ */
272