/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2023 * * * * 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. * ****************************************************************************************/ #ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TIMEQUANTIZER___H__ #define __OPENSPACE_MODULE_GLOBEBROWSING___TIMEQUANTIZER___H__ #include #include #include 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 The time 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 Time& checkTime) const; /* * 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 */ const char* clamp(const char* 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_view 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_view 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(std::string_view initDateTime); /* * Set the date/time value * * \params input the ISO8601 date/time string (YYYY-MM-DDTHH:mm:ss) to set */ void setTime(std::string_view input); /* * 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: int _year = 2000; int _month = 1; int _day = 1; int _hour = 0; int _minute = 0; int _second = 0; }; /** * Used to quantize time to discrete values. */ class TimeQuantizer { public: TimeQuantizer() = default; /* * 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 * value representing the time resolution as seconds. * * Example: parseTimeResolutionStr("1d"); * * \param resolutionStr with the format {number}{unit} where supported units are: * (s)econds, (m)inutes, (h)ours, (d)ays, (y)ears * \return the time resolution in seconds */ double parseTimeResolutionStr(const std::string& resolutionStr); /** * Quantizes a OpenSpace Time into descrete values. If the provided Time \p t is * outside the time range, it will be clamped to the the time range. * * \param t Time instance, which will be quantized * \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); /** * 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 quantized(Time& start, Time& end); private: void verifyStartTimeRestrictions(); void verifyResolutionRestrictions(const int value, const char unit); void doFirstApproximation(DateTime& q, DateTime& unQ, double value, char unit); double computeSecondsFromResolution(const int valueIn, const char unit); double _resolution = 0.0; double _resolutionValue = 0.0; char _resolutionUnit = 'd'; DateTime _dt; DateTime _start; RangedTime _timerange; }; } // namespace openspace::globebrowsing #endif // __OPENSPACE_MODULE_GLOBEBROWSING___TIMEQUANTIZER___H__