mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 16:00:08 +00:00
Adding robust date calculation to WMO header
This commit is contained in:
parent
c00016cb69
commit
7e9895e002
3 changed files with 178 additions and 72 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
|
@ -45,8 +46,41 @@ public:
|
||||||
std::string product_category() const;
|
std::string product_category() const;
|
||||||
std::string product_designator() const;
|
std::string product_designator() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the WMO date/time
|
||||||
|
*
|
||||||
|
* Gets the WMO date/time. Uses the optional date hint provided via
|
||||||
|
* SetDateHint(std::chrono::year_month). If the date hint has not been
|
||||||
|
* provided, the endTimeHint parameter is required.
|
||||||
|
*
|
||||||
|
* @param [in] endTimeHint The optional end time bounds to provide. This is
|
||||||
|
* ignored if a date hint has been provided to determine an absolute date.
|
||||||
|
*/
|
||||||
|
std::chrono::sys_time<std::chrono::minutes> GetDateTime(
|
||||||
|
std::optional<std::chrono::system_clock::time_point> endTimeHint =
|
||||||
|
std::nullopt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse a WMO header
|
||||||
|
*
|
||||||
|
* @param [in] is The input stream to parse
|
||||||
|
*/
|
||||||
bool Parse(std::istream& is);
|
bool Parse(std::istream& is);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Provide a date hint for the WMO parser
|
||||||
|
*
|
||||||
|
* The WMO header contains a date/time in the format DDMMSS. The year and
|
||||||
|
* month must be derived using another source. The date hint provides the
|
||||||
|
* additional context required to determine the absolute product time.
|
||||||
|
*
|
||||||
|
* This function will update any absolute date/time already calculated, or
|
||||||
|
* affect the calculation of a subsequent absolute date/time.
|
||||||
|
*
|
||||||
|
* @param [in] dateHint The date hint to provide the WMO header parser
|
||||||
|
*/
|
||||||
|
void SetDateHint(std::chrono::year_month dateHint);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<WmoHeaderImpl> p;
|
std::unique_ptr<WmoHeaderImpl> p;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -119,71 +119,11 @@ std::chrono::system_clock::time_point Segment::event_begin() const
|
||||||
// If event begin is 000000T0000Z
|
// If event begin is 000000T0000Z
|
||||||
if (eventBegin == std::chrono::system_clock::time_point {})
|
if (eventBegin == std::chrono::system_clock::time_point {})
|
||||||
{
|
{
|
||||||
using namespace std::chrono;
|
|
||||||
|
|
||||||
// Determine event end from P-VTEC string
|
// Determine event end from P-VTEC string
|
||||||
system_clock::time_point eventEnd =
|
std::chrono::system_clock::time_point eventEnd =
|
||||||
header_->vtecString_[0].pVtec_.event_end();
|
header_->vtecString_[0].pVtec_.event_end();
|
||||||
|
|
||||||
auto endDays = floor<days>(eventEnd);
|
eventBegin = wmoHeader_->GetDateTime(eventEnd);
|
||||||
year_month_day endDate {endDays};
|
|
||||||
|
|
||||||
// Determine WMO date/time
|
|
||||||
std::string wmoDateTime = wmoHeader_->date_time();
|
|
||||||
|
|
||||||
bool wmoDateTimeValid = false;
|
|
||||||
unsigned int dayOfMonth = 0;
|
|
||||||
unsigned long beginHour = 0;
|
|
||||||
unsigned long beginMinute = 0;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// WMO date time is in the format DDHHMM
|
|
||||||
dayOfMonth =
|
|
||||||
static_cast<unsigned int>(std::stoul(wmoDateTime.substr(0, 2)));
|
|
||||||
beginHour = std::stoul(wmoDateTime.substr(2, 2));
|
|
||||||
beginMinute = std::stoul(wmoDateTime.substr(4, 2));
|
|
||||||
wmoDateTimeValid = true;
|
|
||||||
}
|
|
||||||
catch (const std::exception&)
|
|
||||||
{
|
|
||||||
logger_->warn("Malformed WMO date/time: {}", wmoDateTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wmoDateTimeValid)
|
|
||||||
{
|
|
||||||
// Combine end date year and month with WMO date time
|
|
||||||
eventBegin =
|
|
||||||
sys_days {endDate.year() / endDate.month() / day {dayOfMonth}} +
|
|
||||||
hours {beginHour} + minutes {beginMinute};
|
|
||||||
|
|
||||||
// If the begin date is after the end date, assume the start time
|
|
||||||
// was the previous month (give a 1 day grace period for expiring
|
|
||||||
// events in the past)
|
|
||||||
if (eventBegin > eventEnd + 24h)
|
|
||||||
{
|
|
||||||
// If the current end month is January
|
|
||||||
if (endDate.month() == January)
|
|
||||||
{
|
|
||||||
// The begin month must be December of last year
|
|
||||||
eventBegin =
|
|
||||||
sys_days {
|
|
||||||
year {static_cast<int>((endDate.year() - 1y).count())} /
|
|
||||||
December / day {dayOfMonth}} +
|
|
||||||
hours {beginHour} + minutes {beginMinute};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Back up one month
|
|
||||||
eventBegin =
|
|
||||||
sys_days {endDate.year() /
|
|
||||||
month {static_cast<unsigned int>(
|
|
||||||
(endDate.month() - month {1}).count())} /
|
|
||||||
day {dayOfMonth}} +
|
|
||||||
hours {beginHour} + minutes {beginMinute};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,17 +42,26 @@ public:
|
||||||
WmoHeaderImpl(const WmoHeaderImpl&&) = delete;
|
WmoHeaderImpl(const WmoHeaderImpl&&) = delete;
|
||||||
WmoHeaderImpl& operator=(const WmoHeaderImpl&&) = delete;
|
WmoHeaderImpl& operator=(const WmoHeaderImpl&&) = delete;
|
||||||
|
|
||||||
|
void CalculateAbsoluteDateTime();
|
||||||
|
bool ParseDateTime(unsigned int& dayOfMonth,
|
||||||
|
unsigned long& hour,
|
||||||
|
unsigned long& minute);
|
||||||
|
|
||||||
bool operator==(const WmoHeaderImpl& o) const;
|
bool operator==(const WmoHeaderImpl& o) const;
|
||||||
|
|
||||||
std::string sequenceNumber_;
|
std::string sequenceNumber_ {};
|
||||||
std::string dataType_;
|
std::string dataType_ {};
|
||||||
std::string geographicDesignator_;
|
std::string geographicDesignator_ {};
|
||||||
std::string bulletinId_;
|
std::string bulletinId_ {};
|
||||||
std::string icao_;
|
std::string icao_ {};
|
||||||
std::string dateTime_;
|
std::string dateTime_ {};
|
||||||
std::string bbbIndicator_;
|
std::string bbbIndicator_ {};
|
||||||
std::string productCategory_;
|
std::string productCategory_ {};
|
||||||
std::string productDesignator_;
|
std::string productDesignator_ {};
|
||||||
|
|
||||||
|
std::optional<std::chrono::year_month> dateHint_ {};
|
||||||
|
std::optional<std::chrono::sys_time<std::chrono::minutes>>
|
||||||
|
absoluteDateTime_ {};
|
||||||
};
|
};
|
||||||
|
|
||||||
WmoHeader::WmoHeader() : p(std::make_unique<WmoHeaderImpl>()) {}
|
WmoHeader::WmoHeader() : p(std::make_unique<WmoHeaderImpl>()) {}
|
||||||
|
|
@ -124,6 +133,71 @@ std::string WmoHeader::product_designator() const
|
||||||
return p->productDesignator_;
|
return p->productDesignator_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::chrono::sys_time<std::chrono::minutes> WmoHeader::GetDateTime(
|
||||||
|
std::optional<std::chrono::system_clock::time_point> endTimeHint)
|
||||||
|
{
|
||||||
|
std::chrono::sys_time<std::chrono::minutes> wmoDateTime {};
|
||||||
|
|
||||||
|
if (p->absoluteDateTime_.has_value())
|
||||||
|
{
|
||||||
|
wmoDateTime = p->absoluteDateTime_.value();
|
||||||
|
}
|
||||||
|
else if (endTimeHint.has_value())
|
||||||
|
{
|
||||||
|
bool dateTimeValid = false;
|
||||||
|
unsigned int dayOfMonth = 0;
|
||||||
|
unsigned long hour = 0;
|
||||||
|
unsigned long minute = 0;
|
||||||
|
|
||||||
|
dateTimeValid = p->ParseDateTime(dayOfMonth, hour, minute);
|
||||||
|
|
||||||
|
if (dateTimeValid)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
auto endDays = floor<days>(endTimeHint.value());
|
||||||
|
year_month_day endDate {endDays};
|
||||||
|
|
||||||
|
// Combine end date year and month with WMO date time
|
||||||
|
wmoDateTime =
|
||||||
|
sys_days {endDate.year() / endDate.month() / day {dayOfMonth}} +
|
||||||
|
hours {hour} + minutes {minute};
|
||||||
|
|
||||||
|
// If the begin date is after the end date, assume the start time
|
||||||
|
// was the previous month (give a 1 day grace period for expiring
|
||||||
|
// events in the past)
|
||||||
|
if (wmoDateTime > endTimeHint.value() + 24h)
|
||||||
|
{
|
||||||
|
// If the current end month is January
|
||||||
|
if (endDate.month() == January)
|
||||||
|
{
|
||||||
|
year_month x = year {2024} / December;
|
||||||
|
sys_days y;
|
||||||
|
|
||||||
|
// The begin month must be December of last year
|
||||||
|
wmoDateTime =
|
||||||
|
sys_days {
|
||||||
|
year {static_cast<int>((endDate.year() - 1y).count())} /
|
||||||
|
December / day {dayOfMonth}} +
|
||||||
|
hours {hour} + minutes {minute};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Back up one month
|
||||||
|
wmoDateTime =
|
||||||
|
sys_days {endDate.year() /
|
||||||
|
month {static_cast<unsigned int>(
|
||||||
|
(endDate.month() - month {1}).count())} /
|
||||||
|
day {dayOfMonth}} +
|
||||||
|
hours {hour} + minutes {minute};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return wmoDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
bool WmoHeader::Parse(std::istream& is)
|
bool WmoHeader::Parse(std::istream& is)
|
||||||
{
|
{
|
||||||
bool headerValid = true;
|
bool headerValid = true;
|
||||||
|
|
@ -224,6 +298,8 @@ bool WmoHeader::Parse(std::istream& is)
|
||||||
p->icao_ = wmoTokenList[1];
|
p->icao_ = wmoTokenList[1];
|
||||||
p->dateTime_ = wmoTokenList[2];
|
p->dateTime_ = wmoTokenList[2];
|
||||||
|
|
||||||
|
p->CalculateAbsoluteDateTime();
|
||||||
|
|
||||||
if (wmoTokenList.size() == 4)
|
if (wmoTokenList.size() == 4)
|
||||||
{
|
{
|
||||||
p->bbbIndicator_ = wmoTokenList[3];
|
p->bbbIndicator_ = wmoTokenList[3];
|
||||||
|
|
@ -255,4 +331,60 @@ bool WmoHeader::Parse(std::istream& is)
|
||||||
return headerValid;
|
return headerValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WmoHeader::SetDateHint(std::chrono::year_month dateHint)
|
||||||
|
{
|
||||||
|
p->dateHint_ = dateHint;
|
||||||
|
p->CalculateAbsoluteDateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WmoHeaderImpl::ParseDateTime(unsigned int& dayOfMonth,
|
||||||
|
unsigned long& hour,
|
||||||
|
unsigned long& minute)
|
||||||
|
{
|
||||||
|
bool dateTimeValid = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// WMO date time is in the format DDHHMM
|
||||||
|
dayOfMonth =
|
||||||
|
static_cast<unsigned int>(std::stoul(dateTime_.substr(0, 2)));
|
||||||
|
hour = std::stoul(dateTime_.substr(2, 2));
|
||||||
|
minute = std::stoul(dateTime_.substr(4, 2));
|
||||||
|
dateTimeValid = true;
|
||||||
|
}
|
||||||
|
catch (const std::exception&)
|
||||||
|
{
|
||||||
|
logger_->warn("Malformed WMO date/time: {}", dateTime_);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dateTimeValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WmoHeaderImpl::CalculateAbsoluteDateTime()
|
||||||
|
{
|
||||||
|
bool dateTimeValid = false;
|
||||||
|
|
||||||
|
if (dateHint_.has_value() && !dateTime_.empty())
|
||||||
|
{
|
||||||
|
unsigned int dayOfMonth = 0;
|
||||||
|
unsigned long hour = 0;
|
||||||
|
unsigned long minute = 0;
|
||||||
|
|
||||||
|
dateTimeValid = ParseDateTime(dayOfMonth, hour, minute);
|
||||||
|
|
||||||
|
if (dateTimeValid)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
absoluteDateTime_ = sys_days {dateHint_->year() / dateHint_->month() /
|
||||||
|
day {dayOfMonth}} +
|
||||||
|
hours {hour} + minutes {minute};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dateTimeValid)
|
||||||
|
{
|
||||||
|
absoluteDateTime_.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace scwx::awips
|
} // namespace scwx::awips
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue