Loading...
Searching...
No Matches
location.hpp
Go to the documentation of this file.
#ifndef OSMIUM_OSM_LOCATION_HPP
#define OSMIUM_OSM_LOCATION_HPP
/*
This file is part of Osmium (https://osmcode.org/libosmium).
Copyright 2013-2026 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <functional>
#include <iosfwd>
#include <iterator>
#include <limits>
#include <stdexcept>
#include <string>
namespace osmium {
struct invalid_location : public std::range_error {
explicit invalid_location(const std::string& what) :
std::range_error(what) {
}
explicit invalid_location(const char* what) :
std::range_error(what) {
}
}; // struct invalid_location
namespace detail {
enum {
coordinate_precision = 10000000
};
// Convert string with a floating point number into integer suitable
// for use as coordinate in a Location.
inline int32_t string_to_location_coordinate(const char** data) {
const char* str = *data;
const char* full = str;
int64_t result = 0;
int sign = 1;
// one more than significant digits to allow rounding
int64_t scale = 8;
// paranoia check for maximum number of digits
int max_digits = 10;
// optional minus sign
if (*str == '-') {
sign = -1;
++str;
}
if (*str != '.') {
// there has to be at least one digit
if (*str >= '0' && *str <= '9') {
result = *str - '0';
++str;
} else {
throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
}
// optional additional digits before decimal point
while (*str >= '0' && *str <= '9' && max_digits > 0) {
result = result * 10 + (*str - '0');
++str;
--max_digits;
}
if (max_digits == 0) {
throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
}
} else {
// need at least one digit after decimal dot if there was no
// digit before decimal dot
if (*(str + 1) < '0' || *(str + 1) > '9') {
throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
}
}
// optional decimal point
if (*str == '.') {
++str;
// read significant digits
for (; scale > 0 && *str >= '0' && *str <= '9'; --scale, ++str) {
result = result * 10 + (*str - '0');
}
// ignore non-significant digits
max_digits = 20;
while (*str >= '0' && *str <= '9' && max_digits > 0) {
++str;
--max_digits;
}
if (max_digits == 0) {
throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
}
}
// optional exponent in scientific notation
if (*str == 'e' || *str == 'E') {
++str;
int esign = 1;
// optional minus sign
if (*str == '-') {
esign = -1;
++str;
}
int64_t eresult = 0;
// there has to be at least one digit in exponent
if (*str >= '0' && *str <= '9') {
eresult = *str - '0';
++str;
} else {
throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
}
// optional additional digits in exponent
max_digits = 5;
while (*str >= '0' && *str <= '9' && max_digits > 0) {
eresult = eresult * 10 + (*str - '0');
++str;
--max_digits;
}
if (max_digits == 0) {
throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
}
scale += eresult * esign;
}
if (scale < 0) {
for (; scale < 0 && result > 0; ++scale) {
result /= 10;
}
} else {
for (; scale > 0; --scale) {
result *= 10;
}
}
result = (result + 5) / 10 * sign;
if (result > std::numeric_limits<int32_t>::max() ||
result < std::numeric_limits<int32_t>::min()) {
throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"};
}
*data = str;
return static_cast<int32_t>(result);
}
// Convert integer as used by location for coordinates into a string.
template <typename T>
inline T append_location_coordinate_to_string(T iterator, int32_t value) {
// need to special-case this, because later `value = -value` would overflow.
if (value == std::numeric_limits<int32_t>::min()) {
static const char minresult[] = "-214.7483648";
return std::copy_n(minresult, sizeof(minresult) - 1, iterator);
}
// handle negative values
if (value < 0) {
*iterator++ = '-';
value = -value;
}
// write digits into temporary buffer
int32_t v = value;
char temp[10];
char* t = temp;
do {
*t++ = static_cast<char>(v % 10) + '0'; // NOLINT(bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions)
v /= 10;
} while (v != 0);
while (t - temp < 7) {
*t++ = '0';
}
// write out digits before decimal point
if (value >= coordinate_precision) {
if (value >= 10 * coordinate_precision) {
if (value >= 100 * coordinate_precision) {
*iterator++ = *--t;
}
*iterator++ = *--t;
}
*iterator++ = *--t;
} else {
*iterator++ = '0';
}
// remove trailing zeros
const char* tn = temp;
while (tn < t && *tn == '0') {
++tn;
}
// decimal point
if (t != tn) {
*iterator++ = '.';
while (t != tn) {
*iterator++ = *--t;
}
}
return iterator;
}
} // namespace detail
class Location {
int32_t m_x; // NOLINT(modernize-use-default-member-init)
int32_t m_y; // NOLINT(modernize-use-default-member-init)
constexpr static double precision() noexcept {
return static_cast<double>(detail::coordinate_precision);
}
#ifdef OSMIUM_LOCATION_WITH_EXTRA_BIT
enum {
mask_b30 = 1UL << 30UL
};
static constexpr int32_t remove_extra_bit(int32_t v) noexcept {
if (v == undefined_coordinate) {
return v;
}
auto const value = static_cast<uint32_t>(v);
auto const n30 = value & ~mask_b30;
auto const b30 = (value >> 1UL) & mask_b30;
return static_cast<int32_t>(n30 | b30);
}
#else
static constexpr int32_t remove_extra_bit(int32_t v) noexcept {
return v;
}
#endif
public:
// this value is used for a coordinate to mark it as undefined
// MSVC doesn't declare std::numeric_limits<int32_t>::max() as
// constexpr, so we hard code this for the time being.
// undefined_coordinate = std::numeric_limits<int32_t>::max();
enum {
undefined_coordinate = 2147483647
};
static int32_t double_to_fix(const double c) noexcept {
return static_cast<int32_t>(std::round(c * precision()));
}
static constexpr double fix_to_double(const int32_t c) noexcept {
return static_cast<double>(c) / precision();
}
explicit constexpr Location() noexcept :
m_x(undefined_coordinate),
m_y(undefined_coordinate) {
}
constexpr Location(const int32_t x, const int32_t y) noexcept :
m_x(x),
m_y(remove_extra_bit(y)) {
}
constexpr Location(const int64_t x, const int64_t y) noexcept :
m_x(static_cast<int32_t>(x)),
m_y(remove_extra_bit(static_cast<int32_t>(y))) {
}
Location(const double lon, const double lat) :
m_x(double_to_fix(lon)),
m_y(remove_extra_bit(double_to_fix(lat))) {
}
explicit constexpr operator bool() const noexcept {
return m_x != undefined_coordinate && m_y != undefined_coordinate;
}
constexpr bool valid() const noexcept {
return x() >= -180 * precision()
&& x() <= 180 * precision()
&& y() >= -90 * precision()
&& y() <= 90 * precision();
}
constexpr bool is_defined() const noexcept {
return m_x != undefined_coordinate || m_y != undefined_coordinate;
}
constexpr bool is_undefined() const noexcept {
return m_x == undefined_coordinate && m_y == undefined_coordinate;
}
constexpr int32_t x() const noexcept {
return m_x;
}
constexpr int32_t y() const noexcept {
return remove_extra_bit(m_y);
}
Location& set_x(const int32_t x) noexcept {
m_x = x;
return *this;
}
Location& set_y(const int32_t y) noexcept {
m_y = remove_extra_bit(y);
return *this;
}
double lon() const {
if (!valid()) {
throw osmium::invalid_location{"invalid location"};
}
return fix_to_double(x());
}
double lon_without_check() const noexcept {
return fix_to_double(x());
}
double lat() const {
if (!valid()) {
throw osmium::invalid_location{"invalid location"};
}
return fix_to_double(y());
}
double lat_without_check() const noexcept {
return fix_to_double(y());
}
Location& set_lon(double lon) noexcept {
return set_x(double_to_fix(lon));
}
Location& set_lat(double lat) noexcept {
return set_y(double_to_fix(lat));
}
Location& set_lon(const char* str) {
const char** data = &str;
const auto value = detail::string_to_location_coordinate(data);
if (**data != '\0') {
throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
}
return set_x(value);
}
Location& set_lat(const char* str) {
const char** data = &str;
const auto value = detail::string_to_location_coordinate(data);
if (**data != '\0') {
throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"};
}
return set_y(value);
}
Location& set_lon_partial(const char** str) {
return set_x(detail::string_to_location_coordinate(str));
}
Location& set_lat_partial(const char** str) {
return set_y(detail::string_to_location_coordinate(str));
}
template <typename T>
T as_string_without_check(T iterator, const char separator = ',') const {
iterator = detail::append_location_coordinate_to_string(iterator, x());
*iterator++ = separator;
return detail::append_location_coordinate_to_string(iterator, y());
}
template <typename T>
T as_string(T iterator, const char separator = ',') const {
if (!valid()) {
throw osmium::invalid_location{"invalid location"};
}
return as_string_without_check(iterator, separator);
}
#ifdef OSMIUM_LOCATION_WITH_EXTRA_BIT
constexpr bool get_extra_bit() const noexcept {
assert(m_y != undefined_coordinate);
auto const b = static_cast<uint32_t>(m_y);
auto const b31 = b >> 31UL;
auto const b30 = (b >> 30UL) & 1UL;
return b30 ^ b31;
}
constexpr void set_extra_bit() noexcept {
assert(m_y != undefined_coordinate);
auto const b = static_cast<uint32_t>(m_y);
auto const n30 = b & ~mask_b30;
auto const b30 = (~b >> 1UL) & mask_b30;
m_y = static_cast<int32_t>(n30 | b30);
}
constexpr void set_extra_bit(bool value) noexcept {
assert(m_y != undefined_coordinate);
auto const b = static_cast<uint32_t>(m_y);
auto const n30 = b & ~mask_b30;
auto const b30 = ((b >> 31UL) ^ value) << 30UL;
m_y = static_cast<int32_t>(n30 | b30);
}
constexpr void clear_extra_bit() noexcept {
assert(m_y != undefined_coordinate);
m_y = y();
}
constexpr void toggle_extra_bit() noexcept {
assert(m_y != undefined_coordinate);
auto const b = static_cast<uint32_t>(m_y);
auto const n30 = b & ~mask_b30;
auto const b30 = ~b & mask_b30;
m_y = static_cast<int32_t>(n30 | b30);
}
#endif
}; // class Location
constexpr bool operator==(const Location& lhs, const Location& rhs) noexcept {
return lhs.x() == rhs.x() && lhs.y() == rhs.y();
}
constexpr bool operator!=(const Location& lhs, const Location& rhs) noexcept {
return !(lhs == rhs);
}
constexpr bool operator<(const Location& lhs, const Location& rhs) noexcept {
return (lhs.x() == rhs.x() && lhs.y() < rhs.y()) || lhs.x() < rhs.x();
}
constexpr bool operator>(const Location& lhs, const Location& rhs) noexcept {
return rhs < lhs;
}
constexpr bool operator<=(const Location& lhs, const Location& rhs) noexcept {
return !(rhs < lhs);
}
constexpr bool operator>=(const Location& lhs, const Location& rhs) noexcept {
return !(lhs < rhs);
}
template <typename TChar, typename TTraits>
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::Location& location) {
if (location) {
out << '(';
location.as_string(std::ostream_iterator<char>(out), ',');
out << ')';
} else {
out << "(undefined,undefined)";
}
return out;
}
namespace detail {
template <int N>
inline size_t hash(const osmium::Location& location) noexcept {
return static_cast<uint32_t>(location.x()) ^ static_cast<uint32_t>(location.y());
}
template <>
inline size_t hash<8>(const osmium::Location& location) noexcept {
uint64_t h = location.x();
h <<= 32U;
return static_cast<size_t>(h ^ static_cast<uint64_t>(location.y()));
}
} // namespace detail
} // namespace osmium
namespace std {
// This pragma is a workaround for a bug in an old libc implementation
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmismatched-tags"
#endif
template <>
struct hash<osmium::Location> {
using argument_type = osmium::Location;
using result_type = size_t;
size_t operator()(const osmium::Location& location) const noexcept {
return osmium::detail::hash<sizeof(size_t)>(location);
}
};
#ifdef __clang__
#pragma clang diagnostic pop
#endif
} // namespace std
#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