Libosmium  2.17.3
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-2022 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  throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
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  throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
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  throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
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  throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
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  throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
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  throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
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  throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
191  }
192 
193  *data = str;
194  return static_cast<int32_t>(result);
195  }
196 
197  // Convert integer as used by location for coordinates into a string.
198  template <typename T>
199  inline T append_location_coordinate_to_string(T iterator, int32_t value) {
200  // need to special-case this, because later `value = -value` would overflow.
201  if (value == std::numeric_limits<int32_t>::min()) {
202  static const char minresult[] = "-214.7483648";
203  return std::copy_n(minresult, sizeof(minresult) - 1, iterator);
204  }
205 
206  // handle negative values
207  if (value < 0) {
208  *iterator++ = '-';
209  value = -value;
210  }
211 
212  // write digits into temporary buffer
213  int32_t v = value;
214  char temp[10];
215  char* t = temp;
216  do {
217  *t++ = static_cast<char>(v % 10) + '0'; // NOLINT(bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions)
218  v /= 10;
219  } while (v != 0);
220 
221  while (t - temp < 7) {
222  *t++ = '0';
223  }
224 
225  // write out digits before decimal point
226  if (value >= coordinate_precision) {
227  if (value >= 10 * coordinate_precision) {
228  if (value >= 100 * coordinate_precision) {
229  *iterator++ = *--t;
230  }
231  *iterator++ = *--t;
232  }
233  *iterator++ = *--t;
234  } else {
235  *iterator++ = '0';
236  }
237 
238  // remove trailing zeros
239  const char* tn = temp;
240  while (tn < t && *tn == '0') {
241  ++tn;
242  }
243 
244  // decimal point
245  if (t != tn) {
246  *iterator++ = '.';
247  while (t != tn) {
248  *iterator++ = *--t;
249  }
250  }
251 
252  return iterator;
253  }
254 
255  } // namespace detail
256 
271  class Location {
272 
273  int32_t m_x; // NOLINT(modernize-use-default-member-init)
274  int32_t m_y; // NOLINT(modernize-use-default-member-init)
275 
276  constexpr static double precision() noexcept {
277  return static_cast<double>(detail::coordinate_precision);
278  }
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 * precision()));
292  }
293 
294  static constexpr double fix_to_double(const int32_t c) noexcept {
295  return static_cast<double>(c) / precision();
296  }
297 
301  explicit constexpr Location() noexcept :
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 
330  Location(const double lon, const double lat) :
332  m_y(double_to_fix(lat)) {
333  }
334 
342  explicit constexpr operator bool() const noexcept {
344  }
345 
352  constexpr bool valid() const noexcept {
353  return m_x >= -180 * precision()
354  && m_x <= 180 * precision()
355  && m_y >= -90 * precision()
356  && m_y <= 90 * precision();
357  }
358 
364  constexpr bool is_defined() const noexcept {
366  }
367 
373  constexpr bool is_undefined() const noexcept {
375  }
376 
377  constexpr int32_t x() const noexcept {
378  return m_x;
379  }
380 
381  constexpr int32_t y() const noexcept {
382  return m_y;
383  }
384 
385  Location& set_x(const int32_t x) noexcept {
386  m_x = x;
387  return *this;
388  }
389 
390  Location& set_y(const int32_t y) noexcept {
391  m_y = y;
392  return *this;
393  }
394 
400  double lon() const {
401  if (!valid()) {
402  throw osmium::invalid_location{"invalid location"};
403  }
404  return fix_to_double(m_x);
405  }
406 
410  double lon_without_check() const noexcept {
411  return fix_to_double(m_x);
412  }
413 
419  double lat() const {
420  if (!valid()) {
421  throw osmium::invalid_location{"invalid location"};
422  }
423  return fix_to_double(m_y);
424  }
425 
429  double lat_without_check() const noexcept {
430  return fix_to_double(m_y);
431  }
432 
433  Location& set_lon(double lon) noexcept {
434  m_x = double_to_fix(lon);
435  return *this;
436  }
437 
438  Location& set_lat(double lat) noexcept {
439  m_y = double_to_fix(lat);
440  return *this;
441  }
442 
443  Location& set_lon(const char* str) {
444  const char** data = &str;
445  const auto value = detail::string_to_location_coordinate(data);
446  if (**data != '\0') {
447  throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
448  }
449  m_x = value;
450  return *this;
451  }
452 
453  Location& set_lat(const char* str) {
454  const char** data = &str;
455  const auto value = detail::string_to_location_coordinate(data);
456  if (**data != '\0') {
457  throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
458  }
459  m_y = value;
460  return *this;
461  }
462 
463  Location& set_lon_partial(const char** str) {
464  m_x = detail::string_to_location_coordinate(str);
465  return *this;
466  }
467 
468  Location& set_lat_partial(const char** str) {
469  m_y = detail::string_to_location_coordinate(str);
470  return *this;
471  }
472 
473  template <typename T>
474  T as_string_without_check(T iterator, const char separator = ',') const {
475  iterator = detail::append_location_coordinate_to_string(iterator, x());
476  *iterator++ = separator;
477  return detail::append_location_coordinate_to_string(iterator, y());
478  }
479 
480  template <typename T>
481  T as_string(T iterator, const char separator = ',') const {
482  if (!valid()) {
483  throw osmium::invalid_location{"invalid location"};
484  }
485  return as_string_without_check(iterator, separator);
486  }
487 
488  }; // class Location
489 
493  inline constexpr bool operator==(const Location& lhs, const Location& rhs) noexcept {
494  return lhs.x() == rhs.x() && lhs.y() == rhs.y();
495  }
496 
497  inline constexpr bool operator!=(const Location& lhs, const Location& rhs) noexcept {
498  return !(lhs == rhs);
499  }
500 
506  inline constexpr bool operator<(const Location& lhs, const Location& rhs) noexcept {
507  return (lhs.x() == rhs.x() && lhs.y() < rhs.y()) || lhs.x() < rhs.x();
508  }
509 
510  inline constexpr bool operator>(const Location& lhs, const Location& rhs) noexcept {
511  return rhs < lhs;
512  }
513 
514  inline constexpr bool operator<=(const Location& lhs, const Location& rhs) noexcept {
515  return !(rhs < lhs);
516  }
517 
518  inline constexpr bool operator>=(const Location& lhs, const Location& rhs) noexcept {
519  return !(lhs < rhs);
520  }
521 
525  template <typename TChar, typename TTraits>
526  inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::Location& location) {
527  if (location) {
528  out << '(';
529  location.as_string(std::ostream_iterator<char>(out), ',');
530  out << ')';
531  } else {
532  out << "(undefined,undefined)";
533  }
534  return out;
535  }
536 
537  namespace detail {
538 
539  template <int N>
540  inline size_t hash(const osmium::Location& location) noexcept {
541  return static_cast<uint32_t>(location.x()) ^ static_cast<uint32_t>(location.y());
542  }
543 
544  template <>
545  inline size_t hash<8>(const osmium::Location& location) noexcept {
546  uint64_t h = location.x();
547  h <<= 32U;
548  return static_cast<size_t>(h ^ static_cast<uint64_t>(location.y()));
549  }
550 
551  } // namespace detail
552 
553 } // namespace osmium
554 
555 namespace std {
556 
557 // This pragma is a workaround for a bug in an old libc implementation
558 #ifdef __clang__
559 #pragma clang diagnostic push
560 #pragma clang diagnostic ignored "-Wmismatched-tags"
561 #endif
562  template <>
563  struct hash<osmium::Location> {
565  using result_type = size_t;
566  size_t operator()(const osmium::Location& location) const noexcept {
567  return osmium::detail::hash<sizeof(size_t)>(location);
568  }
569  };
570 #ifdef __clang__
571 #pragma clang diagnostic pop
572 #endif
573 
574 } // namespace std
575 
576 #endif // OSMIUM_OSM_LOCATION_HPP
Definition: location.hpp:271
double lon_without_check() const noexcept
Definition: location.hpp:410
int32_t m_x
Definition: location.hpp:273
constexpr Location(const int32_t x, const int32_t y) noexcept
Definition: location.hpp:311
Location & set_lat_partial(const char **str)
Definition: location.hpp:468
constexpr bool is_defined() const noexcept
Definition: location.hpp:364
double lon() const
Definition: location.hpp:400
static constexpr double fix_to_double(const int32_t c) noexcept
Definition: location.hpp:294
int32_t m_y
Definition: location.hpp:274
Location(const double lon, const double lat)
Definition: location.hpp:330
T as_string_without_check(T iterator, const char separator=',') const
Definition: location.hpp:474
constexpr bool is_undefined() const noexcept
Definition: location.hpp:373
static int32_t double_to_fix(const double c) noexcept
Definition: location.hpp:290
constexpr int32_t x() const noexcept
Definition: location.hpp:377
constexpr bool valid() const noexcept
Definition: location.hpp:352
Location & set_lat(double lat) noexcept
Definition: location.hpp:438
Location & set_y(const int32_t y) noexcept
Definition: location.hpp:390
Location & set_lat(const char *str)
Definition: location.hpp:453
constexpr Location() noexcept
Definition: location.hpp:301
Location & set_x(const int32_t x) noexcept
Definition: location.hpp:385
@ undefined_coordinate
Definition: location.hpp:287
Location & set_lon(double lon) noexcept
Definition: location.hpp:433
T as_string(T iterator, const char separator=',') const
Definition: location.hpp:481
constexpr static double precision() noexcept
Definition: location.hpp:276
constexpr Location(const int64_t x, const int64_t y) noexcept
Definition: location.hpp:321
double lat_without_check() const noexcept
Definition: location.hpp:429
double lat() const
Definition: location.hpp:419
Location & set_lon(const char *str)
Definition: location.hpp:443
constexpr int32_t y() const noexcept
Definition: location.hpp:381
Location & set_lon_partial(const char **str)
Definition: location.hpp:463
Definition: attr.hpp:342
Namespace for everything in the Osmium library.
Definition: assembler.hpp:53
bool operator==(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:442
bool operator<=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:461
bool operator>(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:457
bool operator>=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:465
bool operator!=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:446
std::basic_ostream< TChar, TTraits > & operator<<(std::basic_ostream< TChar, TTraits > &out, const item_type item_type)
Definition: item_type.hpp:187
bool operator<(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:453
Definition: location.hpp:555
Definition: location.hpp:53
invalid_location(const char *what)
Definition: location.hpp:59
invalid_location(const std::string &what)
Definition: location.hpp:55
size_t result_type
Definition: location.hpp:565
size_t operator()(const osmium::Location &location) const noexcept
Definition: location.hpp:566