Libosmium  2.11.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-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 <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  if (*str != '.') {
90  // there has to be at least one digit
91  if (*str >= '0' && *str <= '9') {
92  result = *str - '0';
93  ++str;
94  } else {
95  goto error;
96  }
97 
98  // optional additional digits before decimal point
99  while (*str >= '0' && *str <= '9' && max_digits > 0) {
100  result = result * 10 + (*str - '0');
101  ++str;
102  --max_digits;
103  }
104 
105  if (max_digits == 0) {
106  goto error;
107  }
108  } else {
109  // need at least one digit after decimal dot if there was no
110  // digit before decimal dot
111  if (*(str + 1) < '0' || *(str + 1) > '9') {
112  goto error;
113  }
114  }
115 
116  // optional decimal point
117  if (*str == '.') {
118  ++str;
119 
120  // read significant digits
121  for (; scale > 0 && *str >= '0' && *str <= '9'; --scale, ++str) {
122  result = result * 10 + (*str - '0');
123  }
124 
125  // ignore non-significant digits
126  max_digits = 20;
127  while (*str >= '0' && *str <= '9' && max_digits > 0) {
128  ++str;
129  --max_digits;
130  }
131 
132  if (max_digits == 0) {
133  goto error;
134  }
135  }
136 
137  // optional exponent in scientific notation
138  if (*str == 'e' || *str == 'E') {
139  ++str;
140 
141  int esign = 1;
142  // optional minus sign
143  if (*str == '-') {
144  esign = -1;
145  ++str;
146  }
147 
148  int64_t eresult = 0;
149 
150  // there has to be at least one digit in exponent
151  if (*str >= '0' && *str <= '9') {
152  eresult = *str - '0';
153  ++str;
154  } else {
155  goto error;
156  }
157 
158  // optional additional digits in exponent
159  max_digits = 5;
160  while (*str >= '0' && *str <= '9' && max_digits > 0) {
161  eresult = eresult * 10 + (*str - '0');
162  ++str;
163  --max_digits;
164  }
165 
166  if (max_digits == 0) {
167  goto error;
168  }
169 
170  scale += eresult * esign;
171  }
172 
173  if (scale < 0) {
174  for (; scale < 0 && result > 0; ++scale) {
175  result /= 10;
176  }
177  } else {
178  for (; scale > 0; --scale) {
179  result *= 10;
180  }
181  }
182 
183  result = (result + 5) / 10 * sign;
184 
185  if (result > std::numeric_limits<int32_t>::max() ||
186  result < std::numeric_limits<int32_t>::min()) {
187  goto error;
188  }
189 
190  *data = str;
191  return static_cast<int32_t>(result);
192 
193  error:
194 
195  throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
196  }
197 
198  // Convert integer as used by location for coordinates into a string.
199  template <typename T>
200  inline T append_location_coordinate_to_string(T iterator, int32_t value) {
201  // handle negative values
202  if (value < 0) {
203  *iterator++ = '-';
204  value = -value;
205  }
206 
207  // write digits into temporary buffer
208  int32_t v = value;
209  char temp[10];
210  char* t = temp;
211  do {
212  *t++ = char(v % 10) + '0';
213  v /= 10;
214  } while (v != 0);
215 
216  while (t-temp < 7) {
217  *t++ = '0';
218  }
219 
220  // write out digits before decimal point
221  if (value >= coordinate_precision) {
222  if (value >= 10 * coordinate_precision) {
223  if (value >= 100 * coordinate_precision) {
224  *iterator++ = *--t;
225  }
226  *iterator++ = *--t;
227  }
228  *iterator++ = *--t;
229  } else {
230  *iterator++ = '0';
231  }
232 
233  // remove trailing zeros
234  const char* tn = temp;
235  while (tn < t && *tn == '0') {
236  ++tn;
237  }
238 
239  // decimal point
240  if (t != tn) {
241  *iterator++ = '.';
242  while (t != tn) {
243  *iterator++ = *--t;
244  }
245  }
246 
247  return iterator;
248  }
249 
250  } // namespace detail
251 
266  class Location {
267 
268  int32_t m_x;
269  int32_t m_y;
270 
271  public:
272 
273  // this value is used for a coordinate to mark it as undefined
274  // MSVC doesn't declare std::numeric_limits<int32_t>::max() as
275  // constexpr, so we hard code this for the time being.
276  // static constexpr int32_t undefined_coordinate = std::numeric_limits<int32_t>::max();
277  static constexpr int32_t undefined_coordinate = 2147483647;
278 
279  static int32_t double_to_fix(const double c) noexcept {
280  return static_cast<int32_t>(std::round(c * detail::coordinate_precision));
281  }
282 
283  static constexpr double fix_to_double(const int32_t c) noexcept {
284  return static_cast<double>(c) / detail::coordinate_precision;
285  }
286 
290  explicit constexpr Location() noexcept :
291  m_x(undefined_coordinate),
292  m_y(undefined_coordinate) {
293  }
294 
300  constexpr Location(const int32_t x, const int32_t y) noexcept :
301  m_x(x),
302  m_y(y) {
303  }
304 
310  constexpr Location(const int64_t x, const int64_t y) noexcept :
311  m_x(static_cast<int32_t>(x)),
312  m_y(static_cast<int32_t>(y)) {
313  }
314 
318  Location(const double lon, const double lat) :
319  m_x(double_to_fix(lon)),
320  m_y(double_to_fix(lat)) {
321  }
322 
323  Location(const Location&) = default;
324  Location(Location&&) = default;
325  Location& operator=(const Location&) = default;
326  Location& operator=(Location&&) = default;
327  ~Location() = default;
328 
333  explicit constexpr operator bool() const noexcept {
334  return m_x != undefined_coordinate && m_y != undefined_coordinate;
335  }
336 
341  constexpr bool valid() const noexcept {
342  return m_x >= -180 * detail::coordinate_precision
343  && m_x <= 180 * detail::coordinate_precision
344  && m_y >= -90 * detail::coordinate_precision
345  && m_y <= 90 * detail::coordinate_precision;
346  }
347 
348  constexpr int32_t x() const noexcept {
349  return m_x;
350  }
351 
352  constexpr int32_t y() const noexcept {
353  return m_y;
354  }
355 
356  Location& set_x(const int32_t x) noexcept {
357  m_x = x;
358  return *this;
359  }
360 
361  Location& set_y(const int32_t y) noexcept {
362  m_y = y;
363  return *this;
364  }
365 
371  double lon() const {
372  if (!valid()) {
373  throw osmium::invalid_location("invalid location");
374  }
375  return fix_to_double(m_x);
376  }
377 
381  double lon_without_check() const {
382  return fix_to_double(m_x);
383  }
384 
390  double lat() const {
391  if (!valid()) {
392  throw osmium::invalid_location("invalid location");
393  }
394  return fix_to_double(m_y);
395  }
396 
400  double lat_without_check() const {
401  return fix_to_double(m_y);
402  }
403 
404  Location& set_lon(double lon) noexcept {
405  m_x = double_to_fix(lon);
406  return *this;
407  }
408 
409  Location& set_lat(double lat) noexcept {
410  m_y = double_to_fix(lat);
411  return *this;
412  }
413 
414  Location& set_lon(const char* str) {
415  const char** data = &str;
416  m_x = detail::string_to_location_coordinate(data);
417  if (**data != '\0') {
418  throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
419  }
420  return *this;
421  }
422 
423  Location& set_lat(const char* str) {
424  const char** data = &str;
425  m_y = detail::string_to_location_coordinate(data);
426  if (**data != '\0') {
427  throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
428  }
429  return *this;
430  }
431 
432  Location& set_lon_partial(const char** str) {
433  m_x = detail::string_to_location_coordinate(str);
434  return *this;
435  }
436 
437  Location& set_lat_partial(const char** str) {
438  m_y = detail::string_to_location_coordinate(str);
439  return *this;
440  }
441 
442  template <typename T>
443  T as_string_without_check(T iterator, const char separator = ',') const {
444  iterator = detail::append_location_coordinate_to_string(iterator, x());
445  *iterator++ = separator;
446  return detail::append_location_coordinate_to_string(iterator, y());
447  }
448 
449  template <typename T>
450  T as_string(T iterator, const char separator = ',') const {
451  if (!valid()) {
452  throw osmium::invalid_location("invalid location");
453  }
454  return as_string_without_check(iterator, separator);
455  }
456 
457  }; // class Location
458 
462  inline constexpr bool operator==(const Location& lhs, const Location& rhs) noexcept {
463  return lhs.x() == rhs.x() && lhs.y() == rhs.y();
464  }
465 
466  inline constexpr bool operator!=(const Location& lhs, const Location& rhs) noexcept {
467  return ! (lhs == rhs);
468  }
469 
475  inline constexpr bool operator<(const Location& lhs, const Location& rhs) noexcept {
476  return (lhs.x() == rhs.x() && lhs.y() < rhs.y()) || lhs.x() < rhs.x();
477  }
478 
479  inline constexpr bool operator>(const Location& lhs, const Location& rhs) noexcept {
480  return rhs < lhs;
481  }
482 
483  inline constexpr bool operator<=(const Location& lhs, const Location& rhs) noexcept {
484  return ! (rhs < lhs);
485  }
486 
487  inline constexpr bool operator>=(const Location& lhs, const Location& rhs) noexcept {
488  return ! (lhs < rhs);
489  }
490 
494  template <typename TChar, typename TTraits>
495  inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::Location& location) {
496  if (location) {
497  out << '(';
498  location.as_string(std::ostream_iterator<char>(out), ',');
499  out << ')';
500  } else {
501  out << "(undefined,undefined)";
502  }
503  return out;
504  }
505 
506  namespace detail {
507 
508  template <int N>
509  inline size_t hash(const osmium::Location& location) noexcept {
510  return location.x() ^ location.y();
511  }
512 
513  template <>
514  inline size_t hash<8>(const osmium::Location& location) noexcept {
515  uint64_t h = location.x();
516  h <<= 32;
517  return static_cast<size_t>(h ^ location.y());
518  }
519 
520  } // namespace detail
521 
522 } // namespace osmium
523 
524 namespace std {
525 
526 // This pragma is a workaround for a bug in an old libc implementation
527 #ifdef __clang__
528 #pragma clang diagnostic push
529 #pragma clang diagnostic ignored "-Wmismatched-tags"
530 #endif
531  template <>
532  struct hash<osmium::Location> {
534  using result_type = size_t;
535  size_t operator()(const osmium::Location& location) const noexcept {
536  return osmium::detail::hash<sizeof(size_t)>(location);
537  }
538  };
539 #ifdef __clang__
540 #pragma clang diagnostic pop
541 #endif
542 
543 } // namespace std
544 
545 #endif // OSMIUM_OSM_LOCATION_HPP
Location & set_lon(const char *str)
Definition: location.hpp:414
static int32_t double_to_fix(const double c) noexcept
Definition: location.hpp:279
double lon() const
Definition: location.hpp:371
bool operator<=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:455
Location & set_lat_partial(const char **str)
Definition: location.hpp:437
double lat_without_check() const
Definition: location.hpp:400
constexpr bool operator==(const Box &lhs, const Box &rhs) noexcept
Definition: box.hpp:221
Location & set_lon(double lon) noexcept
Definition: location.hpp:404
double lat() const
Definition: location.hpp:390
Definition: reader_iterator.hpp:39
constexpr Location(const int64_t x, const int64_t y) noexcept
Definition: location.hpp:310
constexpr Location(const int32_t x, const int32_t y) noexcept
Definition: location.hpp:300
constexpr bool valid() const noexcept
Definition: location.hpp:341
Location & set_lat(const char *str)
Definition: location.hpp:423
size_t operator()(const osmium::Location &location) const noexcept
Definition: location.hpp:535
bool operator<(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:447
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:269
constexpr int32_t y() const noexcept
Definition: location.hpp:352
invalid_location(const std::string &what)
Definition: location.hpp:54
Location & set_lat(double lat) noexcept
Definition: location.hpp:409
Location & set_y(const int32_t y) noexcept
Definition: location.hpp:361
Definition: location.hpp:266
int32_t m_x
Definition: location.hpp:268
Location & set_x(const int32_t x) noexcept
Definition: location.hpp:356
bool operator>=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:459
T as_string(T iterator, const char separator=',') const
Definition: location.hpp:450
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:443
invalid_location(const char *what)
Definition: location.hpp:58
constexpr int32_t x() const noexcept
Definition: location.hpp:348
double lon_without_check() const
Definition: location.hpp:381
static constexpr double fix_to_double(const int32_t c) noexcept
Definition: location.hpp:283
Location & set_lon_partial(const char **str)
Definition: location.hpp:432
Location(const double lon, const double lat)
Definition: location.hpp:318
bool operator!=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:440
size_t result_type
Definition: location.hpp:534
constexpr Location() noexcept
Definition: location.hpp:290