1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
#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
|