Libosmium 2.22.0 Fast and flexible C++ library for working with OpenStreetMap data

Loading...
Searching...
No Matches
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-2025 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 :
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
330 Location(const double lon, const double lat) :
331 m_x(double_to_fix(lon)),
332 m_y(double_to_fix(lat)) {
333 }
334
342 explicit constexpr operator bool() const noexcept {
343 return m_x != undefined_coordinate && m_y != undefined_coordinate;
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 {
365 return m_x != undefined_coordinate || m_y != undefined_coordinate;
366 }
367
373 constexpr bool is_undefined() const noexcept {
374 return m_x == undefined_coordinate && m_y == undefined_coordinate;
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 constexpr bool operator==(const Location& lhs, const Location& rhs) noexcept {
494 return lhs.x() == rhs.x() && lhs.y() == rhs.y();
495 }
496
497 constexpr bool operator!=(const Location& lhs, const Location& rhs) noexcept {
498 return !(lhs == rhs);
499 }
500
506 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 constexpr bool operator>(const Location& lhs, const Location& rhs) noexcept {
511 return rhs < lhs;
512 }
513
514 constexpr bool operator<=(const Location& lhs, const Location& rhs) noexcept {
515 return !(rhs < lhs);
516 }
517
518 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> {
564 using argument_type = 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
osmium::Location
Definition location.hpp:271
osmium::Location::lon_without_check
double lon_without_check() const noexcept
Definition location.hpp:410
osmium::Location::m_x
int32_t m_x
Definition location.hpp:273
osmium::Location::Location
constexpr Location(const int32_t x, const int32_t y) noexcept
Definition location.hpp:311
osmium::Location::is_defined
constexpr bool is_defined() const noexcept
Definition location.hpp:364
osmium::Location::lon
double lon() const
Definition location.hpp:400
osmium::Location::fix_to_double
static constexpr double fix_to_double(const int32_t c) noexcept
Definition location.hpp:294
osmium::Location::m_y
int32_t m_y
Definition location.hpp:274
osmium::Location::set_lat
Location & set_lat(const char *str)
Definition location.hpp:453
osmium::Location::Location
Location(const double lon, const double lat)
Definition location.hpp:330
osmium::Location::set_y
Location & set_y(const int32_t y) noexcept
Definition location.hpp:390
osmium::Location::as_string_without_check
T as_string_without_check(T iterator, const char separator=',') const
Definition location.hpp:474
osmium::Location::is_undefined
constexpr bool is_undefined() const noexcept
Definition location.hpp:373
osmium::Location::double_to_fix
static int32_t double_to_fix(const double c) noexcept
Definition location.hpp:290
osmium::Location::set_x
Location & set_x(const int32_t x) noexcept
Definition location.hpp:385
osmium::Location::set_lon
Location & set_lon(double lon) noexcept
Definition location.hpp:433
osmium::Location::x
constexpr int32_t x() const noexcept
Definition location.hpp:377
osmium::Location::valid
constexpr bool valid() const noexcept
Definition location.hpp:352
osmium::Location::Location
constexpr Location() noexcept
Definition location.hpp:301
osmium::Location::undefined_coordinate
@ undefined_coordinate
Definition location.hpp:287
osmium::Location::set_lat_partial
Location & set_lat_partial(const char **str)
Definition location.hpp:468
osmium::Location::as_string
T as_string(T iterator, const char separator=',') const
Definition location.hpp:481
osmium::Location::set_lon
Location & set_lon(const char *str)
Definition location.hpp:443
osmium::Location::Location
constexpr Location(const int64_t x, const int64_t y) noexcept
Definition location.hpp:321
osmium::Location::precision
static constexpr double precision() noexcept
Definition location.hpp:276
osmium::Location::lat_without_check
double lat_without_check() const noexcept
Definition location.hpp:429
osmium::Location::lat
double lat() const
Definition location.hpp:419
osmium::Location::set_lat
Location & set_lat(double lat) noexcept
Definition location.hpp:438
osmium::Location::y
constexpr int32_t y() const noexcept
Definition location.hpp:381
osmium::Location::set_lon_partial
Location & set_lon_partial(const char **str)
Definition location.hpp:463
detail
Definition attr.hpp:342
osmium
Namespace for everything in the Osmium library.
Definition assembler.hpp:53
osmium::operator==
bool operator==(const Changeset &lhs, const Changeset &rhs)
Definition changeset.hpp:440
osmium::operator<=
bool operator<=(const Changeset &lhs, const Changeset &rhs)
Definition changeset.hpp:459
osmium::operator>
bool operator>(const Changeset &lhs, const Changeset &rhs)
Definition changeset.hpp:455
osmium::operator>=
bool operator>=(const Changeset &lhs, const Changeset &rhs)
Definition changeset.hpp:463
osmium::operator!=
bool operator!=(const Changeset &lhs, const Changeset &rhs)
Definition changeset.hpp:444
osmium::operator<<
std::basic_ostream< TChar, TTraits > & operator<<(std::basic_ostream< TChar, TTraits > &out, const item_type item_type)
Definition item_type.hpp:187
osmium::operator<
bool operator<(const Changeset &lhs, const Changeset &rhs)
Definition changeset.hpp:451
std
Definition location.hpp:555
osmium::invalid_location
Definition location.hpp:53
osmium::invalid_location::invalid_location
invalid_location(const char *what)
Definition location.hpp:59
osmium::invalid_location::invalid_location
invalid_location(const std::string &what)
Definition location.hpp:55
std::hash< osmium::Location >::result_type
size_t result_type
Definition location.hpp:565
std::hash< osmium::Location >::operator()
size_t operator()(const osmium::Location &location) const noexcept
Definition location.hpp:566