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
|
#ifndef ANGLE_H
#define ANGLE_H
#include <string>
#include <sstream>
#include <stdexcept>
#include <cmath>
#include <boost/algorithm/string/case_conv.hpp>
class Angle
{
public:
enum Unit { Radians, Degrees, Arcminutes, Arcseconds, Milliarcseconds };
/**
* Parse the string as an angle, possibly with unit specification, and return in radians.
* @return The angle
*/
static double Parse(const std::string& s, const std::string& valueDescription, Unit defaultUnit);
static std::string ToNiceString(double angleRad);
private:
static size_t findNumberEnd(const std::string& s);
static bool isDigit(const char c) { return c>='0' && c<='9'; }
static bool isWhitespace(const char c) { return c==' ' || c=='\t'; }
};
inline std::string Angle::ToNiceString(double angleRad)
{
std::ostringstream str;
double degAngle = angleRad * 180.0 / M_PI;
if(degAngle >= 2.0)
{
str << round(degAngle*100.0)/100.0 << " deg";
}
else {
double minAngle = angleRad * 180.0 * 60.0 / M_PI;
if(minAngle >= 2.0)
{
str << round(minAngle*100.0)/100.0 << "'";
}
else {
double secAngle = angleRad * 180.0 * 60.0 * 60.0 / M_PI;
if(secAngle >= 1.0)
{
str << round(secAngle*100.0)/100.0 << "''";
}
else {
str << round (secAngle*100.0*1000.0)/100.0 << " masec";
}
}
}
return str.str();
}
inline double Angle::Parse(const std::string& s, const std::string& valueDescription, Unit defaultUnit)
{
size_t end = findNumberEnd(s);
if(end == 0)
throw std::runtime_error("Error parsing " + valueDescription);
std::string number = s.substr(0, end);
double val = atof(number.c_str());
// Skip whitespace after number
const char *c = s.c_str();
while(isWhitespace(c[end]))
++end;
std::string unitStr = std::string(&c[end]);
boost::to_lower(unitStr);
// Unit string empty? Than use default unit.
if(unitStr.empty())
{
switch(defaultUnit)
{
case Radians:
return val;
case Degrees:
return val * M_PI/180.0;
case Arcminutes:
return val * M_PI/(180.0*60.0);
case Arcseconds:
return val * M_PI/(180.0*60.0*60.0);
case Milliarcseconds:
return val * M_PI/(180.0*60.0*60.0*1000.0);
}
}
// In degrees?
else if(unitStr=="deg" || unitStr=="degrees")
return val * M_PI/180.0;
// In arcmin?
else if(unitStr.empty() || unitStr=="amin" || unitStr=="arcmin" || unitStr=="\'")
return val * M_PI/(180.0*60.0);
// In arcsec?
else if(unitStr.empty() || unitStr=="asec" || unitStr=="arcsec" || unitStr=="\'\'")
return val * M_PI/(180.0*60.0*60.0);
// In marcsec?
else if(unitStr.empty() || unitStr=="masec" || unitStr=="marcsec")
return val * M_PI/(180.0*60.0*60.0*1000.0);
// In radians
else if(unitStr.empty() || unitStr=="rad" || unitStr=="radians")
return val;
throw std::runtime_error("Invalid unit specification in angle given for " + valueDescription);
}
inline size_t Angle::findNumberEnd(const std::string& s)
{
const char* c = s.c_str();
size_t pos = 0;
while(isWhitespace(c[pos]))
++pos;
while(isDigit(c[pos]))
++pos;
if(c[pos]=='.')
++pos;
while(isDigit(c[pos]))
++pos;
if(c[pos]=='e' || c[pos]=='E')
{
++pos;
if(c[pos]=='-' || c[pos]=='+')
++pos;
while(isDigit(c[pos]))
++pos;
}
return pos;
}
#endif
|