mirror of
https://github.com/JS8Call-improved/JS8Call-improved
synced 2026-08-13 17:47:36 -04:00
159 lines
4.9 KiB
C++
159 lines
4.9 KiB
C++
#include <QString>
|
|
#include <QStringView>
|
|
|
|
#include <cmath>
|
|
#include <utility>
|
|
|
|
namespace Geodesic {
|
|
// Azimuth class, describes an azimuth in degrees. Created via
|
|
// interpolation of Maidenhead grid coordinates, and as such
|
|
// will be invalid if interpolation failed, typically due to
|
|
// bad coordinates.
|
|
|
|
class Azimuth {
|
|
// Data members
|
|
|
|
float m_value = NAN;
|
|
|
|
// Constructors
|
|
|
|
Azimuth() = default;
|
|
Azimuth(float const value) : m_value{value} {}
|
|
|
|
// Allow construction only by Vector.
|
|
|
|
friend class Vector;
|
|
|
|
public:
|
|
// Allow copying, moving, and assignment by anyone.
|
|
|
|
Azimuth(Azimuth const &) = default;
|
|
Azimuth &operator=(Azimuth const &) = default;
|
|
Azimuth(Azimuth &&) noexcept = default;
|
|
Azimuth &operator=(Azimuth &&) noexcept = default;
|
|
|
|
// Inline Accessors
|
|
|
|
auto isValid() const { return !std::isnan(m_value); }
|
|
|
|
// Conversion operators; return validity and value. These
|
|
// are all we need to implement an ordering relation, but
|
|
// we do so elsewhere.
|
|
|
|
explicit operator bool() const noexcept { return isValid(); }
|
|
operator float() const noexcept { return m_value; }
|
|
|
|
// Return as a directional arrow and cardinal direction, if
|
|
// valid, an empty string if invalid.
|
|
|
|
QStringView compass() const;
|
|
|
|
// String conversion, to the nearest whole degree; always
|
|
// succeeds, returning an empty string if invalid. Caller
|
|
// must specify if they want units or just a bare value.
|
|
|
|
QString toString(bool units) const;
|
|
};
|
|
|
|
// Distance class, describes a distance in kilometers. Created via
|
|
// interpolation of Maidenhead grid coordinates, and as such will
|
|
// be invalid if interpolation failed, typically due to bad coordinates.
|
|
//
|
|
// May additionally be defined as 'close', meaning that either of the
|
|
// grids provided was only 4 characters and the computed distance was
|
|
// short, so we know it's close, but not just how close. In this case,
|
|
// the value will be a non-zero minimum constant, and string conversion
|
|
// will prepend a '<'.
|
|
//
|
|
// While distances are stored internally only in kilometers, caller may
|
|
// request string conversion in terms of statute miles.
|
|
|
|
class Distance {
|
|
// Value, in kilometers, that we consider to be the close limit, so
|
|
// if we're informed that we should consider the value for closeness,
|
|
// i.e., one of the grids that gave rise to use is of at best square
|
|
// magnitude, we'd consider any value at or under the limit to be of
|
|
// only the limit in terms of precision.
|
|
|
|
static constexpr float CLOSE = 120.0f;
|
|
|
|
// Data members
|
|
|
|
float m_value = NAN;
|
|
|
|
// Constructors
|
|
|
|
Distance() = default;
|
|
Distance(float const value, bool const close)
|
|
: m_value{close && CLOSE > value ? INFINITY : value} {}
|
|
|
|
// Allow construction only by Vector.
|
|
|
|
friend class Vector;
|
|
|
|
public:
|
|
// Allow copying, moving, and assignment by anyone.
|
|
|
|
Distance(Distance const &) = default;
|
|
Distance &operator=(Distance const &) = default;
|
|
Distance(Distance &&) noexcept = default;
|
|
Distance &operator=(Distance &&) noexcept = default;
|
|
|
|
// Inline Accessors
|
|
|
|
auto isValid() const { return !std::isnan(m_value); }
|
|
auto isClose() const { return std::isinf(m_value); }
|
|
|
|
// Conversion operators; return validity and value. These
|
|
// are all we need to implement an ordering relation, but
|
|
// we do so elsewhere.
|
|
|
|
explicit operator bool() const noexcept { return isValid(); }
|
|
operator float() const noexcept { return isClose() ? CLOSE : m_value; }
|
|
|
|
// String conversion, to the nearest whole kilometer or mile,
|
|
// always succeeds, returning an empty string if invalid. Caller
|
|
// must specify if they want units or just the bare value.
|
|
|
|
QString toString(bool miles, bool units) const;
|
|
};
|
|
|
|
// Vector class, aggregate of azimuth and distance from an
|
|
// origin grid to a remote grid.
|
|
|
|
class Vector {
|
|
// Data members
|
|
|
|
Azimuth m_azimuth;
|
|
Distance m_distance;
|
|
|
|
// Constructors; disallow creation without going through
|
|
// the vector() function.
|
|
|
|
Vector() = default;
|
|
Vector(std::pair<float, float> const &azdist, bool const square)
|
|
: m_azimuth{azdist.first}, m_distance{azdist.second, square} {}
|
|
|
|
friend Vector vector(QStringView, QStringView);
|
|
|
|
public:
|
|
// Allow copying, moving, and assignment by anyone.
|
|
|
|
Vector(Vector const &) = default;
|
|
Vector &operator=(Vector const &) = default;
|
|
Vector(Vector &&) noexcept = default;
|
|
Vector &operator=(Vector &&) noexcept = default;
|
|
|
|
// Inline accessors
|
|
|
|
Azimuth const &azimuth() const { return m_azimuth; }
|
|
Distance const &distance() const { return m_distance; }
|
|
};
|
|
|
|
// Creation method; manages a cache, returning cached data
|
|
// if possible. This gets called just a lot; performing the
|
|
// calculations needed to make a vector are not cheap, so
|
|
// we want to reuse results as much as possible.
|
|
|
|
Vector vector(QStringView origin, QStringView remote);
|
|
} // namespace Geodesic
|