Libosmium  2.13.1
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-2017 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  constexpr const int coordinate_precision = 10000000;
68 
69  // Convert string with a floating point number into integer suitable
70  // for use as coordinate in a Location.
71  inline int32_t string_to_location_coordinate(const char** data) {
72  const char* str = *data;
73  const char* full = str;
74 
75  int64_t result = 0;
76  int sign = 1;
77 
78  // one more than significant digits to allow rounding
79  int64_t scale = 8;
80 
81  // paranoia check for maximum number of digits
82  int max_digits = 10;
83 
84  // optional minus sign
85  if (*str == '-') {
86  sign = -1;
87  ++str;
88  }
89 
90  if (*str != '.') {
91  // there has to be at least one digit
92  if (*str >= '0' && *str <= '9') {
93  result = *str - '0';
94  ++str;
95  } else {
96  goto error;
97  }
98 
99  // optional additional digits before decimal point
100  while (*str >= '0' && *str <= '9' && max_digits > 0) {
101  result = result * 10 + (*str - '0');
102  ++str;
103  --max_digits;
104  }
105 
106  if (max_digits == 0) {
107  goto error;
108  }
109  } else {
110  // need at least one digit after decimal dot if there was no
111  // digit before decimal dot
112  if (*(str + 1) < '0' || *(str + 1) > '9') {
113  goto error;
114  }
115  }
116 
117  // optional decimal point
118  if (*str == '.') {
119  ++str;
120 
121  // read significant digits
122  for (; scale > 0 && *str >= '0' && *str <= '9'; --scale, ++str) {
123  result = result * 10 + (*str - '0');
124  }
125 
126  // ignore non-significant digits
127  max_digits = 20;
128  while (*str >= '0' && *str <= '9' && max_digits > 0) {
129  ++str;
130  --max_digits;
131  }
132 
133  if (max_digits == 0) {
134  goto error;
135  }
136  }
137 
138  // optional exponent in scientific notation
139  if (*str == 'e' || *str == 'E') {
140  ++str;
141 
142  int esign = 1;
143  // optional minus sign
144  if (*str == '-') {
145  esign = -1;
146  ++str;
147  }
148 
149  int64_t eresult = 0;
150 
151  // there has to be at least one digit in exponent
152  if (*str >= '0' && *str <= '9') {
153  eresult = *str - '0';
154  ++str;
155  } else {
156  goto error;
157  }
158 
159  // optional additional digits in exponent
160  max_digits = 5;
161  while (*str >= '0' && *str <= '9' && max_digits > 0) {
162  eresult = eresult * 10 + (*str - '0');
163  ++str;
164  --max_digits;
165  }
166 
167  if (max_digits == 0) {
168  goto error;
169  }
170 
171  scale += eresult * esign;
172  }
173 
174  if (scale < 0) {
175  for (; scale < 0 && result > 0; ++scale) {
176  result /= 10;
177  }
178  } else {
179  for (; scale > 0; --scale) {
180  result *= 10;
181  }
182  }
183 
184  result = (result + 5) / 10 * sign;
185 
186  if (result > std::numeric_limits<int32_t>::max() ||
187  result < std::numeric_limits<int32_t>::min()) {
188  goto error;
189  }
190 
191  *data = str;
192  return static_cast<int32_t>(result);
193 
194  error:
195 
196  throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
197  }
198 
199  // Convert integer as used by location for coordinates into a string.
200  template <typename T>
201  inline T append_location_coordinate_to_string(T iterator, int32_t value) {
202  // need to special-case this, because later `value = -value` would overflow.
203  if (value == std::numeric_limits<int32_t>::min()) {
204  static const char minresult[] = "-214.7483648";
205  return std::copy_n(minresult, sizeof(minresult) - 1, iterator);
206  }
207 
208  // handle negative values
209  if (value < 0) {
210  *iterator++ = '-';
211  value = -value;
212  }
213 
214  // write digits into temporary buffer
215  int32_t v = value;
216  char temp[10];
217  char* t = temp;
218  do {
219  *t++ = char(v % 10) + '0';
220  v /= 10;
221  } while (v != 0);
222 
223  while (t-temp < 7) {
224  *t++ = '0';
225  }
226 
227  // write out digits before decimal point
228  if (value >= coordinate_precision) {
229  if (value >= 10 * coordinate_precision) {
230  if (value >= 100 * coordinate_precision) {
231  *iterator++ = *--t;
232  }
233  *iterator++ = *--t;
234  }
235  *iterator++ = *--t;
236  } else {
237  *iterator++ = '0';
238  }
239 
240  // remove trailing zeros
241  const char* tn = temp;
242  while (tn < t && *tn == '0') {
243  ++tn;
244  }
245 
246  // decimal point
247  if (t != tn) {
248  *iterator++ = '.';
249  while (t != tn) {
250  *iterator++ = *--t;
251  }
252  }
253 
254  return iterator;
255  }
256 
257  } // namespace detail
258 
273  class Location {
274 
275  int32_t m_x;
276  int32_t m_y;
277 
278  public:
279 
280  // this value is used for a coordinate to mark it as undefined
281  // MSVC doesn't declare std::numeric_limits<int32_t>::max() as
282  // constexpr, so we hard code this for the time being.
283  // static constexpr int32_t undefined_coordinate = std::numeric_limits<int32_t>::max();
284  static constexpr int32_t undefined_coordinate = 2147483647;
285 
286  static int32_t double_to_fix(const double c) noexcept {
287  return static_cast<int32_t>(std::round(c * detail::coordinate_precision));
288  }
289 
290  static constexpr double fix_to_double(const int32_t c) noexcept {
291  return static_cast<double>(c) / detail::coordinate_precision;
292  }
293 
297  explicit constexpr Location() noexcept :
298  m_x(undefined_coordinate),
299  m_y(undefined_coordinate) {
300  }
301 
307  constexpr Location(const int32_t x, const int32_t y) noexcept :
308  m_x(x),
309  m_y(y) {
310  }
311 
317  constexpr Location(const int64_t x, const int64_t y) noexcept :
318  m_x(static_cast<int32_t>(x)),
319  m_y(static_cast<int32_t>(y)) {
320  }
321 
325  Location(const double lon, const double lat) :
326  m_x(double_to_fix(lon)),
327  m_y(double_to_fix(lat)) {
328  }
329 
330  Location(const Location&) = default;
331  Location(Location&&) = default;
332  Location& operator=(const Location&) = default;
333  Location& operator=(Location&&) = default;
334  ~Location() = default;
335 
343  explicit constexpr operator bool() const noexcept {
344  return m_x != undefined_coordinate && m_y != undefined_coordinate;
345  }
346 
353  constexpr bool valid() const noexcept {
354  return m_x >= -180 * detail::coordinate_precision
355  && m_x <= 180 * detail::coordinate_precision
356  && m_y >= -90 * detail::coordinate_precision
357  && m_y <= 90 * detail::coordinate_precision;
358  }
359 
365  constexpr bool is_defined() const noexcept {
366  return m_x != undefined_coordinate || m_y != undefined_coordinate;
367  }
368 
374  constexpr bool is_undefined() const noexcept {
375  return m_x == undefined_coordinate && m_y == undefined_coordinate;
376  }
377 
378  constexpr int32_t x() const noexcept {
379  return m_x;
380  }
381 
382  constexpr int32_t y() const noexcept {
383  return m_y;
384  }
385 
386  Location& set_x(const int32_t x) noexcept {
387  m_x = x;
388  return *this;
389  }
390 
391  Location& set_y(const int32_t y) noexcept {
392  m_y = y;
393  return *this;
394  }
395 
401  double lon() const {
402  if (!valid()) {
403  throw osmium::invalid_location{"invalid location"};
404  }
405  return fix_to_double(m_x);
406  }
407 
411  double lon_without_check() const {
412  return fix_to_double(m_x);
413  }
414 
420  double lat() const {
421  if (!valid()) {
422  throw osmium::invalid_location{"invalid location"};
423  }
424  return fix_to_double(m_y);
425  }
426 
430  double lat_without_check() const {
431  return fix_to_double(m_y);
432  }
433 
434  Location& set_lon(double lon) noexcept {
435  m_x = double_to_fix(lon);
436  return *this;
437  }
438 
439  Location& set_lat(double lat) noexcept {
440  m_y = double_to_fix(lat);
441  return *this;
442  }
443 
444  Location& set_lon(const char* str) {
445  const char** data = &str;
446  m_x = detail::string_to_location_coordinate(data);
447  if (**data != '\0') {
448  throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
449  }
450  return *this;
451  }
452 
453  Location& set_lat(const char* str) {
454  const char** data = &str;
455  m_y = detail::string_to_location_coordinate(data);
456  if (**data != '\0') {
457  throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
458  }
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 location.x() ^ 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 <<= 32;
547  return static_cast<size_t>(h ^ 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:444
static int32_t double_to_fix(const double c) noexcept
Definition: location.hpp:286
double lon() const
Definition: location.hpp:401
bool operator<=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:455
Location & set_lat_partial(const char **str)
Definition: location.hpp:467
double lat_without_check() const
Definition: location.hpp:430
constexpr bool operator==(const Box &lhs, const Box &rhs) noexcept
Definition: box.hpp:221
Location & set_lon(double lon) noexcept
Definition: location.hpp:434
double lat() const
Definition: location.hpp:420
Definition: reader_iterator.hpp:39
constexpr Location(const int64_t x, const int64_t y) noexcept
Definition: location.hpp:317
constexpr Location(const int32_t x, const int32_t y) noexcept
Definition: location.hpp:307
constexpr bool valid() const noexcept
Definition: location.hpp:353
Location & set_lat(const char *str)
Definition: location.hpp:453
size_t operator()(const osmium::Location &location) const noexcept
Definition: location.hpp:565
bool operator<(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:447
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:276
constexpr int32_t y() const noexcept
Definition: location.hpp:382
invalid_location(const std::string &what)
Definition: location.hpp:55
Location & set_lat(double lat) noexcept
Definition: location.hpp:439
Location & set_y(const int32_t y) noexcept
Definition: location.hpp:391
Definition: location.hpp:273
int32_t m_x
Definition: location.hpp:275
Location & set_x(const int32_t x) noexcept
Definition: location.hpp:386
bool operator>=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:459
T as_string(T iterator, const char separator=',') const
Definition: location.hpp:480
bool operator>(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:451
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:378
double lon_without_check() const
Definition: location.hpp:411
static constexpr double fix_to_double(const int32_t c) noexcept
Definition: location.hpp:290
constexpr bool is_defined() const noexcept
Definition: location.hpp:365
Location & set_lon_partial(const char **str)
Definition: location.hpp:462
Location(const double lon, const double lat)
Definition: location.hpp:325
bool operator!=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:440
constexpr bool is_undefined() const noexcept
Definition: location.hpp:374
size_t result_type
Definition: location.hpp:564
constexpr Location() noexcept
Definition: location.hpp:297