Some cleanup of timequantizer and its test

This commit is contained in:
Alexander Bock
2020-02-12 09:36:09 +01:00
parent eb83d49cf3
commit be8d850ef7
3 changed files with 555 additions and 610 deletions

View File

@@ -25,20 +25,329 @@
#include <modules/globebrowsing/src/timequantizer.h>
#include <openspace/util/time.h>
#include <ghoul/fmt.h>
#include <ghoul/glm.h>
#include <ghoul/misc/assert.h>
#include <ghoul/misc/exception.h>
#include <algorithm>
#include <sstream>
#include <iomanip>
#include <sstream>
namespace openspace::globebrowsing {
namespace {
TimeQuantizer::TimeQuantizer(const std::string& start, const std::string& end,
// returns the number of days in a given month and year (takes leap year into account)
int monthSize(int month, int year) {
// A year is a leap year if it is divisible by 4 unless it is also divisible by 100
// unless it is divisible by 4 *and* 100
const bool leap = ((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0));
switch (month) {
case 2:
return leap ? 29 : 28;
case 4:
case 6:
case 9:
case 11:
return 30;
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
default:
return 31;
}
}
/**
* singleIncrement is used for any of the date/time types, and handles overflow
* values using the min/max parameters
*
* \param oper the date/time variable to operate on (will be changed)
* \param val the value of the increment, which may be changed in this function
* if an overflow occurs
* \param min the minimum allowable value
* \param max the maximum allowable value (determines where overflow occurs)
*/
bool singleIncrement(int& oper, int& val, int min, int max) {
oper += val;
if (oper <= max) {
return true;
}
oper = oper - max - (1 - min);
// Only single increments for the less-significant units on rollover
val = 1;
return false;
}
/**
* singleDecrement is used for any of the date/time types, and handles underflow
* values using the min/max parameters
*
* \param oper the date/time variable to operate on (will be changed)
* \param val the value of the decrement, which may be changed in this function
* if an underflow occurs
* \param min the minimum allowable value
* \param max the maximum allowable value (determines where underflow occurs)
*/
bool singleDecrement(int& oper, int& val, int min, int max) {
oper -= val;
if (oper >= min) {
return true;
}
oper = oper + max + (1 - min);
// Only single increments for the less-significant units on rollover
val = 1;
return false;
}
} // namespace
RangedTime::RangedTime(std::string start, std::string end)
: _start(std::move(start))
, _end(std::move(end))
{
setStart(_start);
setEnd(_end);
}
void RangedTime::setStart(std::string start) {
Time t1;
t1.setTime(start);
_startJ2000 = t1.j2000Seconds();
_start = start;
}
void RangedTime::setEnd(const std::string end) {
Time t2;
t2.setTime(end);
_endJ2000 = t2.j2000Seconds();
_end = end;
}
bool RangedTime::includes(const std::string& checkTime) {
Time t;
t.setTime(checkTime);
const double tj = t.j2000Seconds();
return (_startJ2000 <= tj && tj <= _endJ2000);
}
std::string RangedTime::clamp(const std::string& checkTime) {
Time t;
t.setTime(checkTime);
const double tj = t.j2000Seconds();
if (tj < _startJ2000) {
return _start;
}
else if (tj > _endJ2000) {
return _end;
}
else {
return checkTime;
}
}
std::string RangedTime::start() const {
return _start;
}
std::string RangedTime::end() const {
return _end;
}
DateTime::DateTime(const std::string& initDateTime) {
setTime(initDateTime);
};
void DateTime::setTime(const std::string& input) {
_year = std::stoi(input.substr(index_year, len_year));
_month = std::stoi(input.substr(index_month, len_nonYear));
_day = std::stoi(input.substr(index_day, len_nonYear));
_hour = std::stoi(input.substr(index_hour, len_nonYear));
_minute = std::stoi(input.substr(index_minute, len_nonYear));
_second = std::stoi(input.substr(index_second, len_nonYear));
}
std::string DateTime::ISO8601() const {
return fmt::format(
"{:0>4}-{:0>2}-{:0>2}T{:0>2}:{:0>2}:{:0>2}",
_year, _month, _day, _hour, _minute, _second
);
};
double DateTime::J2000() const {
Time t;
std::string timeString = ISO8601();
t.setTime(timeString);
return t.j2000Seconds();
}
void DateTime::operator=(DateTime& src) {
_year = src.year();
_month = src.month();
_day = src.day();
_hour = src.hour();
_minute = src.minute();
_second = src.second();
}
int DateTime::increment(int value, char unit, double error, double resolution) {
unsigned int nIncrements = std::abs(static_cast<int>(error / resolution));
if (nIncrements == 0) {
nIncrements = 1;
}
for (unsigned int i = 0; i < nIncrements; ++i) {
incrementOnce(value, unit);
}
return nIncrements;
}
void DateTime::incrementOnce(int value, char unit) {
bool inBounds = true;
switch (unit) {
case 'm':
if (singleIncrement(_minute, value, 0, 59))
break;
// else fall-through if overflow...
case 'h':
if (singleIncrement(_hour, value, 0, 23))
break;
// else fall-through if overflow...
case 'd':
if (singleIncrement(_day, value, 1, monthSize(_month, _year)))
break;
// else fall-through if overflow...
case 'M':
inBounds = singleIncrement(_month, value, 1, 12);
_day = std::clamp(_day, 1, monthSize(_month, _year));
if (inBounds)
break;
// else fall-through if overflow...
case 'y':
_year += value;
break;
default:
throw ghoul::RuntimeError(
"Invalid unit format in TQ incrementOnce '" + std::to_string(unit) +
"'. Expected 'y', 'M', 'd', 'h', or 'm'."
);
}
}
int DateTime::decrement(int value, char unit, double error, double resolution) {
unsigned int nDecrements = std::abs(static_cast<int>(error / resolution));
if (nDecrements == 0) {
nDecrements = 1;
}
for (unsigned int i = 0; i < nDecrements; ++i) {
decrementOnce(value, unit);
}
return nDecrements;
}
void DateTime::decrementOnce(int value, char unit) {
bool inBounds = true;
switch (unit) {
case 'm':
if (singleDecrement(_minute, value, 0, 59)) {
break;
}
// else fall-through if underflow...
case 'h':
if (singleDecrement(_hour, value, 0, 23)) {
break;
}
// else fall-through if underflow...
case 'd':
if (singleDecrement(_day, value, 1,
monthSize(_month == 1 ? 12 : _month - 1, _year)))
{
break;
}
// else fall-through if underflow...
case 'M':
inBounds = singleDecrement(_month, value, 1, 12);
_day = std::clamp(_day, 1, monthSize(_month, _year));
if (inBounds) {
break;
}
// else fall-through if underflow...
case 'y':
_year -= value;
break;
default:
throw ghoul::RuntimeError(
"Invalid unit format in TQ decrementOnce '" + std::to_string(unit) +
"'. Expected 'y', 'M', 'd', 'h', or 'm'."
);
}
}
int DateTime::year() const {
return _year;
}
int DateTime::month() const {
return _month;
}
int DateTime::day() const {
return _day;
}
int DateTime::hour() const {
return _hour;
}
int DateTime::minute() const {
return _minute;
}
int DateTime::second() const {
return _second;
}
void DateTime::setYear(int y) {
_year = y;
}
void DateTime::setMonth(int m) {
_month = m;
}
void DateTime::setDay(int d) {
_day = d;
}
void DateTime::setHour(int h) {
_hour = h;
}
void DateTime::setMinute(int m) {
_minute = m;
}
void DateTime::setSecond(int s) {
_second = (s > 0) ? ((s <= 59) ? s : 59) : 0;
}
TimeQuantizer::TimeQuantizer(std::string start, std::string end,
const std::string& resolution)
: _start(start)
, _timerange(start, end)
, _timerange(std::move(start), std::move(end))
{
verifyStartTimeRestrictions();
_resolution = parseTimeResolutionStr(resolution);
@@ -75,88 +384,59 @@ void TimeQuantizer::setResolution(const std::string& resolutionString) {
void TimeQuantizer::verifyStartTimeRestrictions() {
if (_start.day() < 1 || _start.day() > 28) {
throw ghoul::RuntimeError(
"Invalid start day value of " + std::to_string(_start.day()) +
" for day of month. Valid days are 1 - 28."
);
throw ghoul::RuntimeError(fmt::format(
"Invalid start day value of {} for day of month. Valid days are 1 - 28",
_start.day()
));
}
if (_start.hour() != 0 || _start.minute() != 0 || _start.second() != 0) {
throw ghoul::RuntimeError(
"Invalid start time value of " + std::to_string(_start.hour()) + ":" +
std::to_string(_start.minute()) + ":" + std::to_string(_start.second()) +
". Time must be 00:00:00."
);
throw ghoul::RuntimeError(fmt::format(
"Invalid start time value of {}:{}:{}. Time must be 00:00:00",
_start.hour(), _start.minute(), _start.second()
));
}
}
void TimeQuantizer::verifyResolutionRestrictions(const int value, const char unit) {
switch (unit) {
case 'y':
break;
case 'M':
switch (value) {
case 1:
case 2:
case 3:
case 4:
case 6:
case 'y':
break;
default:
throw ghoul::RuntimeError(
"Invalid resolution count of " + std::to_string(value) + " for (M)onth"
+ " option. Valid counts are 1, 2, 3, 4, or 6."
);
}
break;
case 'd':
if (value < 1 || value > 28) {
throw ghoul::RuntimeError(
"Invalid resolution count of " + std::to_string(value) + " for (d)ay"
+ " option. Valid counts are 1 - 28."
);
}
break;
case 'h':
switch (value) {
case 1:
case 2:
case 3:
case 4:
case 6:
case 12:
case 'M':
if (value < 1 || value > 6) {
throw ghoul::RuntimeError(fmt::format(
"Invalid resolution count of {} for (M)onth option. Valid counts are "
"1, 2, 3, 4, or 6", value
));
}
break;
default:
throw ghoul::RuntimeError(
"Invalid resolution count of " + std::to_string(value) + " for (h)our"
+ " option. Valid counts are 1, 2, 3, 4, 6, or 12."
);
}
break;
case 'm':
switch (value) {
case 15:
case 30:
case 'd':
if (value < 1 || value > 28) {
throw ghoul::RuntimeError(fmt::format(
"Invalid resolution count of {} for (d)ay option. Valid counts are "
"1 - 28", value
));
}
break;
case 'h':
if (!(value >= 1 && value <= 4 || value == 6 || value == 12)) {
throw ghoul::RuntimeError(fmt::format(
"Invalid resolution count of {} for (h)our option. Valid counts are "
"1, 2, 3, 4, 6, or 12", value
));
}
break;
case 'm':
if (value != 15 && value != 30) {
throw ghoul::RuntimeError(fmt::format(
"Invalid resolution count of {} for (m)inute option. Valid counts "
"are 15 or 30", value
));
}
break;
default:
throw ghoul::RuntimeError(
"Invalid resolution count of " + std::to_string(value) + " for (m)inute"
+ " option. Valid counts are 1, 2, 3, 4, 6, or 12."
);
}
break;
default:
throw ghoul::RuntimeError(
"Invalid resolution unit format '" + std::to_string(unit) +
"'. Expected 'y', 'M', 'd', 'h', or 'm'."
);
throw ghoul::RuntimeError(fmt::format(
"Invalid unit format '{}'. Expected 'y', 'M', 'd', 'h', or 'm'", unit
));
}
}
@@ -165,31 +445,31 @@ double TimeQuantizer::computeSecondsFromResolution(const int valueIn, const char
// convert value to seconds, based on unit.
// The switch statment has intentional fall throughs
switch (unit) {
case 'y':
value *= 365;
[[fallthrough]];
case 'd':
value *= 24.0;
[[fallthrough]];
case 'h':
value *= 60.0;
[[fallthrough]];
case 'm':
value *= 60.0;
[[fallthrough]];
case 's':
value *= 1.0;
break;
case 'y':
value *= 365;
[[fallthrough]];
case 'd':
value *= 24.0;
[[fallthrough]];
case 'h':
value *= 60.0;
[[fallthrough]];
case 'm':
value *= 60.0;
[[fallthrough]];
case 's':
value *= 1.0;
break;
case 'M':
value *= (30.4 * 24.0 * 60.0 * 60.0);
break;
case 'M':
value *= (30.4 * 24.0 * 60.0 * 60.0);
break;
default:
throw ghoul::RuntimeError(
"Invalid resolution unit format '" + std::to_string(unit) +
"'. Expected 'y', 'M', 'd', 'h', 'm' or 's'."
);
default:
throw ghoul::RuntimeError(fmt::format(
"Invalid resolution unit format '{}'. Expected 'y', 'M', 'd', 'h', 'm', "
"or 's'", unit
));
}
return value;
}
@@ -255,61 +535,59 @@ void TimeQuantizer::doFirstApproximation(DateTime& quantized, DateTime& unQ,
double addToTime;
switch (unit) {
case 'y':
minYearsToAdjust = static_cast<double>(unQ.year()) -
static_cast<double>(_start.year());
minIncrementsAdjust = minYearsToAdjust / value;
quantized.setYear(_start.year() + static_cast<int>(minIncrementsAdjust) * value);
break;
case 'M':
isSimMonthPastQuantizedMonth = unQ.month() > static_cast<int>(value);
quantized.setYear((isSimMonthPastQuantizedMonth) ? unQ.year() : unQ.year() - 1);
break;
case 'd':
error = diff(quantized, unQ);
error /= 86400;
originalHour = quantized.hour();
originalMinute = quantized.minute();
originalSecond = quantized.second();
addToTime = std::round(error) * 86400;
testDay.setTime(quantized.J2000() + addToTime);
quantized.setTime(testDay.ISO8601());
quantized.setHour(originalHour);
quantized.setMinute(originalMinute);
quantized.setSecond(originalSecond);
break;
case 'h':
quantized = unQ;
quantized.setMinute(0);
quantized.setSecond(0);
if (unQ.hour() >= 12) {
quantized.setHour(0);
}
else {
quantized.decrementOnce(1, 'd');
}
break;
case 'm':
quantized = unQ;
quantized.setMinute(0);
quantized.setSecond(0);
if (quantized.hour() > 0) {
quantized.decrementOnce(1, 'h');
}
else {
quantized.decrementOnce(1, 'd');
}
break;
default:
throw ghoul::RuntimeError(
"Invalid unit format in doFirstApproximation '" + std::to_string(unit) +
"'. Expected 'y', 'M', 'd', 'h', or 'm'."
);
case 'y':
minYearsToAdjust = static_cast<double>(unQ.year()) -
static_cast<double>(_start.year());
minIncrementsAdjust = minYearsToAdjust / value;
quantized.setYear(
_start.year() + static_cast<int>(minIncrementsAdjust) * value
);
break;
case 'M':
isSimMonthPastQuantizedMonth = unQ.month() > static_cast<int>(value);
quantized.setYear(
(isSimMonthPastQuantizedMonth) ? unQ.year() : unQ.year() - 1
);
break;
case 'd':
error = diff(quantized, unQ) / (60 * 60 * 24);
originalHour = quantized.hour();
originalMinute = quantized.minute();
originalSecond = quantized.second();
addToTime = std::round(error) * 86400;
testDay.setTime(quantized.J2000() + addToTime);
quantized.setTime(testDay.ISO8601());
quantized.setHour(originalHour);
quantized.setMinute(originalMinute);
quantized.setSecond(originalSecond);
break;
case 'h':
quantized = unQ;
quantized.setMinute(0);
quantized.setSecond(0);
if (unQ.hour() >= 12) {
quantized.setHour(0);
}
else {
quantized.decrementOnce(1, 'd');
}
break;
case 'm':
quantized = unQ;
quantized.setMinute(0);
quantized.setSecond(0);
if (quantized.hour() > 0) {
quantized.decrementOnce(1, 'h');
}
else {
quantized.decrementOnce(1, 'd');
}
break;
default:
throw ghoul::RuntimeError(fmt::format(
"Invalid unit format in doFirstApproximation '{}'. Expected 'y', 'M', "
"d', 'h', or 'm'", unit
));
}
}
@@ -324,7 +602,10 @@ std::vector<std::string> TimeQuantizer::quantized(Time& start, Time& end) {
const double endSeconds = e.J2000();
const double delta = endSeconds - startSeconds;
ghoul_assert(int(delta) % int(_resolution) == 0, "Quantization error");
ghoul_assert(
static_cast<int>(delta) % static_cast<int>(_resolution) == 0,
"Quantization error"
);
const int nSteps = static_cast<int>(delta / _resolution);
std::vector<std::string> result;
@@ -338,281 +619,4 @@ std::vector<std::string> TimeQuantizer::quantized(Time& start, Time& end) {
return result;
}
DateTime::DateTime(std::string initDateTime) {
setTime(initDateTime);
};
void DateTime::setTime(const std::string& input) {
_year = std::stoi(input.substr(index_year, len_year));
_month = std::stoi(input.substr(index_month, len_nonYear));
_day = std::stoi(input.substr(index_day, len_nonYear));
_hour = std::stoi(input.substr(index_hour, len_nonYear));
_minute = std::stoi(input.substr(index_minute, len_nonYear));
_second = std::stoi(input.substr(index_second, len_nonYear));
}
std::string DateTime::ISO8601() {
std::stringstream sstr = std::stringstream();
sstr << std::setfill('0') << std::setw(4) << _year << "-";
sstr << std::setfill('0') << std::setw(2) << _month << "-";
sstr << std::setfill('0') << std::setw(2) << _day << "T";
sstr << std::setfill('0') << std::setw(2) << _hour << ":";
sstr << std::setfill('0') << std::setw(2) << _minute << ":";
sstr << std::setfill('0') << std::setw(2) << _second;
return sstr.str();
};
double DateTime::J2000() {
Time t;
std::string timeString = ISO8601();
t.setTime(timeString);
return t.j2000Seconds();
}
void DateTime::operator= (DateTime& src) {
_year = src.year();
_month = src.month();
_day = src.day();
_hour = src.hour();
_minute = src.minute();
_second = src.second();
}
int DateTime::increment(int value, char unit, double error, double resolution) {
unsigned int nIncrements = std::abs(static_cast<int>(error / resolution));
if (nIncrements == 0) {
nIncrements = 1;
}
for (unsigned int i = 0; i < nIncrements; ++i) {
incrementOnce(value, unit);
}
return nIncrements;
}
void DateTime::incrementOnce(int value, char unit) {
bool inBounds = true;
switch (unit) {
case 'm':
if (singleIncrement(_minute, value, 0, 59))
break;
//else fall-through if overflow...
case 'h':
if (singleIncrement(_hour, value, 0, 23))
break;
//else fall-through if overflow...
case 'd':
if (singleIncrement(_day, value, 1, monthSize(_month, _year)))
break;
//else fall-through if overflow...
case 'M':
inBounds = singleIncrement(_month, value, 1, 12);
_day = std::clamp(_day, 1, monthSize(_month, _year));
if (inBounds)
break;
//else fall-through if overflow...
case 'y':
_year += value;
break;
default:
throw ghoul::RuntimeError(
"Invalid unit format in TQ incrementOnce '" + std::to_string(unit) +
"'. Expected 'y', 'M', 'd', 'h', or 'm'."
);
}
}
bool DateTime::singleIncrement(int& oper, int& val, int min, int max) {
oper += val;
if (oper <= max) {
return true;
}
oper = oper - max - (1 - min);
//Only single increments for the less-significant units on rollover
val = 1;
return false;
}
int DateTime::decrement(int value, char unit, double error, double resolution) {
unsigned int nDecrements = std::abs(static_cast<int>(error / resolution));
if (nDecrements == 0) {
nDecrements = 1;
}
for (unsigned int i = 0; i < nDecrements; ++i) {
decrementOnce(value, unit);
}
return nDecrements;
}
void DateTime::decrementOnce(int value, char unit) {
bool inBounds = true;
switch (unit) {
case 'm':
if (singleDecrement(_minute, value, 0, 59))
break;
//else fall-through if underflow...
case 'h':
if (singleDecrement(_hour, value, 0, 23))
break;
//else fall-through if underflow...
case 'd':
if (singleDecrement(_day, value, 1,
monthSize(_month == 1 ? 12 : _month - 1, _year)))
{
break;
}
//else fall-through if underflow...
case 'M':
inBounds = singleDecrement(_month, value, 1, 12);
_day = std::clamp(_day, 1, monthSize(_month, _year));
if (inBounds)
break;
//else fall-through if underflow...
case 'y':
_year -= value;
break;
default:
throw ghoul::RuntimeError(
"Invalid unit format in TQ decrementOnce '" + std::to_string(unit) +
"'. Expected 'y', 'M', 'd', 'h', or 'm'."
);
}
}
bool DateTime::singleDecrement(int& oper, int& val, int min, int max) {
oper -= val;
if (oper >= min) {
return true;
}
oper = oper + max + (1 - min);
//Only single increments for the less-significant units on rollover
val = 1;
return false;
}
int DateTime::monthSize(int month, int year) {
bool leap = ((year - 2000) % 4) == 0;
switch (month) {
case 2:
return (leap) ? 29 : 28;
break;
case 4:
case 6:
case 9:
case 11:
return 30;
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
default:
return 31;
}
}
int DateTime::year() {
return _year;
}
int DateTime::month() {
return _month;
}
int DateTime::day() {
return _day;
}
int DateTime::hour() {
return _hour;
}
int DateTime::minute() {
return _minute;
}
int DateTime::second() {
return _second;
}
void DateTime::setYear(int y) {
_year = y;
}
void DateTime::setMonth(int m) {
_month = m;
}
void DateTime::setDay(int d) {
_day = d;
}
void DateTime::setHour(int h) {
_hour = h;
}
void DateTime::setMinute(int m) {
_minute = m;
}
void DateTime::setSecond(int s) {
_second = (s > 0) ? ((s <= 59) ? s : 59) : 0;
}
RangedTime::RangedTime(const std::string start, const std::string end)
: _start(start),
_end(end)
{
setStart(start);
setEnd(end);
}
void RangedTime::setStart(const std::string start) {
Time t1;
t1.setTime(start);
_startJ2000 = t1.j2000Seconds();
_start = start;
}
void RangedTime::setEnd(const std::string end) {
Time t2;
t2.setTime(end);
_endJ2000 = t2.j2000Seconds();
_end = end;
}
bool RangedTime::includes(const std::string& checkTime) {
Time t;
t.setTime(checkTime);
double tj = t.j2000Seconds();
return (_startJ2000 <= tj && tj <= _endJ2000);
}
std::string RangedTime::clamp(const std::string& checkTime) {
Time t;
t.setTime(checkTime);
double tj = t.j2000Seconds();
if (tj < _startJ2000) {
return _start;
}
else if (tj > _endJ2000) {
return _end;
}
else {
return checkTime;
}
}
std::string RangedTime::start() {
return _start;
}
std::string RangedTime::end() {
return _end;
}
} // namespace openspace::globebrowsing

View File

@@ -48,7 +48,7 @@ public:
* \param start The date/time start of the time range.
* \param end The date/time ending of the time range.
*/
RangedTime(const std::string start, const std::string end);
RangedTime(std::string start, std::string end);
/*
* Checks if a date/time value falls within the start/end range defined in this
@@ -76,14 +76,14 @@ public:
*
* \returns The ISO8601 date/time string that defines the start of the range
*/
std::string start();
std::string start() const;
/*
* Get the end date/time of the time range
*
* \returns The ISO8601 date/time string that defines the end of the range
*/
std::string end();
std::string end() const;
/*
* Set the start date/time of the time range
@@ -116,13 +116,13 @@ private:
*/
class DateTime {
public:
DateTime() {};
DateTime() = default;
/*
* Constructor that initializes with date/time string
*
* \params initDateTime the ISO8601 date/time string (YYYY-MM-DDTHH:mm:ss)
*/
DateTime(std::string initDateTime);
DateTime(const std::string& initDateTime);
/*
* Set the date/time value
@@ -136,14 +136,14 @@ public:
*
* \params src the DateTime object to copy from
*/
void operator= (DateTime& src);
void operator=(DateTime& src);
/*
* Get the date/time value in ISO8601 format
*
* \returns the date/time value string
*/
std::string ISO8601();
std::string ISO8601() const;
/*
* Get the J2000 seconds equivalent of the object's date/time, using
@@ -151,43 +151,43 @@ public:
*
* \returns J2000 seconds of date/time
*/
double J2000();
double J2000() const;
/*
* Get the year of the object's date/time (YYYY format)
* \returns integer value of the year
*/
int year();
int year() const;
/*
* Get the month of the object's date/time (1 - 12)
* \returns integer value of the month
*/
int month();
int month() const;
/*
* Get the day-of-month of the object's date/time (1 - 31)
* \returns integer value of the day
*/
int day();
int day() const;
/*
* Get the hour of the object's date/time
* \returns integer value of the hour (0 - 23)
*/
int hour();
int hour() const;
/*
* Get the minute of the object's date/time
* \returns integer value of the minutes
*/
int minute();
int minute() const;
/*
* Get the seconds of the object's date/time
* \returns integer value of the seconds
*/
int second();
int second() const;
/*
* Set the year of the object's date/time
@@ -276,34 +276,7 @@ public:
void decrementOnce(int value, char unit);
private:
/*
* singleIncrement is used for any of the date/time types, and handles overflow
* values using the min/max parameters
*
* oper the date/time variable to operate on (will be changed)
* val the value of the increment, which may be changed in this function
* if an overflow occurs
* min the minimum allowable value
* max the maximum allowable value (determines where overflow occurs)
*/
bool singleIncrement(int& oper, int& val, int min, int max);
/*
* singleDecrement is used for any of the date/time types, and handles underflow
* values using the min/max parameters
*
* oper the date/time variable to operate on (will be changed)
* val the value of the decrement, which may be changed in this function
* if an underflow occurs
* min the minimum allowable value
* max the maximum allowable value (determines where underflow occurs)
*/
bool singleDecrement(int& oper, int& val, int min, int max);
//returns the number of days in a given month and year (takes leap year into account)
int monthSize(int month, int year);
//index_ values are indices into an ISO8601 YYYY-MM-ddTHH:mm:ss string
// index_ values are indices into an ISO8601 YYYY-MM-ddTHH:mm:ss string
const int index_year = 0;
const int index_month = 5;
const int index_day = 8;
@@ -342,8 +315,7 @@ public:
* (h)our Example: '12h' = 12 hours. Allowable values: 1, 2, 3, 4, 6, 12
* (m)inute Example: '15m' = 15 minutes. Allowable values: 0, 15, 30
*/
TimeQuantizer(const std::string& start, const std::string& end,
const std::string& resolution);
TimeQuantizer(std::string start, std::string end, const std::string& resolution);
/*
* Set the time range start & end date/time range.

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2019 *
* Copyright (c) 2014-2020 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -24,13 +24,15 @@
#include "catch2/catch.hpp"
#include <openspace/util/time.h>
#include "modules/globebrowsing/src/timequantizer.h"
#include <openspace/util/spicemanager.h>
#include <openspace/util/time.h>
#include <ghoul/filesystem/filesystem.h>
#include "SpiceUsr.h"
#include "SpiceZpr.h"
using namespace openspace;
namespace {
constexpr const int FILLEN = 128;
constexpr const int TYPLEN = 32;
@@ -50,109 +52,81 @@ namespace {
REQUIRE(kernelID == 1);
return kernelID;
}
void singleTimeTest(Time& t, globebrowsing::TimeQuantizer& tq, bool clamp,
const std::string& input, const std::string& expected)
{
t.setTime(input);
tq.quantize(t, clamp);
REQUIRE(t.ISO8601() == expected);
}
void singleResolutionTest(globebrowsing::TimeQuantizer& tq, std::string resolution,
std::string expectedType, bool expectFailure)
{
std::string res;
const std::string search = "Invalid resolution ";
try {
tq.setResolution(resolution);
}
catch (const ghoul::RuntimeError & e) {
res = e.message;
}
if (expectFailure) {
REQUIRE(res.find(search) != std::string::npos);
REQUIRE(res.find(expectedType) != std::string::npos);
}
else {
REQUIRE(res.find(search) == std::string::npos);
}
}
void singleStartTimeTest(globebrowsing::TimeQuantizer& tq, std::string startTime,
std::string expectedErrSubstring, bool expectFailure)
{
std::string res;
try {
tq.setStartEndRange(startTime, startTime);
}
catch (const ghoul::RuntimeError & e) {
res = e.message;
}
if (expectFailure) {
REQUIRE(res.find(expectedErrSubstring) != std::string::npos);
}
else {
REQUIRE(res.find(expectedErrSubstring) == std::string::npos);
}
}
void singleStartTimeTest(std::string startTime, std::string expectedErrSubstring,
bool expectFailure)
{
std::string res;
try {
globebrowsing::TimeQuantizer tq(startTime, startTime, "1d");
}
catch (const ghoul::RuntimeError & e) {
res = e.message;
}
if (expectFailure) {
REQUIRE(res.find(expectedErrSubstring) != std::string::npos);
}
else {
REQUIRE(res.find(expectedErrSubstring) == std::string::npos);
}
}
} // namespace
static void singleTimeTest(openspace::Time& t,
openspace::globebrowsing::TimeQuantizer& tq, bool clamp,
const std::string& input, const std::string& expected)
{
t.setTime(input);
tq.quantize(t, clamp);
REQUIRE(t.ISO8601() == expected);
}
static void singleResolutionTest(openspace::globebrowsing::TimeQuantizer& tq,
std::string resolution, std::string expectedType,
bool expectFailure)
{
std::string res;
std::string search = "Invalid resolution ";
try {
tq.setResolution(resolution);
}
catch (const ghoul::RuntimeError& e) {
res = e.message;
}
if (expectFailure) {
REQUIRE(res.find(search) != std::string::npos);
REQUIRE(res.find(expectedType) != std::string::npos);
}
else {
REQUIRE(res.find(search) == std::string::npos);
}
}
static void singleStartTimeTest1(openspace::globebrowsing::TimeQuantizer& tq,
std::string startTime, std::string expectedErrSubstring,
bool expectFailure)
{
std::string res;
try {
tq.setStartEndRange(startTime, startTime);
}
catch (const ghoul::RuntimeError& e) {
res = e.message;
}
if (expectFailure) {
REQUIRE(res.find(expectedErrSubstring) != std::string::npos);
}
else {
REQUIRE(res.find(expectedErrSubstring) == std::string::npos);
}
}
static void singleStartTimeTest2(std::string startTime, std::string expectedErrSubstring,
bool expectFailure)
{
using namespace openspace::globebrowsing;
std::string res;
try {
TimeQuantizer tq(startTime, startTime, "1d");
}
catch (const ghoul::RuntimeError& e) {
res = e.message;
}
if (expectFailure) {
REQUIRE(res.find(expectedErrSubstring) != std::string::npos);
}
else {
REQUIRE(res.find(expectedErrSubstring) == std::string::npos);
}
}
static void LoadSpiceKernel () {
openspace::SpiceManager::initialize();
loadLSKKernel();
// naif0008.tls is a text file, check if loaded.
SpiceBoolean found;
kdata_c(
0,
"text",
FILLEN,
TYPLEN,
SRCLEN,
spicemanager_constants::file,
spicemanager_constants::filtyp,
spicemanager_constants::source,
&spicemanager_constants::handle,
&found
);
REQUIRE(found == SPICETRUE);
openspace::SpiceManager::deinitialize();
}
TEST_CASE("TimeQuantizer: Test years resolution", "[timequantizer]") {
openspace::SpiceManager::initialize();
SpiceManager::initialize();
loadLSKKernel();
using namespace openspace::globebrowsing;
TimeQuantizer t1;
openspace::Time testT;
globebrowsing::TimeQuantizer t1;
Time testT;
t1.setStartEndRange("2019-12-09T00:00:00", "2030-03-01T00:00:00");
t1.setResolution("1y");
@@ -176,16 +150,15 @@ TEST_CASE("TimeQuantizer: Test years resolution", "[timequantizer]") {
singleTimeTest(testT, t1, true, "2028-12-08T23:59:59", "2025-12-09T00:00:00.000");
singleTimeTest(testT, t1, true, "2028-12-09T00:00:01", "2028-12-09T00:00:00.000");
openspace::SpiceManager::deinitialize();
SpiceManager::deinitialize();
}
TEST_CASE("TimeQuantizer: Test days resolution", "[timequantizer]") {
openspace::SpiceManager::initialize();
SpiceManager::initialize();
loadLSKKernel();
using namespace openspace::globebrowsing;
TimeQuantizer t1;
openspace::Time testT;
globebrowsing::TimeQuantizer t1;
Time testT;
t1.setStartEndRange("2019-12-09T00:00:00", "2020-03-01T00:00:00");
t1.setResolution("1d");
@@ -220,16 +193,15 @@ TEST_CASE("TimeQuantizer: Test days resolution", "[timequantizer]") {
singleTimeTest(testT, t1, true, "2020-03-01T00:30:00", "2020-03-01T00:00:00.000");
singleTimeTest(testT, t1, true, "2019-03-04T00:00:02", "2019-03-04T00:00:00.000");
openspace::SpiceManager::deinitialize();
SpiceManager::deinitialize();
}
TEST_CASE("TimeQuantizer: Test months resolution", "[timequantizer]") {
openspace::SpiceManager::initialize();
SpiceManager::initialize();
loadLSKKernel();
using namespace openspace::globebrowsing;
TimeQuantizer t1;
openspace::Time testT;
globebrowsing::TimeQuantizer t1;
Time testT;
t1.setStartEndRange("2017-01-28T00:00:00", "2020-09-01T00:00:00");
t1.setResolution("1M");
@@ -260,16 +232,15 @@ TEST_CASE("TimeQuantizer: Test months resolution", "[timequantizer]") {
singleTimeTest(testT, t1, true, "2017-05-30T04:15:45", "2017-05-28T00:00:00.000");
singleTimeTest(testT, t1, true, "2017-10-17T05:01:00", "2017-05-28T00:00:00.000");
openspace::SpiceManager::deinitialize();
SpiceManager::deinitialize();
}
TEST_CASE("TimeQuantizer: Test hours & minutes resolution", "[timequantizer]") {
openspace::SpiceManager::initialize();
SpiceManager::initialize();
loadLSKKernel();
using namespace openspace::globebrowsing;
TimeQuantizer t1;
openspace::Time testT;
globebrowsing::TimeQuantizer t1;
Time testT;
t1.setStartEndRange("2019-02-21T00:00:00", "2021-09-01T00:00:00");
t1.setResolution("2h");
@@ -300,15 +271,14 @@ TEST_CASE("TimeQuantizer: Test hours & minutes resolution", "[timequantizer]") {
singleTimeTest(testT, t1, true, "2019-02-28T22:29:59", "2019-02-28T22:15:00.000");
singleTimeTest(testT, t1, true, "2019-02-28T22:59:59", "2019-02-28T22:45:00.000");
openspace::SpiceManager::deinitialize();
SpiceManager::deinitialize();
}
TEST_CASE("TimeQuantizer: Test valid resolutions", "[timequantizer]") {
openspace::SpiceManager::initialize();
SpiceManager::initialize();
loadLSKKernel();
using namespace openspace::globebrowsing;
TimeQuantizer t1;
globebrowsing::TimeQuantizer t1;
singleResolutionTest(t1, "29d", "(d)ay option.", true);
singleResolutionTest(t1, "0d", "(d)ay option.", true);
@@ -324,35 +294,34 @@ TEST_CASE("TimeQuantizer: Test valid resolutions", "[timequantizer]") {
singleResolutionTest(t1, "31m", "(m)inute option.", true);
singleResolutionTest(t1, "10s", "unit format", true);
openspace::SpiceManager::deinitialize();
SpiceManager::deinitialize();
}
TEST_CASE("TimeQuantizer: Test start time pre-existing object", "[timequantizer]") {
openspace::SpiceManager::initialize();
SpiceManager::initialize();
loadLSKKernel();
using namespace openspace::globebrowsing;
TimeQuantizer t1;
globebrowsing::TimeQuantizer t1;
singleStartTimeTest1(t1, "2017-01-20T00:00:00", "Invalid start", false);
singleStartTimeTest1(t1, "2017-01-29T00:00:00", "Invalid start day value", true);
singleStartTimeTest1(t1, "2017-01-28T12:00:00", "Invalid start time value", true);
singleStartTimeTest1(t1, "2017-01-28T00:01:00", "Invalid start time value", true);
singleStartTimeTest1(t1, "2017-01-28T00:00:01", "Invalid start time value", true);
singleStartTimeTest(t1, "2017-01-20T00:00:00", "Invalid start", false);
singleStartTimeTest(t1, "2017-01-29T00:00:00", "Invalid start day value", true);
singleStartTimeTest(t1, "2017-01-28T12:00:00", "Invalid start time value", true);
singleStartTimeTest(t1, "2017-01-28T00:01:00", "Invalid start time value", true);
singleStartTimeTest(t1, "2017-01-28T00:00:01", "Invalid start time value", true);
openspace::SpiceManager::deinitialize();
SpiceManager::deinitialize();
}
TEST_CASE("TimeQuantizer: Test start time using constructor", "[timequantizer]") {
openspace::SpiceManager::initialize();
SpiceManager::initialize();
loadLSKKernel();
singleStartTimeTest2("2017-01-20T00:00:00", "Invalid start", false);
singleStartTimeTest2("2017-01-29T00:00:00", "Invalid start day value", true);
singleStartTimeTest2("2017-01-28T12:00:00", "Invalid start time value", true);
singleStartTimeTest2("2017-01-28T00:01:00", "Invalid start time value", true);
singleStartTimeTest2("2017-01-28T00:00:01", "Invalid start time value", true);
singleStartTimeTest("2017-01-20T00:00:00", "Invalid start", false);
singleStartTimeTest("2017-01-29T00:00:00", "Invalid start day value", true);
singleStartTimeTest("2017-01-28T12:00:00", "Invalid start time value", true);
singleStartTimeTest("2017-01-28T00:01:00", "Invalid start time value", true);
singleStartTimeTest("2017-01-28T00:00:01", "Invalid start time value", true);
openspace::SpiceManager::deinitialize();
SpiceManager::deinitialize();
}