mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-02-27 23:39:01 -06:00
@@ -421,7 +421,8 @@ std::string consumeTemporalMetaData(TemporalTileProvider& t, const std::string&
|
||||
}
|
||||
|
||||
try {
|
||||
t.timeQuantizer = TimeQuantizer(start, end, timeResolution);
|
||||
t.timeQuantizer.setStartEndRange(start.ISO8601(), end.ISO8601());
|
||||
t.timeQuantizer.setResolution(timeResolution);
|
||||
}
|
||||
catch (const ghoul::RuntimeError& e) {
|
||||
throw ghoul::RuntimeError(fmt::format(
|
||||
|
||||
@@ -25,22 +25,333 @@
|
||||
#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 <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
namespace openspace::globebrowsing {
|
||||
|
||||
TimeQuantizer::TimeQuantizer(const Time& start, const Time& end, double resolution)
|
||||
: _timerange(start.j2000Seconds(), end.j2000Seconds())
|
||||
, _resolution(resolution)
|
||||
{}
|
||||
namespace {
|
||||
|
||||
TimeQuantizer::TimeQuantizer(const Time& start, const Time& 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)
|
||||
: TimeQuantizer(start, end, parseTimeResolutionStr(resolution))
|
||||
{}
|
||||
: _start(start)
|
||||
, _timerange(std::move(start), std::move(end))
|
||||
{
|
||||
verifyStartTimeRestrictions();
|
||||
_resolution = parseTimeResolutionStr(resolution);
|
||||
}
|
||||
|
||||
double TimeQuantizer::parseTimeResolutionStr(const std::string& resolutionStr) {
|
||||
const char unit = resolutionStr.back();
|
||||
@@ -48,52 +359,158 @@ double TimeQuantizer::parseTimeResolutionStr(const std::string& resolutionStr) {
|
||||
|
||||
char* p;
|
||||
double value = strtol(numberString.c_str(), &p, 10);
|
||||
_resolutionValue = value;
|
||||
_resolutionUnit = unit;
|
||||
if (*p) { // not a number
|
||||
throw ghoul::RuntimeError("Cannot convert " + numberString + " to number");
|
||||
}
|
||||
else {
|
||||
// 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;
|
||||
default:
|
||||
throw ghoul::RuntimeError(
|
||||
"Invalid unit format '" + std::string(1, unit) +
|
||||
"'. Expected 'y', 'd', 'h', 'm' or 's'."
|
||||
);
|
||||
}
|
||||
return value;
|
||||
verifyResolutionRestrictions(static_cast<int>(value), unit);
|
||||
return computeSecondsFromResolution(static_cast<int>(value), unit);
|
||||
}
|
||||
}
|
||||
|
||||
bool TimeQuantizer::quantize(Time& t, bool clamp) const {
|
||||
const double unquantized = t.j2000Seconds();
|
||||
if (_timerange.includes(unquantized)) {
|
||||
const double quantized = std::floor((unquantized - _timerange.start) /
|
||||
_resolution) *
|
||||
_resolution + _timerange.start;
|
||||
t.setTime(quantized);
|
||||
void TimeQuantizer::setStartEndRange(const std::string& start, const std::string& end) {
|
||||
_start.setTime(start);
|
||||
verifyStartTimeRestrictions();
|
||||
|
||||
_timerange.setStart(start);
|
||||
_timerange.setEnd(end);
|
||||
}
|
||||
|
||||
void TimeQuantizer::setResolution(const std::string& resolutionString) {
|
||||
_resolution = parseTimeResolutionStr(resolutionString);
|
||||
}
|
||||
|
||||
void TimeQuantizer::verifyStartTimeRestrictions() {
|
||||
if (_start.day() < 1 || _start.day() > 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(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':
|
||||
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;
|
||||
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(fmt::format(
|
||||
"Invalid unit format '{}'. Expected 'y', 'M', 'd', 'h', or 'm'", unit
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
double TimeQuantizer::computeSecondsFromResolution(const int valueIn, const char unit) {
|
||||
double value = static_cast<double>(valueIn);
|
||||
// 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 'M':
|
||||
value *= (30.4 * 24.0 * 60.0 * 60.0);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw ghoul::RuntimeError(fmt::format(
|
||||
"Invalid resolution unit format '{}'. Expected 'y', 'M', 'd', 'h', 'm', "
|
||||
"or 's'", unit
|
||||
));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool TimeQuantizer::quantize(Time& t, bool clamp) {
|
||||
const std::string unquantizedStr = t.ISO8601();
|
||||
DateTime unquantized(unquantizedStr);
|
||||
//resolutionFraction helps to improve iteration performance
|
||||
const double resolutionFraction = 0.7;
|
||||
double error = 0.0;
|
||||
const int iterationLimit = 50;
|
||||
int iterations = 0;
|
||||
int lastIncr = 0;
|
||||
int lastDecr = 0;
|
||||
|
||||
if (_timerange.includes(unquantizedStr)) {
|
||||
DateTime quantized = DateTime(_timerange.start());
|
||||
doFirstApproximation(quantized, unquantized, _resolutionValue, _resolutionUnit);
|
||||
error = diff(quantized, unquantized);
|
||||
while (error > (_resolution * resolutionFraction) || error < 0) {
|
||||
if (error > 0) {
|
||||
lastIncr = quantized.increment(static_cast<int>(_resolutionValue),
|
||||
_resolutionUnit, error, _resolution);
|
||||
}
|
||||
else if (error < 0) {
|
||||
lastDecr = quantized.decrement(static_cast<int>(_resolutionValue),
|
||||
_resolutionUnit, error, _resolution);
|
||||
}
|
||||
error = diff(quantized, unquantized);
|
||||
bool hasSettled = (lastIncr == 1 && lastDecr == 1 && error >= 0.0);
|
||||
iterations++;
|
||||
if (hasSettled || iterations > iterationLimit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
quantized.setTime(_timerange.clamp(quantized.ISO8601()));
|
||||
t.setTime(quantized.J2000());
|
||||
return true;
|
||||
}
|
||||
else if (clamp) {
|
||||
const double clampedTime = glm::clamp(
|
||||
unquantized,
|
||||
_timerange.start,
|
||||
_timerange.end
|
||||
);
|
||||
const std::string clampedTime = _timerange.clamp(unquantizedStr);
|
||||
t.setTime(clampedTime);
|
||||
return true;
|
||||
}
|
||||
@@ -102,23 +519,101 @@ bool TimeQuantizer::quantize(Time& t, bool clamp) const {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Time> TimeQuantizer::quantized(const Time& start, const Time& end) const {
|
||||
Time s = start;
|
||||
quantize(s, true);
|
||||
double TimeQuantizer::diff(DateTime& from, DateTime& to) {
|
||||
return to.J2000() - from.J2000();
|
||||
}
|
||||
|
||||
Time e = end;
|
||||
quantize(e, true);
|
||||
void TimeQuantizer::doFirstApproximation(DateTime& quantized, DateTime& unQ,
|
||||
double value, char unit)
|
||||
{
|
||||
double minYearsToAdjust;
|
||||
double minIncrementsAdjust;
|
||||
bool isSimMonthPastQuantizedMonth;
|
||||
double error = 0.0;
|
||||
int originalHour, originalMinute, originalSecond;
|
||||
Time testDay;
|
||||
double addToTime;
|
||||
|
||||
const double startSeconds = s.j2000Seconds();
|
||||
const double endSeconds = e.j2000Seconds();
|
||||
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) / (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
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> TimeQuantizer::quantized(Time& start, Time& end) {
|
||||
DateTime s(start.ISO8601());
|
||||
quantize(start, true);
|
||||
|
||||
DateTime e(end.ISO8601());
|
||||
quantize(end, true);
|
||||
|
||||
const double startSeconds = s.J2000();
|
||||
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<Time> result(nSteps + 1);
|
||||
for (int i = 0; i <= nSteps; ++i) {
|
||||
result[i].setTime(startSeconds + i * _resolution);
|
||||
std::vector<std::string> result;
|
||||
DateTime itr = s;
|
||||
RangedTime range(start.ISO8601(), end.ISO8601());
|
||||
while (range.includes(itr.ISO8601())) {
|
||||
itr.incrementOnce(static_cast<int>(_resolutionValue), _resolutionUnit);
|
||||
result.push_back(itr.ISO8601());
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -33,13 +33,306 @@ namespace openspace { class Time; }
|
||||
|
||||
namespace openspace::globebrowsing {
|
||||
|
||||
/* RangedTime class is used to define an acceptable time range. Functionality includes
|
||||
* checking if a given date/time is within that range, or clamping a date to enforce
|
||||
* this range.
|
||||
*/
|
||||
class RangedTime {
|
||||
public:
|
||||
RangedTime() {};
|
||||
|
||||
/*
|
||||
* Constructor that accepts an ISO8601 date/time string (YYYY-MM-DDTHH:mm:ss) for an
|
||||
* allowable time range defined by start and end values.
|
||||
*
|
||||
* \param start The date/time start of the time range.
|
||||
* \param end The date/time ending of the time range.
|
||||
*/
|
||||
RangedTime(std::string start, std::string end);
|
||||
|
||||
/*
|
||||
* Checks if a date/time value falls within the start/end range defined in this
|
||||
* instance of the class.
|
||||
*
|
||||
* \param checkTime An ISO8601 date/time string to test if it falls within the range
|
||||
*
|
||||
* \returns true if the input date/time falls between the start and end date/times
|
||||
*/
|
||||
bool includes(const std::string& checkTime);
|
||||
|
||||
/*
|
||||
* Enforces the start/end range on a given date/time string by clamping the value
|
||||
*
|
||||
* \param checkTime An ISO8601 date/time string to clamp if falls outside of range
|
||||
*
|
||||
* \returns clamped value of input parameter, will be equal to the start value if
|
||||
* less than start, equal to end if greater than end, or equal to input
|
||||
* parameter if falls in-between
|
||||
*/
|
||||
std::string clamp(const std::string& checkTime);
|
||||
|
||||
/*
|
||||
* Get the start date/time of the time range
|
||||
*
|
||||
* \returns The ISO8601 date/time string that defines the start of the range
|
||||
*/
|
||||
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() const;
|
||||
|
||||
/*
|
||||
* Set the start date/time of the time range
|
||||
*
|
||||
* \param The ISO8601 date/time string that defines the start of the range
|
||||
*/
|
||||
void setStart(const std::string start);
|
||||
|
||||
/*
|
||||
* Set the end date/time of the time range
|
||||
*
|
||||
* \param The ISO8601 date/time string that defines the end of the range
|
||||
*/
|
||||
void setEnd(const std::string start);
|
||||
|
||||
private:
|
||||
std::string _start;
|
||||
std::string _end;
|
||||
double _startJ2000;
|
||||
double _endJ2000;
|
||||
};
|
||||
|
||||
/* DateTime class is used to manage a date/time value and provide methods for increment/
|
||||
* decrementing the value, which gets complicated because of the varying days of the
|
||||
* different months, leap years, etc.
|
||||
* This class exists to handle date/time values within a "people-friendly" calendar
|
||||
* schedule. For example, a temporal data set that's updated on the 10th of every month
|
||||
* will sometimes be updated in 28 days, other times in 31 days. The intent of the class
|
||||
* is to handle all of the "special cases" where simply using J2000 seconds won't work.
|
||||
*/
|
||||
class DateTime {
|
||||
public:
|
||||
DateTime() = default;
|
||||
/*
|
||||
* Constructor that initializes with date/time string
|
||||
*
|
||||
* \params initDateTime the ISO8601 date/time string (YYYY-MM-DDTHH:mm:ss)
|
||||
*/
|
||||
DateTime(const std::string& initDateTime);
|
||||
|
||||
/*
|
||||
* Set the date/time value
|
||||
*
|
||||
* \params input the ISO8601 date/time string (YYYY-MM-DDTHH:mm:ss) to set
|
||||
*/
|
||||
void setTime(const std::string& input);
|
||||
|
||||
/*
|
||||
* Used to deep-copy from another DateTime instance
|
||||
*
|
||||
* \params src the DateTime object to copy from
|
||||
*/
|
||||
void operator=(DateTime& src);
|
||||
|
||||
/*
|
||||
* Get the date/time value in ISO8601 format
|
||||
*
|
||||
* \returns the date/time value string
|
||||
*/
|
||||
std::string ISO8601() const;
|
||||
|
||||
/*
|
||||
* Get the J2000 seconds equivalent of the object's date/time, using
|
||||
* the loaded SPICE kernel
|
||||
*
|
||||
* \returns J2000 seconds of date/time
|
||||
*/
|
||||
double J2000() const;
|
||||
|
||||
/*
|
||||
* Get the year of the object's date/time (YYYY format)
|
||||
* \returns integer value of the year
|
||||
*/
|
||||
int year() const;
|
||||
|
||||
/*
|
||||
* Get the month of the object's date/time (1 - 12)
|
||||
* \returns integer value of the 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() const;
|
||||
|
||||
/*
|
||||
* Get the hour of the object's date/time
|
||||
* \returns integer value of the hour (0 - 23)
|
||||
*/
|
||||
int hour() const;
|
||||
|
||||
/*
|
||||
* Get the minute of the object's date/time
|
||||
* \returns integer value of the minutes
|
||||
*/
|
||||
int minute() const;
|
||||
|
||||
/*
|
||||
* Get the seconds of the object's date/time
|
||||
* \returns integer value of the seconds
|
||||
*/
|
||||
int second() const;
|
||||
|
||||
/*
|
||||
* Set the year of the object's date/time
|
||||
* \param y integer value of the year
|
||||
*/
|
||||
void setYear(int y);
|
||||
|
||||
/*
|
||||
* Set the month of the object's date/time (1 - 12)
|
||||
* \param m integer value of the year
|
||||
*/
|
||||
void setMonth(int m);
|
||||
|
||||
/*
|
||||
* Set the day-of-month of the object's date/time (1 - 31)
|
||||
* \param d integer value of the day
|
||||
*/
|
||||
void setDay(int d);
|
||||
|
||||
/*
|
||||
* Set the hour of the object's date/time (0 - 23)
|
||||
* \param h integer value of the hour
|
||||
*/
|
||||
void setHour(int h);
|
||||
|
||||
/*
|
||||
* Set the minute of the object's date/time
|
||||
* \param m integer value of the minute
|
||||
*/
|
||||
void setMinute(int m);
|
||||
|
||||
/*
|
||||
* Set the seconds of the object's date/time
|
||||
* \param s integer value of the seconds
|
||||
*/
|
||||
void setSecond(int s);
|
||||
|
||||
/*
|
||||
* Increment operation for the date/time
|
||||
*
|
||||
* \param value integer value for number of units in an operation
|
||||
* \param unit single char that specifies the unit of increment. Allowable units are:
|
||||
* (y)ear, (M)onth, (d)ay, (h)our, (m)inute, (s)econd
|
||||
* \param error The difference in J2000 seconds from current date/time to target
|
||||
* (a positive value means target is in the future)
|
||||
* \param resolution The J2000 seconds of the interval defined by the value & unit
|
||||
*
|
||||
* \returns The number of increments that were performed in order to get as close as
|
||||
* possible to the target, where each increment is defined by the value &
|
||||
* unit (and approximated but not fixed by the resolution param)
|
||||
*/
|
||||
int increment(int value, char unit, double error, double resolution);
|
||||
|
||||
/*
|
||||
* Decrement operation for the date/time
|
||||
*
|
||||
* \param value integer value for number of units in an operation
|
||||
* \param unit single char that specifies the unit of decrement. Allowable units are:
|
||||
* (y)ear, (M)onth, (d)ay, (h)our, (m)inute, (s)econd
|
||||
* \param error The difference in J2000 seconds from current date/time to target
|
||||
* (a positive value means target is in the future)
|
||||
* \param resolution The J2000 seconds of the interval defined by the value & unit
|
||||
*
|
||||
* \returns The number of decrements that were performed in order to get as close as
|
||||
* possible to the target, where each decrement is defined by the value &
|
||||
* unit (and approximated but not fixed by the resolution param)
|
||||
*/
|
||||
int decrement(int value, char unit, double error, double resolution);
|
||||
|
||||
/*
|
||||
* Single increment operation for the date/time
|
||||
*
|
||||
* \param value integer value for number of units in an operation
|
||||
* \param unit single char that specifies the unit of increment. Allowable units are:
|
||||
* (y)ear, (M)onth, (d)ay, (h)our, (m)inute, (s)econd
|
||||
*/
|
||||
void incrementOnce(int value, char unit);
|
||||
|
||||
/*
|
||||
* Single decrement operation for the date/time
|
||||
*
|
||||
* \param value integer value for number of units in an operation
|
||||
* \param unit single char that specifies the unit of decrement. Allowable units are:
|
||||
* (y)ear, (M)onth, (d)ay, (h)our, (m)inute, (s)econd
|
||||
*/
|
||||
void decrementOnce(int value, char unit);
|
||||
|
||||
private:
|
||||
// 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;
|
||||
const int index_hour = 11;
|
||||
const int index_minute = 14;
|
||||
const int index_second = 17;
|
||||
|
||||
const int len_year = 4;
|
||||
const int len_nonYear = 2;
|
||||
|
||||
int _year = 2000;
|
||||
int _month = 1;
|
||||
int _day = 1;
|
||||
int _hour = 0;
|
||||
int _minute = 0;
|
||||
int _second = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to quantize time to descrete values.
|
||||
* Used to quantize time to discrete values.
|
||||
*/
|
||||
struct TimeQuantizer {
|
||||
class TimeQuantizer {
|
||||
public:
|
||||
TimeQuantizer() = default;
|
||||
TimeQuantizer(const Time& start, const Time& end, double resolution);
|
||||
TimeQuantizer(const Time& start, const Time& end, const std::string& resolution);
|
||||
/*
|
||||
* Constructor that initializes with formatted strings for start & ends date/times,
|
||||
* and a time resolution within that range
|
||||
*
|
||||
* \params start the ISO8601 date/time string (YYYY-MM-DDTHH:mm:ss) for start
|
||||
* \params end the ISO8601 date/time string (YYYY-MM-DDTHH:mm:ss) for end
|
||||
* \params resolution the formatted resolution, which consists of an integer & unit
|
||||
* character. The acceptable unit characters are:
|
||||
* (y)ear Example: '1y' = 1 year. No range limitations
|
||||
* (M)onth Example: '4M' = 4 months. Allowable values: 1, 2, 3, 4, 6
|
||||
* (d)ay Example: '10d' = 10 days. Allowable values: 1 - 28
|
||||
* (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(std::string start, std::string end, const std::string& resolution);
|
||||
|
||||
/*
|
||||
* Set the time range start & end date/time range.
|
||||
*
|
||||
* \param start The ISO8601 date/time string for start of the time range
|
||||
* \param end The ISO8601 date/time string for end of the time range.
|
||||
*/
|
||||
void setStartEndRange(const std::string& start, const std::string& end);
|
||||
|
||||
/*
|
||||
* Set the time resolution
|
||||
*
|
||||
* \param resolutionString String that defines the resolution within the time range.
|
||||
* see comment header for constructor for the allowable
|
||||
* values and ranges.
|
||||
*/
|
||||
void setResolution(const std::string& resolutionString);
|
||||
|
||||
/**
|
||||
* Takes a time resulition string and parses it into a double
|
||||
@@ -51,7 +344,7 @@ struct TimeQuantizer {
|
||||
* (s)econds, (m)inutes, (h)ours, (d)ays, (y)ears
|
||||
* \return the time resolution in seconds
|
||||
*/
|
||||
static double parseTimeResolutionStr(const std::string& resolutionStr);
|
||||
double parseTimeResolutionStr(const std::string& resolutionStr);
|
||||
|
||||
/**
|
||||
* Quantizes a OpenSpace Time into descrete values. If the provided Time \p t is
|
||||
@@ -61,21 +354,30 @@ struct TimeQuantizer {
|
||||
* \param clamp Whether or not time should be clamped if not t is in the time range
|
||||
* \return wether or not time was quantized
|
||||
*/
|
||||
bool quantize(Time& t, bool clamp) const;
|
||||
bool quantize(Time& t, bool clamp);
|
||||
|
||||
/**
|
||||
* Returns a list of quantized Time objects that represent all the valid quantized
|
||||
* Time%s between \p start and \p end.
|
||||
* Returns a list of quantized Time strings that represent all the valid quantized
|
||||
* time%s between \p start and \p end.
|
||||
*
|
||||
* \param start The start time for the time range quantization
|
||||
* \param end The end time for the time range quantization
|
||||
* \return A list of quantized times between \p start and \end
|
||||
*/
|
||||
std::vector<Time> quantized(const Time& start, const Time& end) const;
|
||||
std::vector<std::string> quantized(Time& start, Time& end);
|
||||
|
||||
private:
|
||||
TimeRange _timerange;
|
||||
double _resolution;
|
||||
void verifyStartTimeRestrictions();
|
||||
void verifyResolutionRestrictions(const int value, const char unit);
|
||||
double diff(DateTime& from, DateTime& to);
|
||||
void doFirstApproximation(DateTime& q, DateTime& unQ, double value, char unit);
|
||||
RangedTime _timerange;
|
||||
double computeSecondsFromResolution(const int valueIn, const char unit);
|
||||
double _resolution = 0.0;
|
||||
double _resolutionValue = 0.0;
|
||||
char _resolutionUnit = 'd';
|
||||
DateTime _dt;
|
||||
DateTime _start;
|
||||
};
|
||||
|
||||
} // namespace openspace::globebrowsing
|
||||
|
||||
@@ -38,6 +38,7 @@ add_executable(
|
||||
test_scriptscheduler.cpp
|
||||
test_spicemanager.cpp
|
||||
test_temporaltileprovider.cpp
|
||||
test_timequantizer.cpp
|
||||
test_timeline.cpp
|
||||
|
||||
regression/517.cpp
|
||||
|
||||
327
tests/test_timequantizer.cpp
Normal file
327
tests/test_timequantizer.cpp
Normal file
@@ -0,0 +1,327 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* 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 *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#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;
|
||||
constexpr const int SRCLEN = 128;
|
||||
|
||||
namespace spicemanager_constants {
|
||||
const int nrMetaKernels = 9;
|
||||
SpiceInt which, handle, count = 0;
|
||||
char file[FILLEN], filtyp[TYPLEN], source[SRCLEN];
|
||||
double abs_error = 0.00001;
|
||||
} // namespace spicemanager_constants
|
||||
|
||||
int loadLSKKernel() {
|
||||
int kernelID = openspace::SpiceManager::ref().loadKernel(
|
||||
absPath("${TESTDIR}/SpiceTest/spicekernels/naif0008.tls")
|
||||
);
|
||||
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
|
||||
|
||||
TEST_CASE("TimeQuantizer: Test years resolution", "[timequantizer]") {
|
||||
SpiceManager::initialize();
|
||||
|
||||
loadLSKKernel();
|
||||
globebrowsing::TimeQuantizer t1;
|
||||
Time testT;
|
||||
|
||||
t1.setStartEndRange("2019-12-09T00:00:00", "2030-03-01T00:00:00");
|
||||
t1.setResolution("1y");
|
||||
|
||||
singleTimeTest(testT, t1, true, "2020-12-08T23:59:59", "2019-12-09T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2020-12-09T00:00:00", "2020-12-09T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2021-12-08T23:59:58", "2020-12-09T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2022-12-09T00:00:02", "2022-12-09T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2022-11-08T13:00:15", "2021-12-09T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2020-12-09T00:00:00", "2020-12-09T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2024-12-08T23:59:59", "2023-12-09T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2024-12-09T00:00:01", "2024-12-09T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2020-12-31T00:00:01", "2020-12-09T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2021-01-01T00:00:00", "2020-12-09T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2020-12-31T23:59:59", "2020-12-09T00:00:00.000");
|
||||
|
||||
t1.setResolution("3y");
|
||||
|
||||
singleTimeTest(testT, t1, true, "2020-12-08T23:59:59", "2019-12-09T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2022-12-09T00:00:00", "2022-12-09T00:00:00.000");
|
||||
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");
|
||||
|
||||
SpiceManager::deinitialize();
|
||||
}
|
||||
|
||||
TEST_CASE("TimeQuantizer: Test days resolution", "[timequantizer]") {
|
||||
SpiceManager::initialize();
|
||||
|
||||
loadLSKKernel();
|
||||
globebrowsing::TimeQuantizer t1;
|
||||
Time testT;
|
||||
|
||||
t1.setStartEndRange("2019-12-09T00:00:00", "2020-03-01T00:00:00");
|
||||
t1.setResolution("1d");
|
||||
|
||||
singleTimeTest(testT, t1, true, "2020-01-07T05:15:45", "2020-01-07T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2020-01-07T00:00:00", "2020-01-07T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2020-01-07T00:00:01", "2020-01-07T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2020-01-06T23:59:59", "2020-01-06T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2020-01-31T23:59:59", "2020-01-31T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2020-02-01T00:00:00", "2020-02-01T00:00:00.000");
|
||||
singleTimeTest(testT, t1, false, "2020-02-01T00:00:00", "2020-02-01T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2020-02-29T00:00:02", "2020-02-29T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2020-01-01T00:00:00", "2020-01-01T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2020-03-02T14:00:00", "2020-03-01T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2019-12-15T14:00:00", "2019-12-15T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2019-12-08T23:59:00", "2019-12-09T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2019-12-05T14:29:00", "2019-12-09T00:00:00.000");
|
||||
|
||||
t1.setStartEndRange("2016-05-28T00:00:00", "2021-09-01T00:00:00");
|
||||
t1.setResolution("4d");
|
||||
|
||||
singleTimeTest(testT, t1, true, "2016-06-01T00:00:00", "2016-06-01T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2016-06-01T00:00:01", "2016-06-01T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2016-07-03T10:00:00", "2016-07-03T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2016-07-07T00:00:00", "2016-07-07T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2021-11-07T00:00:00", "2021-09-01T00:00:00.000");
|
||||
singleTimeTest(testT, t1, false, "2021-11-07T00:00:00", "2021-11-07T00:00:00.000");
|
||||
|
||||
t1.setStartEndRange("2019-02-21T00:00:00", "2021-09-01T00:00:00");
|
||||
t1.setResolution("11d");
|
||||
|
||||
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");
|
||||
|
||||
SpiceManager::deinitialize();
|
||||
}
|
||||
|
||||
TEST_CASE("TimeQuantizer: Test months resolution", "[timequantizer]") {
|
||||
SpiceManager::initialize();
|
||||
|
||||
loadLSKKernel();
|
||||
globebrowsing::TimeQuantizer t1;
|
||||
Time testT;
|
||||
|
||||
t1.setStartEndRange("2017-01-28T00:00:00", "2020-09-01T00:00:00");
|
||||
t1.setResolution("1M");
|
||||
|
||||
singleTimeTest(testT, t1, true, "2017-03-03T05:15:45", "2017-02-28T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2017-03-29T00:15:45", "2017-03-28T00:00:00.000");
|
||||
|
||||
t1.setStartEndRange("2016-01-17T00:00:00", "2020-09-01T00:00:00");
|
||||
t1.setResolution("2M");
|
||||
|
||||
singleTimeTest(testT, t1, true, "2016-01-27T05:15:45", "2016-01-17T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2016-03-16T08:15:45", "2016-01-17T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2016-03-17T18:00:02", "2016-03-17T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2016-05-18T00:00:02", "2016-05-17T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2016-11-17T10:15:45", "2016-11-17T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2017-01-18T05:15:45", "2017-01-17T00:00:00.000");
|
||||
|
||||
t1.setResolution("3M");
|
||||
|
||||
singleTimeTest(testT, t1, true, "2016-04-16T05:15:45", "2016-01-17T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2016-07-27T05:15:45", "2016-07-17T00:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2017-10-17T00:01:00", "2017-10-17T00:00:00.000");
|
||||
|
||||
t1.setStartEndRange("2016-05-28T00:00:00", "2021-09-01T00:00:00");
|
||||
t1.setResolution("6M");
|
||||
|
||||
singleTimeTest(testT, t1, true, "2016-11-28T00:00:05", "2016-11-28T00:00:00.000");
|
||||
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");
|
||||
|
||||
SpiceManager::deinitialize();
|
||||
}
|
||||
|
||||
TEST_CASE("TimeQuantizer: Test hours & minutes resolution", "[timequantizer]") {
|
||||
SpiceManager::initialize();
|
||||
|
||||
loadLSKKernel();
|
||||
globebrowsing::TimeQuantizer t1;
|
||||
Time testT;
|
||||
|
||||
t1.setStartEndRange("2019-02-21T00:00:00", "2021-09-01T00:00:00");
|
||||
t1.setResolution("2h");
|
||||
|
||||
singleTimeTest(testT, t1, true, "2019-02-28T16:10:00", "2019-02-28T16:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2019-02-28T22:00:00", "2019-02-28T22:00:00.000");
|
||||
|
||||
t1.setResolution("3h");
|
||||
|
||||
singleTimeTest(testT, t1, true, "2019-02-28T21:10:00", "2019-02-28T21:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2019-02-28T12:00:00", "2019-02-28T12:00:00.000");
|
||||
|
||||
t1.setStartEndRange("2019-02-21T00:00:00", "2021-09-01T00:00:00");
|
||||
t1.setResolution("30m");
|
||||
|
||||
singleTimeTest(testT, t1, true, "2019-02-27T16:40:00", "2019-02-27T16:30:00.000");
|
||||
singleTimeTest(testT, t1, true, "2019-02-28T22:00:00", "2019-02-28T22:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2019-02-28T22:30:01", "2019-02-28T22:30:00.000");
|
||||
singleTimeTest(testT, t1, true, "2019-02-28T21:29:59", "2019-02-28T21:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2019-02-28T22:59:59", "2019-02-28T22:30:00.000");
|
||||
|
||||
t1.setResolution("15m");
|
||||
|
||||
singleTimeTest(testT, t1, true, "2019-02-28T16:40:00", "2019-02-28T16:30:00.000");
|
||||
singleTimeTest(testT, t1, true, "2019-02-28T22:00:00", "2019-02-28T22:00:00.000");
|
||||
singleTimeTest(testT, t1, true, "2019-02-28T22:30:01", "2019-02-28T22:30:00.000");
|
||||
singleTimeTest(testT, t1, true, "2019-02-28T22:15:01", "2019-02-28T22:15:00.000");
|
||||
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");
|
||||
|
||||
SpiceManager::deinitialize();
|
||||
}
|
||||
|
||||
TEST_CASE("TimeQuantizer: Test valid resolutions", "[timequantizer]") {
|
||||
SpiceManager::initialize();
|
||||
|
||||
loadLSKKernel();
|
||||
globebrowsing::TimeQuantizer t1;
|
||||
|
||||
singleResolutionTest(t1, "29d", "(d)ay option.", true);
|
||||
singleResolutionTest(t1, "0d", "(d)ay option.", true);
|
||||
singleResolutionTest(t1, "5h", "(h)our option.", true);
|
||||
singleResolutionTest(t1, "11h", "(h)our option.", true);
|
||||
singleResolutionTest(t1, "12h", "(h)our option.", false);
|
||||
singleResolutionTest(t1, "78y", "(y)ear option.", false);
|
||||
singleResolutionTest(t1, "12m", "(m)inute option.", true);
|
||||
singleResolutionTest(t1, "1m", "(m)inute option.", true);
|
||||
singleResolutionTest(t1, "0m", "(m)inute option.", true);
|
||||
singleResolutionTest(t1, "15m", "(m)inute option.", false);
|
||||
singleResolutionTest(t1, "30m", "(m)inute option.", false);
|
||||
singleResolutionTest(t1, "31m", "(m)inute option.", true);
|
||||
singleResolutionTest(t1, "10s", "unit format", true);
|
||||
|
||||
SpiceManager::deinitialize();
|
||||
}
|
||||
|
||||
TEST_CASE("TimeQuantizer: Test start time pre-existing object", "[timequantizer]") {
|
||||
SpiceManager::initialize();
|
||||
|
||||
loadLSKKernel();
|
||||
globebrowsing::TimeQuantizer t1;
|
||||
|
||||
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);
|
||||
|
||||
SpiceManager::deinitialize();
|
||||
}
|
||||
|
||||
TEST_CASE("TimeQuantizer: Test start time using constructor", "[timequantizer]") {
|
||||
SpiceManager::initialize();
|
||||
|
||||
loadLSKKernel();
|
||||
|
||||
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);
|
||||
|
||||
SpiceManager::deinitialize();
|
||||
}
|
||||
Reference in New Issue
Block a user