Libosmium: include/osmium/osm/location.hpp Source File

Libosmium 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
6This file is part of Osmium (https://osmcode.org/libosmium).
7
8Copyright 2013-2026 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 <cassert>
38#include <cmath>
39#include <cstdint>
40#include <cstring>
41#include <functional>
42#include <iosfwd>
43#include <iterator>
44#include <limits>
45#include <stdexcept>
46#include <string>
47
48namespace osmium {
49
54 struct invalid_location : public std::range_error {
55
56 explicit invalid_location(const std::string& what) :
57 std::range_error(what) {
58 }
59
60 explicit invalid_location(const char* what) :
61 std::range_error(what) {
62 }
63
64 }; // struct invalid_location
65
66 namespace detail {
67
68 enum {
69 coordinate_precision = 10000000
70 };
71
72 // Convert string with a floating point number into integer suitable
73 // for use as coordinate in a Location.
74 inline int32_t string_to_location_coordinate(const char** data) {
75 const char* str = *data;
76 const char* full = str;
77
78 int64_t result = 0;
79 int sign = 1;
80
81 // one more than significant digits to allow rounding
82 int64_t scale = 8;
83
84 // paranoia check for maximum number of digits
85 int max_digits = 10;
86
87 // optional minus sign
88 if (*str == '-') {
89 sign = -1;
90 ++str;
91 }
92
93 if (*str != '.') {
94 // there has to be at least one digit
95 if (*str >= '0' && *str <= '9') {
96 result = *str - '0';
97 ++str;
98 } else {
99 throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
100 }
101
102 // optional additional digits before decimal point
103 while (*str >= '0' && *str <= '9' && max_digits > 0) {
104 result = result * 10 + (*str - '0');
105 ++str;
106 --max_digits;
107 }
108
109 if (max_digits == 0) {
110 throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
111 }
112 } else {
113 // need at least one digit after decimal dot if there was no
114 // digit before decimal dot
115 if (*(str + 1) < '0' || *(str + 1) > '9') {
116 throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
117 }
118 }
119
120 // optional decimal point
121 if (*str == '.') {
122 ++str;
123
124 // read significant digits
125 for (; scale > 0 && *str >= '0' && *str <= '9'; --scale, ++str) {
126 result = result * 10 + (*str - '0');
127 }
128
129 // ignore non-significant digits
130 max_digits = 20;
131 while (*str >= '0' && *str <= '9' && max_digits > 0) {
132 ++str;
133 --max_digits;
134 }
135
136 if (max_digits == 0) {
137 throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
138 }
139 }
140
141 // optional exponent in scientific notation
142 if (*str == 'e' || *str == 'E') {
143 ++str;
144
145 int esign = 1;
146 // optional minus sign
147 if (*str == '-') {
148 esign = -1;
149 ++str;
150 }
151
152 int64_t eresult = 0;
153
154 // there has to be at least one digit in exponent
155 if (*str >= '0' && *str <= '9') {
156 eresult = *str - '0';
157 ++str;
158 } else {
159 throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
160 }
161
162 // optional additional digits in exponent
163 max_digits = 5;
164 while (*str >= '0' && *str <= '9' && max_digits > 0) {
165 eresult = eresult * 10 + (*str - '0');
166 ++str;
167 --max_digits;
168 }
169
170 if (max_digits == 0) {
171 throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
172 }
173
174 scale += eresult * esign;
175 }
176
177 if (scale < 0) {
178 for (; scale < 0 && result > 0; ++scale) {
179 result /= 10;
180 }
181 } else {
182 for (; scale > 0; --scale) {
183 result *= 10;
184 }
185 }
186
187 result = (result + 5) / 10 * sign;
188
189 if (result > std::numeric_limits<int32_t>::max() ||
190 result < std::numeric_limits<int32_t>::min()) {
191 throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
192 }
193
194 *data = str;
195 return static_cast<int32_t>(result);
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 // need to special-case this, because later `value = -value` would overflow.
202 if (value == std::numeric_limits<int32_t>::min()) {
203 static const char minresult[] = "-214.7483648";
204 return std::copy_n(minresult, sizeof(minresult) - 1, iterator);
205 }
206
207 // handle negative values
208 if (value < 0) {
209 *iterator++ = '-';
210 value = -value;
211 }
212
213 // write digits into temporary buffer
214 int32_t v = value;
215 char temp[10];
216 char* t = temp;
217 do {
218 *t++ = static_cast<char>(v % 10) + '0'; // NOLINT(bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions)
219 v /= 10;
220 } while (v != 0);
221
222 while (t - temp < 7) {
223 *t++ = '0';
224 }
225
226 // write out digits before decimal point
227 if (value >= coordinate_precision) {
228 if (value >= 10 * coordinate_precision) {
229 if (value >= 100 * coordinate_precision) {
230 *iterator++ = *--t;
231 }
232 *iterator++ = *--t;
233 }
234 *iterator++ = *--t;
235 } else {
236 *iterator++ = '0';
237 }
238
239 // remove trailing zeros
240 const char* tn = temp;
241 while (tn < t && *tn == '0') {
242 ++tn;
243 }
244
245 // decimal point
246 if (t != tn) {
247 *iterator++ = '.';
248 while (t != tn) {
249 *iterator++ = *--t;
250 }
251 }
252
253 return iterator;
254 }
255
256 } // namespace detail
257
279 class Location {
280
281 int32_t m_x; // NOLINT(modernize-use-default-member-init)
282 int32_t m_y; // NOLINT(modernize-use-default-member-init)
283
284 constexpr static double precision() noexcept {
285 return static_cast<double>(detail::coordinate_precision);
286 }
287
288#ifdef OSMIUM_LOCATION_WITH_EXTRA_BIT
289
294 enum {
295 mask_b30 = 1UL << 30UL
296 };
297
298 static constexpr int32_t remove_extra_bit(int32_t v) noexcept {
299 if (v == undefined_coordinate) {
300 return v;
301 }
302 auto const value = static_cast<uint32_t>(v);
303 auto const n30 = value & ~mask_b30;
304 auto const b30 = (value >> 1UL) & mask_b30;
305 return static_cast<int32_t>(n30 | b30);
306 }
307#else
308 static constexpr int32_t remove_extra_bit(int32_t v) noexcept {
309 return v;
310 }
311#endif
312
313 public:
314
315 // this value is used for a coordinate to mark it as undefined
316 // MSVC doesn't declare std::numeric_limits<int32_t>::max() as
317 // constexpr, so we hard code this for the time being.
318 // undefined_coordinate = std::numeric_limits<int32_t>::max();
319 enum {
320 undefined_coordinate = 2147483647
321 };
322
323 static int32_t double_to_fix(const double c) noexcept {
324 return static_cast<int32_t>(std::round(c * precision()));
325 }
326
327 static constexpr double fix_to_double(const int32_t c) noexcept {
328 return static_cast<double>(c) / precision();
329 }
330
334 explicit constexpr Location() noexcept :
335 m_x(undefined_coordinate),
336 m_y(undefined_coordinate) {
337 }
338
344 constexpr Location(const int32_t x, const int32_t y) noexcept :
345 m_x(x),
346 m_y(remove_extra_bit(y)) {
347 }
348
354 constexpr Location(const int64_t x, const int64_t y) noexcept :
355 m_x(static_cast<int32_t>(x)),
356 m_y(remove_extra_bit(static_cast<int32_t>(y))) {
357 }
358
363 Location(const double lon, const double lat) :
364 m_x(double_to_fix(lon)),
365 m_y(remove_extra_bit(double_to_fix(lat))) {
366 }
367
375 explicit constexpr operator bool() const noexcept {
376 return m_x != undefined_coordinate && m_y != undefined_coordinate;
377 }
378
385 constexpr bool valid() const noexcept {
386 return x() >= -180 * precision()
387 && x() <= 180 * precision()
388 && y() >= -90 * precision()
389 && y() <= 90 * precision();
390 }
391
397 constexpr bool is_defined() const noexcept {
398 return m_x != undefined_coordinate || m_y != undefined_coordinate;
399 }
400
406 constexpr bool is_undefined() const noexcept {
407 return m_x == undefined_coordinate && m_y == undefined_coordinate;
408 }
409
410 constexpr int32_t x() const noexcept {
411 return m_x;
412 }
413
414 constexpr int32_t y() const noexcept {
415 return remove_extra_bit(m_y);
416 }
417
418 Location& set_x(const int32_t x) noexcept {
419 m_x = x;
420 return *this;
421 }
422
428 Location& set_y(const int32_t y) noexcept {
429 m_y = remove_extra_bit(y);
430 return *this;
431 }
432
438 double lon() const {
439 if (!valid()) {
440 throw osmium::invalid_location{"invalid location"};
441 }
442 return fix_to_double(x());
443 }
444
448 double lon_without_check() const noexcept {
449 return fix_to_double(x());
450 }
451
457 double lat() const {
458 if (!valid()) {
459 throw osmium::invalid_location{"invalid location"};
460 }
461 return fix_to_double(y());
462 }
463
467 double lat_without_check() const noexcept {
468 return fix_to_double(y());
469 }
470
471 Location& set_lon(double lon) noexcept {
472 return set_x(double_to_fix(lon));
473 }
474
475 Location& set_lat(double lat) noexcept {
476 return set_y(double_to_fix(lat));
477 }
478
479 Location& set_lon(const char* str) {
480 const char** data = &str;
481 const auto value = detail::string_to_location_coordinate(data);
482 if (**data != '\0') {
483 throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
484 }
485 return set_x(value);
486 }
487
488 Location& set_lat(const char* str) {
489 const char** data = &str;
490 const auto value = detail::string_to_location_coordinate(data);
491 if (**data != '\0') {
492 throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
493 }
494 return set_y(value);
495 }
496
497 Location& set_lon_partial(const char** str) {
498 return set_x(detail::string_to_location_coordinate(str));
499 }
500
501 Location& set_lat_partial(const char** str) {
502 return set_y(detail::string_to_location_coordinate(str));
503 }
504
505 template <typename T>
506 T as_string_without_check(T iterator, const char separator = ',') const {
507 iterator = detail::append_location_coordinate_to_string(iterator, x());
508 *iterator++ = separator;
509 return detail::append_location_coordinate_to_string(iterator, y());
510 }
511
512 template <typename T>
513 T as_string(T iterator, const char separator = ',') const {
514 if (!valid()) {
515 throw osmium::invalid_location{"invalid location"};
516 }
517 return as_string_without_check(iterator, separator);
518 }
519
520#ifdef OSMIUM_LOCATION_WITH_EXTRA_BIT
524 constexpr bool get_extra_bit() const noexcept {
525 assert(m_y != undefined_coordinate);
526
527 auto const b = static_cast<uint32_t>(m_y);
528 auto const b31 = b >> 31UL;
529 auto const b30 = (b >> 30UL) & 1UL;
530 return b30 ^ b31;
531 }
532
538 constexpr void set_extra_bit() noexcept {
539 assert(m_y != undefined_coordinate);
540
541 auto const b = static_cast<uint32_t>(m_y);
542 auto const n30 = b & ~mask_b30;
543 auto const b30 = (~b >> 1UL) & mask_b30;
544 m_y = static_cast<int32_t>(n30 | b30);
545 }
546
552 constexpr void set_extra_bit(bool value) noexcept {
553 assert(m_y != undefined_coordinate);
554
555 auto const b = static_cast<uint32_t>(m_y);
556 auto const n30 = b & ~mask_b30;
557 auto const b30 = ((b >> 31UL) ^ value) << 30UL;
558 m_y = static_cast<int32_t>(n30 | b30);
559 }
560
566 constexpr void clear_extra_bit() noexcept {
567 assert(m_y != undefined_coordinate);
568
569 m_y = y();
570 }
571
577 constexpr void toggle_extra_bit() noexcept {
578 assert(m_y != undefined_coordinate);
579
580 auto const b = static_cast<uint32_t>(m_y);
581 auto const n30 = b & ~mask_b30;
582 auto const b30 = ~b & mask_b30;
583 m_y = static_cast<int32_t>(n30 | b30);
584 }
585#endif
586
587 }; // class Location
588
592 constexpr bool operator==(const Location& lhs, const Location& rhs) noexcept {
593 return lhs.x() == rhs.x() && lhs.y() == rhs.y();
594 }
595
596 constexpr bool operator!=(const Location& lhs, const Location& rhs) noexcept {
597 return !(lhs == rhs);
598 }
599
605 constexpr bool operator<(const Location& lhs, const Location& rhs) noexcept {
606 return (lhs.x() == rhs.x() && lhs.y() < rhs.y()) || lhs.x() < rhs.x();
607 }
608
609 constexpr bool operator>(const Location& lhs, const Location& rhs) noexcept {
610 return rhs < lhs;
611 }
612
613 constexpr bool operator<=(const Location& lhs, const Location& rhs) noexcept {
614 return !(rhs < lhs);
615 }
616
617 constexpr bool operator>=(const Location& lhs, const Location& rhs) noexcept {
618 return !(lhs < rhs);
619 }
620
624 template <typename TChar, typename TTraits>
625 inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::Location& location) {
626 if (location) {
627 out << '(';
628 location.as_string(std::ostream_iterator<char>(out), ',');
629 out << ')';
630 } else {
631 out << "(undefined,undefined)";
632 }
633 return out;
634 }
635
636 namespace detail {
637
638 template <int N>
639 inline size_t hash(const osmium::Location& location) noexcept {
640 return static_cast<uint32_t>(location.x()) ^ static_cast<uint32_t>(location.y());
641 }
642
643 template <>
644 inline size_t hash<8>(const osmium::Location& location) noexcept {
645 uint64_t h = location.x();
646 h <<= 32U;
647 return static_cast<size_t>(h ^ static_cast<uint64_t>(location.y()));
648 }
649
650 } // namespace detail
651
652} // namespace osmium
653
654 namespace std {
655
656// This pragma is a workaround for a bug in an old libc implementation
657#ifdef __clang__
658#pragma clang diagnostic push
659#pragma clang diagnostic ignored "-Wmismatched-tags"
660#endif
661 template <>
662 struct hash<osmium::Location> {
663 using argument_type = osmium::Location;
664 using result_type = size_t;
665 size_t operator()(const osmium::Location& location) const noexcept {
666 return osmium::detail::hash<sizeof(size_t)>(location);
667 }
668 };
669#ifdef __clang__
670#pragma clang diagnostic pop
671#endif
672
673} // namespace std
674
675#endif // OSMIUM_OSM_LOCATION_HPP
osmium::Location
Definition location.hpp:279
osmium::Location::lon_without_check
double lon_without_check() const noexcept
Definition location.hpp:448
osmium::Location::m_x
int32_t m_x
Definition location.hpp:281
osmium::Location::Location
constexpr Location(const int32_t x, const int32_t y) noexcept
Definition location.hpp:344
osmium::Location::is_defined
constexpr bool is_defined() const noexcept
Definition location.hpp:397
osmium::Location::lon
double lon() const
Definition location.hpp:438
osmium::Location::fix_to_double
static constexpr double fix_to_double(const int32_t c) noexcept
Definition location.hpp:327
osmium::Location::m_y
int32_t m_y
Definition location.hpp:282
osmium::Location::set_lat
Location & set_lat(const char *str)
Definition location.hpp:488
osmium::Location::Location
Location(const double lon, const double lat)
Definition location.hpp:363
osmium::Location::set_y
Location & set_y(const int32_t y) noexcept
Definition location.hpp:428
osmium::Location::as_string_without_check
T as_string_without_check(T iterator, const char separator=',') const
Definition location.hpp:506
osmium::Location::is_undefined
constexpr bool is_undefined() const noexcept
Definition location.hpp:406
osmium::Location::double_to_fix
static int32_t double_to_fix(const double c) noexcept
Definition location.hpp:323
osmium::Location::set_x
Location & set_x(const int32_t x) noexcept
Definition location.hpp:418
osmium::Location::set_lon
Location & set_lon(double lon) noexcept
Definition location.hpp:471
osmium::Location::remove_extra_bit
static constexpr int32_t remove_extra_bit(int32_t v) noexcept
Definition location.hpp:308
osmium::Location::x
constexpr int32_t x() const noexcept
Definition location.hpp:410
osmium::Location::valid
constexpr bool valid() const noexcept
Definition location.hpp:385
osmium::Location::Location
constexpr Location() noexcept
Definition location.hpp:334
osmium::Location::set_lat_partial
Location & set_lat_partial(const char **str)
Definition location.hpp:501
osmium::Location::as_string
T as_string(T iterator, const char separator=',') const
Definition location.hpp:513
osmium::Location::set_lon
Location & set_lon(const char *str)
Definition location.hpp:479
osmium::Location::undefined_coordinate
@ undefined_coordinate
Definition location.hpp:320
osmium::Location::Location
constexpr Location(const int64_t x, const int64_t y) noexcept
Definition location.hpp:354
osmium::Location::precision
static constexpr double precision() noexcept
Definition location.hpp:284
osmium::Location::lat_without_check
double lat_without_check() const noexcept
Definition location.hpp:467
osmium::Location::lat
double lat() const
Definition location.hpp:457
osmium::Location::set_lat
Location & set_lat(double lat) noexcept
Definition location.hpp:475
osmium::Location::y
constexpr int32_t y() const noexcept
Definition location.hpp:414
osmium::Location::set_lon_partial
Location & set_lon_partial(const char **str)
Definition location.hpp:497
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:654
osmium::invalid_location
Definition location.hpp:54
osmium::invalid_location::invalid_location
invalid_location(const char *what)
Definition location.hpp:60
osmium::invalid_location::invalid_location
invalid_location(const std::string &what)
Definition location.hpp:56
std::hash< osmium::Location >::result_type
size_t result_type
Definition location.hpp:664
std::hash< osmium::Location >::argument_type
osmium::Location argument_type
Definition location.hpp:663
std::hash< osmium::Location >::operator()
size_t operator()(const osmium::Location &location) const noexcept
Definition location.hpp:665