Libosmium  2.9.0
Fast and flexible C++ library for working with OpenStreetMap data
location.hpp
Go to the documentation of this file.
1 #ifndef OSMIUM_OSM_LOCATION_HPP
2 #define OSMIUM_OSM_LOCATION_HPP
3 
4 /*
5 
6 This file is part of Osmium (http://osmcode.org/libosmium).
7 
8 Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
9 
10 Boost Software License - Version 1.0 - August 17th, 2003
11 
12 Permission is hereby granted, free of charge, to any person or organization
13 obtaining a copy of the software and accompanying documentation covered by
14 this license (the "Software") to use, reproduce, display, distribute,
15 execute, and transmit the Software, and to prepare derivative works of the
16 Software, and to permit third-parties to whom the Software is furnished to
17 do so, all subject to the following:
18 
19 The copyright notices in the Software and this entire statement, including
20 the above license grant, this restriction and the following disclaimer,
21 must be included in all copies of the Software, in whole or in part, and
22 all derivative works of the Software, unless such copies or derivative
23 works are solely in the form of machine-executable object code generated by
24 a source language processor.
25 
26 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
29 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
30 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
31 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32 DEALINGS IN THE SOFTWARE.
33 
34 */
35 
36 #include <cmath>
37 #include <cstdint>
38 #include <cstring>
39 #include <functional>
40 #include <iosfwd>
41 #include <iterator>
42 #include <limits>
43 #include <stdexcept>
44 #include <string>
45 
46 namespace osmium {
47 
52  struct invalid_location : public std::range_error {
53 
54  explicit invalid_location(const std::string& what) :
55  std::range_error(what) {
56  }
57 
58  explicit invalid_location(const char* what) :
59  std::range_error(what) {
60  }
61 
62  }; // struct invalid_location
63 
64  namespace detail {
65 
66  constexpr const int coordinate_precision = 10000000;
67 
68  // Convert string with a floating point number into integer suitable
69  // for use as coordinate in a Location.
70  inline int32_t string_to_location_coordinate(const char** data) {
71  const char* str = *data;
72  const char* full = str;
73 
74  int64_t result = 0;
75  int sign = 1;
76 
77  // one more than significant digits to allow rounding
78  int64_t scale = 8;
79 
80  // paranoia check for maximum number of digits
81  int max_digits = 10;
82 
83  // optional minus sign
84  if (*str == '-') {
85  sign = -1;
86  ++str;
87  }
88 
89  // there has to be at least one digit
90  if (*str >= '0' && *str <= '9') {
91  result = *str - '0';
92  ++str;
93  } else {
94  goto error;
95  }
96 
97  // optional additional digits before decimal point
98  while (*str >= '0' && *str <= '9' && max_digits > 0) {
99  result = result * 10 + (*str - '0');
100  ++str;
101  --max_digits;
102  }
103 
104  if (max_digits == 0) {
105  goto error;
106  }
107 
108  // optional decimal point
109  if (*str == '.') {
110  ++str;
111 
112  // read significant digits
113  for (; scale > 0 && *str >= '0' && *str <= '9'; --scale, ++str) {
114  result = result * 10 + (*str - '0');
115  }
116 
117  // ignore non-significant digits
118  max_digits = 20;
119  while (*str >= '0' && *str <= '9' && max_digits > 0) {
120  ++str;
121  --max_digits;
122  }
123 
124  if (max_digits == 0) {
125  goto error;
126  }
127  }
128 
129  // optional exponent in scientific notation
130  if (*str == 'e' || *str == 'E') {
131  ++str;
132 
133  int esign = 1;
134  // optional minus sign
135  if (*str == '-') {
136  esign = -1;
137  ++str;
138  }
139 
140  int64_t eresult = 0;
141 
142  // there has to be at least one digit in exponent
143  if (*str >= '0' && *str <= '9') {
144  eresult = *str - '0';
145  ++str;
146  } else {
147  goto error;
148  }
149 
150  // optional additional digits in exponent
151  max_digits = 5;
152  while (*str >= '0' && *str <= '9' && max_digits > 0) {
153  eresult = eresult * 10 + (*str - '0');
154  ++str;
155  --max_digits;
156  }
157 
158  if (max_digits == 0) {
159  goto error;
160  }
161 
162  scale += eresult * esign;
163  }
164 
165  if (scale < 0) {
166  result = 0;
167  } else {
168  for (; scale > 0; --scale) {
169  result *= 10;
170  }
171 
172  result = (result + 5) / 10 * sign;
173 
174  if (result > std::numeric_limits<int32_t>::max() ||
175  result < std::numeric_limits<int32_t>::min()) {
176  goto error;
177  }
178  }
179 
180  *data = str;
181  return static_cast<int32_t>(result);
182 
183  error:
184 
185  throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
186  }
187 
188  // Convert integer as used by location for coordinates into a string.
189  template <typename T>
190  inline T append_location_coordinate_to_string(T iterator, int32_t value) {
191  // handle negative values
192  if (value < 0) {
193  *iterator++ = '-';
194  value = -value;
195  }
196 
197  // write digits into temporary buffer
198  int32_t v = value;
199  char temp[10];
200  char* t = temp;
201  do {
202  *t++ = char(v % 10) + '0';
203  v /= 10;
204  } while (v != 0);
205 
206  while (t-temp < 7) {
207  *t++ = '0';
208  }
209 
210  // write out digits before decimal point
211  if (value >= coordinate_precision) {
212  if (value >= 10 * coordinate_precision) {
213  if (value >= 100 * coordinate_precision) {
214  *iterator++ = *--t;
215  }
216  *iterator++ = *--t;
217  }
218  *iterator++ = *--t;
219  } else {
220  *iterator++ = '0';
221  }
222 
223  // remove trailing zeros
224  const char* tn = temp;
225  while (tn < t && *tn == '0') {
226  ++tn;
227  }
228 
229  // decimal point
230  if (t != tn) {
231  *iterator++ = '.';
232  while (t != tn) {
233  *iterator++ = *--t;
234  }
235  }
236 
237  return iterator;
238  }
239 
240  } // namespace detail
241 
256  class Location {
257 
258  int32_t m_x;
259  int32_t m_y;
260 
261  public:
262 
263  // this value is used for a coordinate to mark it as undefined
264  // MSVC doesn't declare std::numeric_limits<int32_t>::max() as
265  // constexpr, so we hard code this for the time being.
266  // static constexpr int32_t undefined_coordinate = std::numeric_limits<int32_t>::max();
267  static constexpr int32_t undefined_coordinate = 2147483647;
268 
269  static int32_t double_to_fix(const double c) noexcept {
270  return static_cast<int32_t>(std::round(c * detail::coordinate_precision));
271  }
272 
273  static constexpr double fix_to_double(const int32_t c) noexcept {
274  return static_cast<double>(c) / detail::coordinate_precision;
275  }
276 
280  explicit constexpr Location() noexcept :
281  m_x(undefined_coordinate),
282  m_y(undefined_coordinate) {
283  }
284 
290  constexpr Location(const int32_t x, const int32_t y) noexcept :
291  m_x(x),
292  m_y(y) {
293  }
294 
300  constexpr Location(const int64_t x, const int64_t y) noexcept :
301  m_x(static_cast<int32_t>(x)),
302  m_y(static_cast<int32_t>(y)) {
303  }
304 
308  Location(const double lon, const double lat) :
309  m_x(double_to_fix(lon)),
310  m_y(double_to_fix(lat)) {
311  }
312 
313  Location(const Location&) = default;
314  Location(Location&&) = default;
315  Location& operator=(const Location&) = default;
316  Location& operator=(Location&&) = default;
317  ~Location() = default;
318 
323  explicit constexpr operator bool() const noexcept {
324  return m_x != undefined_coordinate && m_y != undefined_coordinate;
325  }
326 
331  constexpr bool valid() const noexcept {
332  return m_x >= -180 * detail::coordinate_precision
333  && m_x <= 180 * detail::coordinate_precision
334  && m_y >= -90 * detail::coordinate_precision
335  && m_y <= 90 * detail::coordinate_precision;
336  }
337 
338  constexpr int32_t x() const noexcept {
339  return m_x;
340  }
341 
342  constexpr int32_t y() const noexcept {
343  return m_y;
344  }
345 
346  Location& set_x(const int32_t x) noexcept {
347  m_x = x;
348  return *this;
349  }
350 
351  Location& set_y(const int32_t y) noexcept {
352  m_y = y;
353  return *this;
354  }
355 
361  double lon() const {
362  if (!valid()) {
363  throw osmium::invalid_location("invalid location");
364  }
365  return fix_to_double(m_x);
366  }
367 
371  double lon_without_check() const {
372  return fix_to_double(m_x);
373  }
374 
380  double lat() const {
381  if (!valid()) {
382  throw osmium::invalid_location("invalid location");
383  }
384  return fix_to_double(m_y);
385  }
386 
390  double lat_without_check() const {
391  return fix_to_double(m_y);
392  }
393 
394  Location& set_lon(double lon) noexcept {
395  m_x = double_to_fix(lon);
396  return *this;
397  }
398 
399  Location& set_lat(double lat) noexcept {
400  m_y = double_to_fix(lat);
401  return *this;
402  }
403 
404  Location& set_lon(const char* str) {
405  const char** data = &str;
406  m_x = detail::string_to_location_coordinate(data);
407  if (**data != '\0') {
408  throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
409  }
410  return *this;
411  }
412 
413  Location& set_lat(const char* str) {
414  const char** data = &str;
415  m_y = detail::string_to_location_coordinate(data);
416  if (**data != '\0') {
417  throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
418  }
419  return *this;
420  }
421 
422  Location& set_lon_partial(const char** str) {
423  m_x = detail::string_to_location_coordinate(str);
424  return *this;
425  }
426 
427  Location& set_lat_partial(const char** str) {
428  m_y = detail::string_to_location_coordinate(str);
429  return *this;
430  }
431 
432  template <typename T>
433  T as_string_without_check(T iterator, const char separator = ',') const {
434  iterator = detail::append_location_coordinate_to_string(iterator, x());
435  *iterator++ = separator;
436  return detail::append_location_coordinate_to_string(iterator, y());
437  }
438 
439  template <typename T>
440  T as_string(T iterator, const char separator = ',') const {
441  if (!valid()) {
442  throw osmium::invalid_location("invalid location");
443  }
444  return as_string_without_check(iterator, separator);
445  }
446 
447  }; // class Location
448 
452  inline constexpr bool operator==(const Location& lhs, const Location& rhs) noexcept {
453  return lhs.x() == rhs.x() && lhs.y() == rhs.y();
454  }
455 
456  inline constexpr bool operator!=(const Location& lhs, const Location& rhs) noexcept {
457  return ! (lhs == rhs);
458  }
459 
465  inline constexpr bool operator<(const Location& lhs, const Location& rhs) noexcept {
466  return (lhs.x() == rhs.x() && lhs.y() < rhs.y()) || lhs.x() < rhs.x();
467  }
468 
469  inline constexpr bool operator>(const Location& lhs, const Location& rhs) noexcept {
470  return rhs < lhs;
471  }
472 
473  inline constexpr bool operator<=(const Location& lhs, const Location& rhs) noexcept {
474  return ! (rhs < lhs);
475  }
476 
477  inline constexpr bool operator>=(const Location& lhs, const Location& rhs) noexcept {
478  return ! (lhs < rhs);
479  }
480 
484  template <typename TChar, typename TTraits>
485  inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::Location& location) {
486  if (location) {
487  out << '(';
488  location.as_string(std::ostream_iterator<char>(out), ',');
489  out << ')';
490  } else {
491  out << "(undefined,undefined)";
492  }
493  return out;
494  }
495 
496  namespace detail {
497 
498  template <int N>
499  inline size_t hash(const osmium::Location& location) noexcept {
500  return location.x() ^ location.y();
501  }
502 
503  template <>
504  inline size_t hash<8>(const osmium::Location& location) noexcept {
505  size_t h = location.x();
506  h <<= 32;
507  return h ^ location.y();
508  }
509 
510  } // namespace detail
511 
512 } // namespace osmium
513 
514 namespace std {
515 
516 // This pragma is a workaround for a bug in an old libc implementation
517 #ifdef __clang__
518 #pragma clang diagnostic push
519 #pragma clang diagnostic ignored "-Wmismatched-tags"
520 #endif
521  template <>
522  struct hash<osmium::Location> {
524  using result_type = size_t;
525  size_t operator()(const osmium::Location& location) const noexcept {
526  return osmium::detail::hash<sizeof(size_t)>(location);
527  }
528  };
529 #ifdef __clang__
530 #pragma clang diagnostic pop
531 #endif
532 
533 } // namespace std
534 
535 #endif // OSMIUM_OSM_LOCATION_HPP
Location & set_lon(const char *str)
Definition: location.hpp:404
static int32_t double_to_fix(const double c) noexcept
Definition: location.hpp:269
double lon() const
Definition: location.hpp:361
bool operator<=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:453
Location & set_lat_partial(const char **str)
Definition: location.hpp:427
double lat_without_check() const
Definition: location.hpp:390
constexpr bool operator==(const Box &lhs, const Box &rhs) noexcept
Definition: box.hpp:221
Location & set_lon(double lon) noexcept
Definition: location.hpp:394
double lat() const
Definition: location.hpp:380
Definition: reader_iterator.hpp:39
constexpr Location(const int64_t x, const int64_t y) noexcept
Definition: location.hpp:300
constexpr Location(const int32_t x, const int32_t y) noexcept
Definition: location.hpp:290
constexpr bool valid() const noexcept
Definition: location.hpp:331
Location & set_lat(const char *str)
Definition: location.hpp:413
size_t operator()(const osmium::Location &location) const noexcept
Definition: location.hpp:525
bool operator<(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:445
Namespace for everything in the Osmium library.
Definition: assembler.hpp:73
Definition: attr.hpp:333
Definition: location.hpp:52
int32_t m_y
Definition: location.hpp:259
constexpr int32_t y() const noexcept
Definition: location.hpp:342
invalid_location(const std::string &what)
Definition: location.hpp:54
Location & set_lat(double lat) noexcept
Definition: location.hpp:399
Location & set_y(const int32_t y) noexcept
Definition: location.hpp:351
Definition: location.hpp:256
int32_t m_x
Definition: location.hpp:258
Location & set_x(const int32_t x) noexcept
Definition: location.hpp:346
bool operator>=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:457
T as_string(T iterator, const char separator=',') const
Definition: location.hpp:440
bool operator>(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:449
T as_string_without_check(T iterator, const char separator=',') const
Definition: location.hpp:433
invalid_location(const char *what)
Definition: location.hpp:58
constexpr int32_t x() const noexcept
Definition: location.hpp:338
double lon_without_check() const
Definition: location.hpp:371
static constexpr double fix_to_double(const int32_t c) noexcept
Definition: location.hpp:273
Location & set_lon_partial(const char **str)
Definition: location.hpp:422
Location(const double lon, const double lat)
Definition: location.hpp:308
bool operator!=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:438
size_t result_type
Definition: location.hpp:524
constexpr Location() noexcept
Definition: location.hpp:280