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-2025 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 <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);
}
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(y) {
}
constexpr Location(const int64_t x, const int64_t y) noexcept :
m_x(static_cast<int32_t>(x)),
m_y(static_cast<int32_t>(y)) {
}
Location(const double lon, const double lat) :
m_x(double_to_fix(lon)),
m_y(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 m_x >= -180 * precision()
&& m_x <= 180 * precision()
&& m_y >= -90 * precision()
&& m_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 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 = y;
return *this;
}
double lon() const {
if (!valid()) {
throw osmium::invalid_location{"invalid location"};
}
return fix_to_double(m_x);
}
double lon_without_check() const noexcept {
return fix_to_double(m_x);
}
double lat() const {
if (!valid()) {
throw osmium::invalid_location{"invalid location"};
}
return fix_to_double(m_y);
}
double lat_without_check() const noexcept {
return fix_to_double(m_y);
}
Location& set_lon(double lon) noexcept {
m_x = double_to_fix(lon);
return *this;
}
Location& set_lat(double lat) noexcept {
m_y = double_to_fix(lat);
return *this;
}
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 + "'"};
}
m_x = value;
return *this;
}
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 + "'"};
}
m_y = value;
return *this;
}
Location& set_lon_partial(const char** str) {
m_x = detail::string_to_location_coordinate(str);
return *this;
}
Location& set_lat_partial(const char** str) {
m_y = detail::string_to_location_coordinate(str);
return *this;
}
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);
}
}; // 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: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