Libosmium  2.17.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
6This file is part of Osmium (https://osmcode.org/libosmium).
7
8Copyright 2013-2021 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 public:
277
278 // this value is used for a coordinate to mark it as undefined
279 // MSVC doesn't declare std::numeric_limits<int32_t>::max() as
280 // constexpr, so we hard code this for the time being.
281 // undefined_coordinate = std::numeric_limits<int32_t>::max();
282 enum {
283 undefined_coordinate = 2147483647
284 };
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 :
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
326 Location(const double lon, const double lat) :
329 }
330
338 explicit constexpr operator bool() const noexcept {
340 }
341
348 constexpr bool valid() const noexcept {
349 return m_x >= -180 * detail::coordinate_precision
350 && m_x <= 180 * detail::coordinate_precision
351 && m_y >= -90 * detail::coordinate_precision
352 && m_y <= 90 * detail::coordinate_precision;
353 }
354
360 constexpr bool is_defined() const noexcept {
362 }
363
369 constexpr bool is_undefined() const noexcept {
371 }
372
373 constexpr int32_t x() const noexcept {
374 return m_x;
375 }
376
377 constexpr int32_t y() const noexcept {
378 return m_y;
379 }
380
381 Location& set_x(const int32_t x) noexcept {
382 m_x = x;
383 return *this;
384 }
385
386 Location& set_y(const int32_t y) noexcept {
387 m_y = y;
388 return *this;
389 }
390
396 double lon() const {
397 if (!valid()) {
398 throw osmium::invalid_location{"invalid location"};
399 }
400 return fix_to_double(m_x);
401 }
402
406 double lon_without_check() const noexcept {
407 return fix_to_double(m_x);
408 }
409
415 double lat() const {
416 if (!valid()) {
417 throw osmium::invalid_location{"invalid location"};
418 }
419 return fix_to_double(m_y);
420 }
421
425 double lat_without_check() const noexcept {
426 return fix_to_double(m_y);
427 }
428
429 Location& set_lon(double lon) noexcept {
431 return *this;
432 }
433
434 Location& set_lat(double lat) noexcept {
436 return *this;
437 }
438
439 Location& set_lon(const char* str) {
440 const char** data = &str;
441 const auto value = detail::string_to_location_coordinate(data);
442 if (**data != '\0') {
443 throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
444 }
445 m_x = value;
446 return *this;
447 }
448
449 Location& set_lat(const char* str) {
450 const char** data = &str;
451 const auto value = detail::string_to_location_coordinate(data);
452 if (**data != '\0') {
453 throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
454 }
455 m_y = value;
456 return *this;
457 }
458
459 Location& set_lon_partial(const char** str) {
460 m_x = detail::string_to_location_coordinate(str);
461 return *this;
462 }
463
464 Location& set_lat_partial(const char** str) {
465 m_y = detail::string_to_location_coordinate(str);
466 return *this;
467 }
468
469 template <typename T>
470 T as_string_without_check(T iterator, const char separator = ',') const {
471 iterator = detail::append_location_coordinate_to_string(iterator, x());
472 *iterator++ = separator;
473 return detail::append_location_coordinate_to_string(iterator, y());
474 }
475
476 template <typename T>
477 T as_string(T iterator, const char separator = ',') const {
478 if (!valid()) {
479 throw osmium::invalid_location{"invalid location"};
480 }
481 return as_string_without_check(iterator, separator);
482 }
483
484 }; // class Location
485
489 inline constexpr bool operator==(const Location& lhs, const Location& rhs) noexcept {
490 return lhs.x() == rhs.x() && lhs.y() == rhs.y();
491 }
492
493 inline constexpr bool operator!=(const Location& lhs, const Location& rhs) noexcept {
494 return !(lhs == rhs);
495 }
496
502 inline constexpr bool operator<(const Location& lhs, const Location& rhs) noexcept {
503 return (lhs.x() == rhs.x() && lhs.y() < rhs.y()) || lhs.x() < rhs.x();
504 }
505
506 inline constexpr bool operator>(const Location& lhs, const Location& rhs) noexcept {
507 return rhs < lhs;
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 !(lhs < rhs);
516 }
517
521 template <typename TChar, typename TTraits>
522 inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::Location& location) {
523 if (location) {
524 out << '(';
525 location.as_string(std::ostream_iterator<char>(out), ',');
526 out << ')';
527 } else {
528 out << "(undefined,undefined)";
529 }
530 return out;
531 }
532
533 namespace detail {
534
535 template <int N>
536 inline size_t hash(const osmium::Location& location) noexcept {
537 return static_cast<uint32_t>(location.x()) ^ static_cast<uint32_t>(location.y());
538 }
539
540 template <>
541 inline size_t hash<8>(const osmium::Location& location) noexcept {
542 uint64_t h = location.x();
543 h <<= 32U;
544 return static_cast<size_t>(h ^ static_cast<uint64_t>(location.y()));
545 }
546
547 } // namespace detail
548
549} // namespace osmium
550
551namespace std {
552
553// This pragma is a workaround for a bug in an old libc implementation
554#ifdef __clang__
555#pragma clang diagnostic push
556#pragma clang diagnostic ignored "-Wmismatched-tags"
557#endif
558 template <>
559 struct hash<osmium::Location> {
561 using result_type = size_t;
562 size_t operator()(const osmium::Location& location) const noexcept {
563 return osmium::detail::hash<sizeof(size_t)>(location);
564 }
565 };
566#ifdef __clang__
567#pragma clang diagnostic pop
568#endif
569
570} // namespace std
571
572#endif // OSMIUM_OSM_LOCATION_HPP
Definition: location.hpp:271
double lon_without_check() const noexcept
Definition: location.hpp:406
int32_t m_x
Definition: location.hpp:273
constexpr Location(const int32_t x, const int32_t y) noexcept
Definition: location.hpp:307
constexpr bool is_defined() const noexcept
Definition: location.hpp:360
double lon() const
Definition: location.hpp:396
static constexpr double fix_to_double(const int32_t c) noexcept
Definition: location.hpp:290
int32_t m_y
Definition: location.hpp:274
Location & set_lat(const char *str)
Definition: location.hpp:449
Location(const double lon, const double lat)
Definition: location.hpp:326
Location & set_y(const int32_t y) noexcept
Definition: location.hpp:386
T as_string_without_check(T iterator, const char separator=',') const
Definition: location.hpp:470
constexpr bool is_undefined() const noexcept
Definition: location.hpp:369
static int32_t double_to_fix(const double c) noexcept
Definition: location.hpp:286
Location & set_x(const int32_t x) noexcept
Definition: location.hpp:381
Location & set_lon(double lon) noexcept
Definition: location.hpp:429
constexpr int32_t x() const noexcept
Definition: location.hpp:373
constexpr bool valid() const noexcept
Definition: location.hpp:348
constexpr Location() noexcept
Definition: location.hpp:297
@ undefined_coordinate
Definition: location.hpp:283
Location & set_lat_partial(const char **str)
Definition: location.hpp:464
T as_string(T iterator, const char separator=',') const
Definition: location.hpp:477
Location & set_lon(const char *str)
Definition: location.hpp:439
constexpr Location(const int64_t x, const int64_t y) noexcept
Definition: location.hpp:317
double lat_without_check() const noexcept
Definition: location.hpp:425
double lat() const
Definition: location.hpp:415
Location & set_lat(double lat) noexcept
Definition: location.hpp:434
constexpr int32_t y() const noexcept
Definition: location.hpp:377
Location & set_lon_partial(const char **str)
Definition: location.hpp:459
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:442
bool operator<=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:461
bool operator>(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:457
bool operator>=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:465
bool operator!=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:446
std::basic_ostream< TChar, TTraits > & operator<<(std::basic_ostream< TChar, TTraits > &out, const item_type item_type)
Definition: item_type.hpp:185
bool operator<(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:453
Definition: location.hpp:551
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:561
size_t operator()(const osmium::Location &location) const noexcept
Definition: location.hpp:562