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