Libosmium  2.20.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
6This file is part of Osmium (https://osmcode.org/libosmium).
7
8Copyright 2013-2023 Jochen Topf <jochen@topf.org> and others (see README).
9
10Boost Software License - Version 1.0 - August 17th, 2003
11
12Permission is hereby granted, free of charge, to any person or organization
13obtaining a copy of the software and accompanying documentation covered by
14this license (the "Software") to use, reproduce, display, distribute,
15execute, and transmit the Software, and to prepare derivative works of the
16Software, and to permit third-parties to whom the Software is furnished to
17do so, all subject to the following:
18
19The copyright notices in the Software and this entire statement, including
20the above license grant, this restriction and the following disclaimer,
21must be included in all copies of the Software, in whole or in part, and
22all derivative works of the Software, unless such copies or derivative
23works are solely in the form of machine-executable object code generated by
24a source language processor.
25
26THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
29SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
30FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
31ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32DEALINGS 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
47namespace 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) :
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 {
435 return *this;
436 }
437
438 Location& set_lat(double lat) noexcept {
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
555namespace 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
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 & set_lat(const char *str)
Definition: location.hpp:453
Location(const double lon, const double lat)
Definition: location.hpp:330
Location & set_y(const int32_t y) noexcept
Definition: location.hpp:390
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
Location & set_x(const int32_t x) noexcept
Definition: location.hpp:385
Location & set_lon(double lon) noexcept
Definition: location.hpp:433
constexpr int32_t x() const noexcept
Definition: location.hpp:377
constexpr bool valid() const noexcept
Definition: location.hpp:352
constexpr Location() noexcept
Definition: location.hpp:301
@ undefined_coordinate
Definition: location.hpp:287
Location & set_lat_partial(const char **str)
Definition: location.hpp:468
T as_string(T iterator, const char separator=',') const
Definition: location.hpp:481
Location & set_lon(const char *str)
Definition: location.hpp:443
constexpr Location(const int64_t x, const int64_t y) noexcept
Definition: location.hpp:321
static constexpr double precision() noexcept
Definition: location.hpp:276
double lat_without_check() const noexcept
Definition: location.hpp:429
double lat() const
Definition: location.hpp:419
Location & set_lat(double lat) noexcept
Definition: location.hpp:438
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:440
bool operator<=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:459
bool operator>(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:455
bool operator>=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:463
bool operator!=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:444
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:451
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