Libosmium  2.15.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 (https://osmcode.org/libosmium).
7 
8 Copyright 2013-2018 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 <algorithm>
37 #include <cmath>
38 #include <cstdint>
39 #include <cstring>
40 #include <functional>
41 #include <iosfwd>
42 #include <iterator>
43 #include <limits>
44 #include <stdexcept>
45 #include <string>
46 
47 namespace osmium {
48 
53  struct invalid_location : public std::range_error {
54 
55  explicit invalid_location(const std::string& what) :
56  std::range_error(what) {
57  }
58 
59  explicit invalid_location(const char* what) :
60  std::range_error(what) {
61  }
62 
63  }; // struct invalid_location
64 
65  namespace detail {
66 
67  enum {
68  coordinate_precision = 10000000
69  };
70 
71  // Convert string with a floating point number into integer suitable
72  // for use as coordinate in a Location.
73  inline int32_t string_to_location_coordinate(const char** data) {
74  const char* str = *data;
75  const char* full = str;
76 
77  int64_t result = 0;
78  int sign = 1;
79 
80  // one more than significant digits to allow rounding
81  int64_t scale = 8;
82 
83  // paranoia check for maximum number of digits
84  int max_digits = 10;
85 
86  // optional minus sign
87  if (*str == '-') {
88  sign = -1;
89  ++str;
90  }
91 
92  if (*str != '.') {
93  // there has to be at least one digit
94  if (*str >= '0' && *str <= '9') {
95  result = *str - '0';
96  ++str;
97  } else {
98  goto error;
99  }
100 
101  // optional additional digits before decimal point
102  while (*str >= '0' && *str <= '9' && max_digits > 0) {
103  result = result * 10 + (*str - '0');
104  ++str;
105  --max_digits;
106  }
107 
108  if (max_digits == 0) {
109  goto error;
110  }
111  } else {
112  // need at least one digit after decimal dot if there was no
113  // digit before decimal dot
114  if (*(str + 1) < '0' || *(str + 1) > '9') {
115  goto error;
116  }
117  }
118 
119  // optional decimal point
120  if (*str == '.') {
121  ++str;
122 
123  // read significant digits
124  for (; scale > 0 && *str >= '0' && *str <= '9'; --scale, ++str) {
125  result = result * 10 + (*str - '0');
126  }
127 
128  // ignore non-significant digits
129  max_digits = 20;
130  while (*str >= '0' && *str <= '9' && max_digits > 0) {
131  ++str;
132  --max_digits;
133  }
134 
135  if (max_digits == 0) {
136  goto error;
137  }
138  }
139 
140  // optional exponent in scientific notation
141  if (*str == 'e' || *str == 'E') {
142  ++str;
143 
144  int esign = 1;
145  // optional minus sign
146  if (*str == '-') {
147  esign = -1;
148  ++str;
149  }
150 
151  int64_t eresult = 0;
152 
153  // there has to be at least one digit in exponent
154  if (*str >= '0' && *str <= '9') {
155  eresult = *str - '0';
156  ++str;
157  } else {
158  goto error;
159  }
160 
161  // optional additional digits in exponent
162  max_digits = 5;
163  while (*str >= '0' && *str <= '9' && max_digits > 0) {
164  eresult = eresult * 10 + (*str - '0');
165  ++str;
166  --max_digits;
167  }
168 
169  if (max_digits == 0) {
170  goto error;
171  }
172 
173  scale += eresult * esign;
174  }
175 
176  if (scale < 0) {
177  for (; scale < 0 && result > 0; ++scale) {
178  result /= 10;
179  }
180  } else {
181  for (; scale > 0; --scale) {
182  result *= 10;
183  }
184  }
185 
186  result = (result + 5) / 10 * sign;
187 
188  if (result > std::numeric_limits<int32_t>::max() ||
189  result < std::numeric_limits<int32_t>::min()) {
190  goto error;
191  }
192 
193  *data = str;
194  return static_cast<int32_t>(result);
195 
196  error:
197 
198  throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
199  }
200 
201  // Convert integer as used by location for coordinates into a string.
202  template <typename T>
203  inline T append_location_coordinate_to_string(T iterator, int32_t value) {
204  // need to special-case this, because later `value = -value` would overflow.
205  if (value == std::numeric_limits<int32_t>::min()) {
206  static const char minresult[] = "-214.7483648";
207  return std::copy_n(minresult, sizeof(minresult) - 1, iterator);
208  }
209 
210  // handle negative values
211  if (value < 0) {
212  *iterator++ = '-';
213  value = -value;
214  }
215 
216  // write digits into temporary buffer
217  int32_t v = value;
218  char temp[10];
219  char* t = temp;
220  do {
221  *t++ = char(v % 10) + '0';
222  v /= 10;
223  } while (v != 0);
224 
225  while (t - temp < 7) {
226  *t++ = '0';
227  }
228 
229  // write out digits before decimal point
230  if (value >= coordinate_precision) {
231  if (value >= 10 * coordinate_precision) {
232  if (value >= 100 * coordinate_precision) {
233  *iterator++ = *--t;
234  }
235  *iterator++ = *--t;
236  }
237  *iterator++ = *--t;
238  } else {
239  *iterator++ = '0';
240  }
241 
242  // remove trailing zeros
243  const char* tn = temp;
244  while (tn < t && *tn == '0') {
245  ++tn;
246  }
247 
248  // decimal point
249  if (t != tn) {
250  *iterator++ = '.';
251  while (t != tn) {
252  *iterator++ = *--t;
253  }
254  }
255 
256  return iterator;
257  }
258 
259  } // namespace detail
260 
275  class Location {
276 
277  int32_t m_x; // NOLINT(modernize-use-default-member-init)
278  int32_t m_y; // NOLINT(modernize-use-default-member-init)
279 
280  public:
281 
282  // this value is used for a coordinate to mark it as undefined
283  // MSVC doesn't declare std::numeric_limits<int32_t>::max() as
284  // constexpr, so we hard code this for the time being.
285  // undefined_coordinate = std::numeric_limits<int32_t>::max();
286  enum {
287  undefined_coordinate = 2147483647
288  };
289 
290  static int32_t double_to_fix(const double c) noexcept {
291  return static_cast<int32_t>(std::round(c * detail::coordinate_precision));
292  }
293 
294  static constexpr double fix_to_double(const int32_t c) noexcept {
295  return static_cast<double>(c) / detail::coordinate_precision;
296  }
297 
301  explicit constexpr Location() noexcept :
302  m_x(undefined_coordinate),
303  m_y(undefined_coordinate) {
304  }
305 
311  constexpr Location(const int32_t x, const int32_t y) noexcept :
312  m_x(x),
313  m_y(y) {
314  }
315 
321  constexpr Location(const int64_t x, const int64_t y) noexcept :
322  m_x(static_cast<int32_t>(x)),
323  m_y(static_cast<int32_t>(y)) {
324  }
325 
329  Location(const double lon, const double lat) :
330  m_x(double_to_fix(lon)),
331  m_y(double_to_fix(lat)) {
332  }
333 
341  explicit constexpr operator bool() const noexcept {
342  return m_x != undefined_coordinate && m_y != undefined_coordinate;
343  }
344 
351  constexpr bool valid() const noexcept {
352  return m_x >= -180 * detail::coordinate_precision
353  && m_x <= 180 * detail::coordinate_precision
354  && m_y >= -90 * detail::coordinate_precision
355  && m_y <= 90 * detail::coordinate_precision;
356  }
357 
363  constexpr bool is_defined() const noexcept {
364  return m_x != undefined_coordinate || m_y != undefined_coordinate;
365  }
366 
372  constexpr bool is_undefined() const noexcept {
373  return m_x == undefined_coordinate && m_y == undefined_coordinate;
374  }
375 
376  constexpr int32_t x() const noexcept {
377  return m_x;
378  }
379 
380  constexpr int32_t y() const noexcept {
381  return m_y;
382  }
383 
384  Location& set_x(const int32_t x) noexcept {
385  m_x = x;
386  return *this;
387  }
388 
389  Location& set_y(const int32_t y) noexcept {
390  m_y = y;
391  return *this;
392  }
393 
399  double lon() const {
400  if (!valid()) {
401  throw osmium::invalid_location{"invalid location"};
402  }
403  return fix_to_double(m_x);
404  }
405 
409  double lon_without_check() const {
410  return fix_to_double(m_x);
411  }
412 
418  double lat() const {
419  if (!valid()) {
420  throw osmium::invalid_location{"invalid location"};
421  }
422  return fix_to_double(m_y);
423  }
424 
428  double lat_without_check() const {
429  return fix_to_double(m_y);
430  }
431 
432  Location& set_lon(double lon) noexcept {
433  m_x = double_to_fix(lon);
434  return *this;
435  }
436 
437  Location& set_lat(double lat) noexcept {
438  m_y = double_to_fix(lat);
439  return *this;
440  }
441 
442  Location& set_lon(const char* str) {
443  const char** data = &str;
444  const auto value = detail::string_to_location_coordinate(data);
445  if (**data != '\0') {
446  throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
447  }
448  m_x = value;
449  return *this;
450  }
451 
452  Location& set_lat(const char* str) {
453  const char** data = &str;
454  const auto value = detail::string_to_location_coordinate(data);
455  if (**data != '\0') {
456  throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
457  }
458  m_y = value;
459  return *this;
460  }
461 
462  Location& set_lon_partial(const char** str) {
463  m_x = detail::string_to_location_coordinate(str);
464  return *this;
465  }
466 
467  Location& set_lat_partial(const char** str) {
468  m_y = detail::string_to_location_coordinate(str);
469  return *this;
470  }
471 
472  template <typename T>
473  T as_string_without_check(T iterator, const char separator = ',') const {
474  iterator = detail::append_location_coordinate_to_string(iterator, x());
475  *iterator++ = separator;
476  return detail::append_location_coordinate_to_string(iterator, y());
477  }
478 
479  template <typename T>
480  T as_string(T iterator, const char separator = ',') const {
481  if (!valid()) {
482  throw osmium::invalid_location{"invalid location"};
483  }
484  return as_string_without_check(iterator, separator);
485  }
486 
487  }; // class Location
488 
492  inline constexpr bool operator==(const Location& lhs, const Location& rhs) noexcept {
493  return lhs.x() == rhs.x() && lhs.y() == rhs.y();
494  }
495 
496  inline constexpr bool operator!=(const Location& lhs, const Location& rhs) noexcept {
497  return !(lhs == rhs);
498  }
499 
505  inline constexpr bool operator<(const Location& lhs, const Location& rhs) noexcept {
506  return (lhs.x() == rhs.x() && lhs.y() < rhs.y()) || lhs.x() < rhs.x();
507  }
508 
509  inline constexpr bool operator>(const Location& lhs, const Location& rhs) noexcept {
510  return rhs < lhs;
511  }
512 
513  inline constexpr bool operator<=(const Location& lhs, const Location& rhs) noexcept {
514  return !(rhs < lhs);
515  }
516 
517  inline constexpr bool operator>=(const Location& lhs, const Location& rhs) noexcept {
518  return !(lhs < rhs);
519  }
520 
524  template <typename TChar, typename TTraits>
525  inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::Location& location) {
526  if (location) {
527  out << '(';
528  location.as_string(std::ostream_iterator<char>(out), ',');
529  out << ')';
530  } else {
531  out << "(undefined,undefined)";
532  }
533  return out;
534  }
535 
536  namespace detail {
537 
538  template <int N>
539  inline size_t hash(const osmium::Location& location) noexcept {
540  return static_cast<uint32_t>(location.x()) ^ static_cast<uint32_t>(location.y());
541  }
542 
543  template <>
544  inline size_t hash<8>(const osmium::Location& location) noexcept {
545  uint64_t h = location.x();
546  h <<= 32u;
547  return static_cast<size_t>(h ^ static_cast<uint64_t>(location.y()));
548  }
549 
550  } // namespace detail
551 
552 } // namespace osmium
553 
554 namespace std {
555 
556 // This pragma is a workaround for a bug in an old libc implementation
557 #ifdef __clang__
558 #pragma clang diagnostic push
559 #pragma clang diagnostic ignored "-Wmismatched-tags"
560 #endif
561  template <>
562  struct hash<osmium::Location> {
564  using result_type = size_t;
565  size_t operator()(const osmium::Location& location) const noexcept {
566  return osmium::detail::hash<sizeof(size_t)>(location);
567  }
568  };
569 #ifdef __clang__
570 #pragma clang diagnostic pop
571 #endif
572 
573 } // namespace std
574 
575 #endif // OSMIUM_OSM_LOCATION_HPP
Location & set_lon(const char *str)
Definition: location.hpp:442
static int32_t double_to_fix(const double c) noexcept
Definition: location.hpp:290
double lon() const
Definition: location.hpp:399
bool operator<=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:461
Location & set_lat_partial(const char **str)
Definition: location.hpp:467
double lat_without_check() const
Definition: location.hpp:428
constexpr bool operator==(const Box &lhs, const Box &rhs) noexcept
Definition: box.hpp:212
Location & set_lon(double lon) noexcept
Definition: location.hpp:432
double lat() const
Definition: location.hpp:418
Definition: location.hpp:554
constexpr Location(const int64_t x, const int64_t y) noexcept
Definition: location.hpp:321
constexpr Location(const int32_t x, const int32_t y) noexcept
Definition: location.hpp:311
constexpr bool valid() const noexcept
Definition: location.hpp:351
Location & set_lat(const char *str)
Definition: location.hpp:452
size_t operator()(const osmium::Location &location) const noexcept
Definition: location.hpp:565
bool operator<(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:453
Namespace for everything in the Osmium library.
Definition: assembler.hpp:53
Definition: attr.hpp:333
Definition: location.hpp:53
int32_t m_y
Definition: location.hpp:278
constexpr int32_t y() const noexcept
Definition: location.hpp:380
invalid_location(const std::string &what)
Definition: location.hpp:55
Location & set_lat(double lat) noexcept
Definition: location.hpp:437
Location & set_y(const int32_t y) noexcept
Definition: location.hpp:389
Definition: location.hpp:275
int32_t m_x
Definition: location.hpp:277
Location & set_x(const int32_t x) noexcept
Definition: location.hpp:384
bool operator>=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:465
T as_string(T iterator, const char separator=',') const
Definition: location.hpp:480
bool operator>(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:457
T as_string_without_check(T iterator, const char separator=',') const
Definition: location.hpp:473
invalid_location(const char *what)
Definition: location.hpp:59
constexpr int32_t x() const noexcept
Definition: location.hpp:376
double lon_without_check() const
Definition: location.hpp:409
static constexpr double fix_to_double(const int32_t c) noexcept
Definition: location.hpp:294
constexpr bool is_defined() const noexcept
Definition: location.hpp:363
Location & set_lon_partial(const char **str)
Definition: location.hpp:462
Location(const double lon, const double lat)
Definition: location.hpp:329
bool operator!=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:446
constexpr bool is_undefined() const noexcept
Definition: location.hpp:372
size_t result_type
Definition: location.hpp:564
constexpr Location() noexcept
Definition: location.hpp:301