mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 05:50:06 +00:00
P-VTEC parsing
This commit is contained in:
parent
e5c40b9eb5
commit
be1d7323bd
10 changed files with 835 additions and 5 deletions
131
test/source/scwx/awips/pvtec.test.cpp
Normal file
131
test/source/scwx/awips/pvtec.test.cpp
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
#include <scwx/awips/pvtec.hpp>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
namespace scwx
|
||||||
|
{
|
||||||
|
namespace awips
|
||||||
|
{
|
||||||
|
|
||||||
|
static const std::string logPrefix_ = "[scwx::awips::pvtec.test] ";
|
||||||
|
|
||||||
|
std::pair<std::chrono::year_month_day,
|
||||||
|
std::chrono::hh_mm_ss<std::chrono::minutes>>
|
||||||
|
GetDateTime(std::chrono::system_clock::time_point t);
|
||||||
|
|
||||||
|
TEST(PVtec, FlashFloodWarningExtended)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
PVtec pvtec;
|
||||||
|
std::string s = "/O.EXT.KJAN.FF.W.0023.000000T0000Z-210606T1700Z/";
|
||||||
|
|
||||||
|
pvtec.Parse(s);
|
||||||
|
|
||||||
|
auto eventBegin = GetDateTime(pvtec.event_begin());
|
||||||
|
auto eventEnd = GetDateTime(pvtec.event_end());
|
||||||
|
|
||||||
|
EXPECT_EQ(pvtec.fixed_identifier(), PVtec::ProductType::Operational);
|
||||||
|
EXPECT_EQ(pvtec.action(), PVtec::Action::ExtendedInTime);
|
||||||
|
EXPECT_EQ(pvtec.office_id(), "KJAN");
|
||||||
|
EXPECT_EQ(pvtec.phenomenon(), Phenomenon::FlashFlood);
|
||||||
|
EXPECT_EQ(pvtec.significance(), Significance::Warning);
|
||||||
|
EXPECT_EQ(pvtec.event_tracking_number(), 23);
|
||||||
|
|
||||||
|
EXPECT_EQ(pvtec.event_begin(), std::chrono::system_clock::time_point {});
|
||||||
|
EXPECT_EQ(eventBegin.first.year(), 1970y);
|
||||||
|
EXPECT_EQ(eventBegin.first.month(), month {1});
|
||||||
|
EXPECT_EQ(eventBegin.first.day(), 1d);
|
||||||
|
EXPECT_EQ(eventBegin.second.hours(), 0h);
|
||||||
|
EXPECT_EQ(eventBegin.second.minutes(), 0min);
|
||||||
|
|
||||||
|
EXPECT_EQ(eventEnd.first.year(), 2021y);
|
||||||
|
EXPECT_EQ(eventEnd.first.month(), month {6});
|
||||||
|
EXPECT_EQ(eventEnd.first.day(), 6d);
|
||||||
|
EXPECT_EQ(eventEnd.second.hours(), 17h);
|
||||||
|
EXPECT_EQ(eventEnd.second.minutes(), 0min);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PVtec, TornadoWarningNew)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
PVtec pvtec;
|
||||||
|
std::string s = "/O.NEW.KLIX.TO.W.0032.210606T1501Z-210606T1600Z/";
|
||||||
|
|
||||||
|
pvtec.Parse(s);
|
||||||
|
|
||||||
|
auto eventBegin = GetDateTime(pvtec.event_begin());
|
||||||
|
auto eventEnd = GetDateTime(pvtec.event_end());
|
||||||
|
|
||||||
|
EXPECT_EQ(pvtec.fixed_identifier(), PVtec::ProductType::Operational);
|
||||||
|
EXPECT_EQ(pvtec.action(), PVtec::Action::New);
|
||||||
|
EXPECT_EQ(pvtec.office_id(), "KLIX");
|
||||||
|
EXPECT_EQ(pvtec.phenomenon(), Phenomenon::Tornado);
|
||||||
|
EXPECT_EQ(pvtec.significance(), Significance::Warning);
|
||||||
|
EXPECT_EQ(pvtec.event_tracking_number(), 32);
|
||||||
|
|
||||||
|
EXPECT_EQ(eventBegin.first.year(), 2021y);
|
||||||
|
EXPECT_EQ(eventBegin.first.month(), month {6});
|
||||||
|
EXPECT_EQ(eventBegin.first.day(), 6d);
|
||||||
|
EXPECT_EQ(eventBegin.second.hours(), 15h);
|
||||||
|
EXPECT_EQ(eventBegin.second.minutes(), 1min);
|
||||||
|
|
||||||
|
EXPECT_EQ(eventEnd.first.year(), 2021y);
|
||||||
|
EXPECT_EQ(eventEnd.first.month(), month {6});
|
||||||
|
EXPECT_EQ(eventEnd.first.day(), 6d);
|
||||||
|
EXPECT_EQ(eventEnd.second.hours(), 16h);
|
||||||
|
EXPECT_EQ(eventEnd.second.minutes(), 0min);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PVtec, TornadoWarningContinued)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
PVtec pvtec;
|
||||||
|
std::string s = "/O.CON.KLIX.TO.W.0032.000000T0000Z-210606T1600Z/";
|
||||||
|
|
||||||
|
pvtec.Parse(s);
|
||||||
|
|
||||||
|
auto eventBegin = GetDateTime(pvtec.event_begin());
|
||||||
|
auto eventEnd = GetDateTime(pvtec.event_end());
|
||||||
|
|
||||||
|
EXPECT_EQ(pvtec.fixed_identifier(), PVtec::ProductType::Operational);
|
||||||
|
EXPECT_EQ(pvtec.action(), PVtec::Action::Continued);
|
||||||
|
EXPECT_EQ(pvtec.office_id(), "KLIX");
|
||||||
|
EXPECT_EQ(pvtec.phenomenon(), Phenomenon::Tornado);
|
||||||
|
EXPECT_EQ(pvtec.significance(), Significance::Warning);
|
||||||
|
EXPECT_EQ(pvtec.event_tracking_number(), 32);
|
||||||
|
|
||||||
|
EXPECT_EQ(pvtec.event_begin(), std::chrono::system_clock::time_point {});
|
||||||
|
EXPECT_EQ(eventBegin.first.year(), 1970y);
|
||||||
|
EXPECT_EQ(eventBegin.first.month(), month {1});
|
||||||
|
EXPECT_EQ(eventBegin.first.day(), 1d);
|
||||||
|
EXPECT_EQ(eventBegin.second.hours(), 0h);
|
||||||
|
EXPECT_EQ(eventBegin.second.minutes(), 0min);
|
||||||
|
|
||||||
|
EXPECT_EQ(eventEnd.first.year(), 2021y);
|
||||||
|
EXPECT_EQ(eventEnd.first.month(), month {6});
|
||||||
|
EXPECT_EQ(eventEnd.first.day(), 6d);
|
||||||
|
EXPECT_EQ(eventEnd.second.hours(), 16h);
|
||||||
|
EXPECT_EQ(eventEnd.second.minutes(), 0min);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::chrono::year_month_day,
|
||||||
|
std::chrono::hh_mm_ss<std::chrono::minutes>>
|
||||||
|
GetDateTime(std::chrono::system_clock::time_point t)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
auto tDays = floor<days>(t);
|
||||||
|
auto tDate = year_month_day {tDays};
|
||||||
|
auto tMinutes = floor<minutes>(t - tDays);
|
||||||
|
auto tTime = hh_mm_ss {tMinutes};
|
||||||
|
|
||||||
|
return std::make_pair(tDate, tTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace awips
|
||||||
|
} // namespace scwx
|
||||||
|
|
@ -8,7 +8,8 @@ find_package(BZip2)
|
||||||
find_package(GTest)
|
find_package(GTest)
|
||||||
|
|
||||||
set(SRC_MAIN source/scwx/wxtest.cpp)
|
set(SRC_MAIN source/scwx/wxtest.cpp)
|
||||||
set(SRC_AWIPS_TESTS source/scwx/awips/text_product_file.test.cpp)
|
set(SRC_AWIPS_TESTS source/scwx/awips/pvtec.test.cpp
|
||||||
|
source/scwx/awips/text_product_file.test.cpp)
|
||||||
set(SRC_COMMON_TESTS source/scwx/common/color_table.test.cpp)
|
set(SRC_COMMON_TESTS source/scwx/common/color_table.test.cpp)
|
||||||
set(SRC_QT_MANAGER_TESTS source/scwx/qt/manager/settings_manager.test.cpp)
|
set(SRC_QT_MANAGER_TESTS source/scwx/qt/manager/settings_manager.test.cpp)
|
||||||
set(SRC_UTIL_TESTS source/scwx/util/rangebuf.test.cpp
|
set(SRC_UTIL_TESTS source/scwx/util/rangebuf.test.cpp
|
||||||
|
|
|
||||||
76
wxdata/include/scwx/awips/phenomenon.hpp
Normal file
76
wxdata/include/scwx/awips/phenomenon.hpp
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace scwx
|
||||||
|
{
|
||||||
|
namespace awips
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class Phenomenon
|
||||||
|
{
|
||||||
|
AshfallLand,
|
||||||
|
AirStagnation,
|
||||||
|
BeachHazard,
|
||||||
|
BriskWind,
|
||||||
|
Blizzard,
|
||||||
|
CoastalFlood,
|
||||||
|
DebrisFlow,
|
||||||
|
DustStorm,
|
||||||
|
BlowingDust,
|
||||||
|
ExtremeCold,
|
||||||
|
ExcessiveHeat,
|
||||||
|
ExtremeWind,
|
||||||
|
Flood,
|
||||||
|
FlashFlood,
|
||||||
|
DenseFogLand,
|
||||||
|
FloodForecastPoints,
|
||||||
|
Frost,
|
||||||
|
FireWeather,
|
||||||
|
Freeze,
|
||||||
|
Gale,
|
||||||
|
HurricaneForceWind,
|
||||||
|
Heat,
|
||||||
|
Hurricane,
|
||||||
|
HighWind,
|
||||||
|
Hydrologic,
|
||||||
|
HardFreeze,
|
||||||
|
IceStorm,
|
||||||
|
LakeEffectSnow,
|
||||||
|
LowWater,
|
||||||
|
LakeshoreFlood,
|
||||||
|
LakeWind,
|
||||||
|
Marine,
|
||||||
|
DenseFogMarine,
|
||||||
|
AshfallMarine,
|
||||||
|
DenseSmokeMarine,
|
||||||
|
RipCurrentRisk,
|
||||||
|
SmallCraft,
|
||||||
|
HazardousSeas,
|
||||||
|
DenseSmokeLand,
|
||||||
|
Storm,
|
||||||
|
StormSurge,
|
||||||
|
SnowSquall,
|
||||||
|
HighSurf,
|
||||||
|
SevereThunderstorm,
|
||||||
|
Tornado,
|
||||||
|
TropicalStorm,
|
||||||
|
Tsunami,
|
||||||
|
Typhoon,
|
||||||
|
HeavyFreezingSpray,
|
||||||
|
WindChill,
|
||||||
|
Wind,
|
||||||
|
WinterStorm,
|
||||||
|
WinterWeather,
|
||||||
|
FreezingFog,
|
||||||
|
FreezingRain,
|
||||||
|
FreezingSpray,
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
Phenomenon GetPhenomenon(const std::string& code);
|
||||||
|
std::string GetPhenomenonCode(Phenomenon phenomenon);
|
||||||
|
std::string GetPhenomenonText(Phenomenon phenomenon);
|
||||||
|
|
||||||
|
} // namespace awips
|
||||||
|
} // namespace scwx
|
||||||
75
wxdata/include/scwx/awips/pvtec.hpp
Normal file
75
wxdata/include/scwx/awips/pvtec.hpp
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <scwx/awips/phenomenon.hpp>
|
||||||
|
#include <scwx/awips/significance.hpp>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace scwx
|
||||||
|
{
|
||||||
|
namespace awips
|
||||||
|
{
|
||||||
|
|
||||||
|
class PVtecImpl;
|
||||||
|
|
||||||
|
class PVtec
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class ProductType
|
||||||
|
{
|
||||||
|
Operational,
|
||||||
|
Test,
|
||||||
|
Experimental,
|
||||||
|
OperationalWithExperimentalVtec,
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Action
|
||||||
|
{
|
||||||
|
New,
|
||||||
|
Continued,
|
||||||
|
ExtendedInArea,
|
||||||
|
ExtendedInTime,
|
||||||
|
ExtendedInAreaAndTime,
|
||||||
|
Upgraded,
|
||||||
|
Canceled,
|
||||||
|
Expired,
|
||||||
|
Routine,
|
||||||
|
Correction,
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit PVtec();
|
||||||
|
~PVtec();
|
||||||
|
|
||||||
|
PVtec(const PVtec&) = delete;
|
||||||
|
PVtec& operator=(const PVtec&) = delete;
|
||||||
|
|
||||||
|
PVtec(PVtec&&) noexcept;
|
||||||
|
PVtec& operator=(PVtec&&) noexcept;
|
||||||
|
|
||||||
|
ProductType fixed_identifier() const;
|
||||||
|
Action action() const;
|
||||||
|
std::string office_id() const;
|
||||||
|
Phenomenon phenomenon() const;
|
||||||
|
Significance significance() const;
|
||||||
|
int16_t event_tracking_number() const;
|
||||||
|
|
||||||
|
std::chrono::system_clock::time_point event_begin() const;
|
||||||
|
std::chrono::system_clock::time_point event_end() const;
|
||||||
|
|
||||||
|
bool Parse(const std::string& s);
|
||||||
|
|
||||||
|
static ProductType GetProductType(const std::string& code);
|
||||||
|
static std::string GetProductTypeCode(ProductType productType);
|
||||||
|
|
||||||
|
static Action GetAction(const std::string& code);
|
||||||
|
static std::string GetActionCode(Action action);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<PVtecImpl> p;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace awips
|
||||||
|
} // namespace scwx
|
||||||
27
wxdata/include/scwx/awips/significance.hpp
Normal file
27
wxdata/include/scwx/awips/significance.hpp
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace scwx
|
||||||
|
{
|
||||||
|
namespace awips
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class Significance
|
||||||
|
{
|
||||||
|
Warning,
|
||||||
|
Watch,
|
||||||
|
Advisory,
|
||||||
|
Statement,
|
||||||
|
Forecast,
|
||||||
|
Outlook,
|
||||||
|
Synopsis,
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
Significance GetSignificance(const std::string& code);
|
||||||
|
std::string GetSignificanceCode(Significance significance);
|
||||||
|
std::string GetSignificanceText(Significance significance);
|
||||||
|
|
||||||
|
} // namespace awips
|
||||||
|
} // namespace scwx
|
||||||
168
wxdata/source/scwx/awips/phenomenon.cpp
Normal file
168
wxdata/source/scwx/awips/phenomenon.cpp
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
#include <scwx/awips/phenomenon.hpp>
|
||||||
|
|
||||||
|
#include <boost/assign.hpp>
|
||||||
|
#include <boost/bimap.hpp>
|
||||||
|
#include <boost/bimap/unordered_set_of.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
namespace scwx
|
||||||
|
{
|
||||||
|
namespace awips
|
||||||
|
{
|
||||||
|
|
||||||
|
static const std::string logPrefix_ = "[scwx::awips::phenomenon] ";
|
||||||
|
|
||||||
|
typedef boost::bimap<boost::bimaps::unordered_set_of<Phenomenon>,
|
||||||
|
boost::bimaps::unordered_set_of<std::string>>
|
||||||
|
PhenomenonCodesBimap;
|
||||||
|
|
||||||
|
static const PhenomenonCodesBimap phenomenonCodes_ =
|
||||||
|
boost::assign::list_of<PhenomenonCodesBimap::relation> //
|
||||||
|
(Phenomenon::AshfallLand, "AF") //
|
||||||
|
(Phenomenon::AirStagnation, "AS") //
|
||||||
|
(Phenomenon::BeachHazard, "BH") //
|
||||||
|
(Phenomenon::BriskWind, "BW") //
|
||||||
|
(Phenomenon::Blizzard, "BZ") //
|
||||||
|
(Phenomenon::CoastalFlood, "CF") //
|
||||||
|
(Phenomenon::DebrisFlow, "DF") //
|
||||||
|
(Phenomenon::DustStorm, "DS") //
|
||||||
|
(Phenomenon::BlowingDust, "DU") //
|
||||||
|
(Phenomenon::ExtremeCold, "EC") //
|
||||||
|
(Phenomenon::ExcessiveHeat, "EH") //
|
||||||
|
(Phenomenon::ExtremeWind, "EW") //
|
||||||
|
(Phenomenon::Flood, "FA") //
|
||||||
|
(Phenomenon::FlashFlood, "FF") //
|
||||||
|
(Phenomenon::DenseFogLand, "FG") //
|
||||||
|
(Phenomenon::FloodForecastPoints, "FL") //
|
||||||
|
(Phenomenon::Frost, "FR") //
|
||||||
|
(Phenomenon::FireWeather, "FW") //
|
||||||
|
(Phenomenon::Freeze, "FZ") //
|
||||||
|
(Phenomenon::Gale, "GL") //
|
||||||
|
(Phenomenon::HurricaneForceWind, "HF") //
|
||||||
|
(Phenomenon::Heat, "HT") //
|
||||||
|
(Phenomenon::Hurricane, "HU") //
|
||||||
|
(Phenomenon::HighWind, "HW") //
|
||||||
|
(Phenomenon::Hydrologic, "HY") //
|
||||||
|
(Phenomenon::HardFreeze, "HZ") //
|
||||||
|
(Phenomenon::IceStorm, "IS") //
|
||||||
|
(Phenomenon::LakeEffectSnow, "LE") //
|
||||||
|
(Phenomenon::LowWater, "LO") //
|
||||||
|
(Phenomenon::LakeshoreFlood, "LS") //
|
||||||
|
(Phenomenon::LakeWind, "LW") //
|
||||||
|
(Phenomenon::Marine, "MA") //
|
||||||
|
(Phenomenon::DenseFogMarine, "MF") //
|
||||||
|
(Phenomenon::AshfallMarine, "MH") //
|
||||||
|
(Phenomenon::DenseSmokeMarine, "MS") //
|
||||||
|
(Phenomenon::RipCurrentRisk, "RP") //
|
||||||
|
(Phenomenon::SmallCraft, "SC") //
|
||||||
|
(Phenomenon::HazardousSeas, "SE") //
|
||||||
|
(Phenomenon::DenseSmokeLand, "SM") //
|
||||||
|
(Phenomenon::Storm, "SR") //
|
||||||
|
(Phenomenon::StormSurge, "SS") //
|
||||||
|
(Phenomenon::SnowSquall, "SQ") //
|
||||||
|
(Phenomenon::HighSurf, "SU") //
|
||||||
|
(Phenomenon::SevereThunderstorm, "SV") //
|
||||||
|
(Phenomenon::Tornado, "TO") //
|
||||||
|
(Phenomenon::TropicalStorm, "TR") //
|
||||||
|
(Phenomenon::Tsunami, "TS") //
|
||||||
|
(Phenomenon::Typhoon, "TY") //
|
||||||
|
(Phenomenon::HeavyFreezingSpray, "UP") //
|
||||||
|
(Phenomenon::WindChill, "WC") //
|
||||||
|
(Phenomenon::Wind, "WI") //
|
||||||
|
(Phenomenon::WinterStorm, "WS") //
|
||||||
|
(Phenomenon::WinterWeather, "WW") //
|
||||||
|
(Phenomenon::FreezingFog, "ZF") //
|
||||||
|
(Phenomenon::FreezingRain, "ZR") //
|
||||||
|
(Phenomenon::FreezingSpray, "ZY") //
|
||||||
|
(Phenomenon::Unknown, "??");
|
||||||
|
|
||||||
|
static const std::unordered_map<Phenomenon, std::string> phenomenonText_ {
|
||||||
|
{Phenomenon::AshfallLand, "Ashfall (land)"}, //
|
||||||
|
{Phenomenon::AirStagnation, "Air Stagnation"}, //
|
||||||
|
{Phenomenon::BeachHazard, "Beach Hazard"}, //
|
||||||
|
{Phenomenon::BriskWind, "Brisk Wind"}, //
|
||||||
|
{Phenomenon::Blizzard, "Blizzard"}, //
|
||||||
|
{Phenomenon::CoastalFlood, "Coastal Flood"}, //
|
||||||
|
{Phenomenon::DebrisFlow, "Debris Flow"}, //
|
||||||
|
{Phenomenon::DustStorm, "Dust Storm"}, //
|
||||||
|
{Phenomenon::BlowingDust, "Blowing Dust"}, //
|
||||||
|
{Phenomenon::ExtremeCold, "Extreme Cold"}, //
|
||||||
|
{Phenomenon::ExcessiveHeat, "Excessive Heat"}, //
|
||||||
|
{Phenomenon::ExtremeWind, "Extreme Wind"}, //
|
||||||
|
{Phenomenon::Flood, "Flood"}, //
|
||||||
|
{Phenomenon::FlashFlood, "Flash Flood"}, //
|
||||||
|
{Phenomenon::DenseFogLand, "Dense Fog (land)"}, //
|
||||||
|
{Phenomenon::Flood, "Flood (Forecast Points)"}, //
|
||||||
|
{Phenomenon::Frost, "Frost"}, //
|
||||||
|
{Phenomenon::FireWeather, "Fire Weather"}, //
|
||||||
|
{Phenomenon::Freeze, "Freeze"}, //
|
||||||
|
{Phenomenon::Gale, "Gale"}, //
|
||||||
|
{Phenomenon::HurricaneForceWind, "Hurricane Force Wind"}, //
|
||||||
|
{Phenomenon::Heat, "Heat"}, //
|
||||||
|
{Phenomenon::Hurricane, "Hurricane"}, //
|
||||||
|
{Phenomenon::HighWind, "High Wind"}, //
|
||||||
|
{Phenomenon::Hydrologic, "Hydrologic"}, //
|
||||||
|
{Phenomenon::HardFreeze, "Hard Freeze"}, //
|
||||||
|
{Phenomenon::IceStorm, "Ice Storm"}, //
|
||||||
|
{Phenomenon::LakeEffectSnow, "Lake Effect Snow"}, //
|
||||||
|
{Phenomenon::LowWater, "Low Water"}, //
|
||||||
|
{Phenomenon::LakeshoreFlood, "Lakeshore Flood"}, //
|
||||||
|
{Phenomenon::LakeWind, "Lake Wind"}, //
|
||||||
|
{Phenomenon::Marine, "Marine"}, //
|
||||||
|
{Phenomenon::DenseFogMarine, "Dense Fog (marine)"}, //
|
||||||
|
{Phenomenon::AshfallMarine, "Ashfall (marine)"}, //
|
||||||
|
{Phenomenon::DenseSmokeMarine, "Dense Smoke (marine)"}, //
|
||||||
|
{Phenomenon::RipCurrentRisk, "Rip Current Risk"}, //
|
||||||
|
{Phenomenon::SmallCraft, "Small Craft"}, //
|
||||||
|
{Phenomenon::HazardousSeas, "Hazardous Seas"}, //
|
||||||
|
{Phenomenon::DenseSmokeLand, "Dense Smoke (land)"}, //
|
||||||
|
{Phenomenon::Storm, "Storm"}, //
|
||||||
|
{Phenomenon::StormSurge, "Storm Surge"}, //
|
||||||
|
{Phenomenon::SnowSquall, "Snow Squall"}, //
|
||||||
|
{Phenomenon::HighSurf, "High Surf"}, //
|
||||||
|
{Phenomenon::SevereThunderstorm, "Severe Thunderstorm"}, //
|
||||||
|
{Phenomenon::Tornado, "Tornado"}, //
|
||||||
|
{Phenomenon::TropicalStorm, "Tropical Storm"}, //
|
||||||
|
{Phenomenon::Tsunami, "Tsunami"}, //
|
||||||
|
{Phenomenon::Typhoon, "Typhoon"}, //
|
||||||
|
{Phenomenon::HeavyFreezingSpray, "Heavy Freezing Spray"}, //
|
||||||
|
{Phenomenon::WindChill, "Wind Chill"}, //
|
||||||
|
{Phenomenon::Wind, "Wind"}, //
|
||||||
|
{Phenomenon::WinterStorm, "Winter Storm"}, //
|
||||||
|
{Phenomenon::WinterWeather, "Winter Weather"}, //
|
||||||
|
{Phenomenon::FreezingFog, "Freezing Fog"}, //
|
||||||
|
{Phenomenon::FreezingRain, "Freezing Rain"}, //
|
||||||
|
{Phenomenon::FreezingSpray, "Freezing Spray"}, //
|
||||||
|
{Phenomenon::Unknown, "Unknown"}};
|
||||||
|
|
||||||
|
Phenomenon GetPhenomenon(const std::string& code)
|
||||||
|
{
|
||||||
|
Phenomenon phenomenon;
|
||||||
|
|
||||||
|
if (phenomenonCodes_.right.find(code) != phenomenonCodes_.right.end())
|
||||||
|
{
|
||||||
|
phenomenon = phenomenonCodes_.right.at(code);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
phenomenon = Phenomenon::Unknown;
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(debug)
|
||||||
|
<< logPrefix_ << "Unrecognized code: \"" << code << "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
return phenomenon;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetPhenomenonCode(Phenomenon phenomenon)
|
||||||
|
{
|
||||||
|
return phenomenonCodes_.left.at(phenomenon);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetPhenomenonText(Phenomenon phenomenon)
|
||||||
|
{
|
||||||
|
return phenomenonText_.at(phenomenon);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace awips
|
||||||
|
} // namespace scwx
|
||||||
275
wxdata/source/scwx/awips/pvtec.cpp
Normal file
275
wxdata/source/scwx/awips/pvtec.cpp
Normal file
|
|
@ -0,0 +1,275 @@
|
||||||
|
// Enable chrono formatters
|
||||||
|
#ifndef __cpp_lib_format
|
||||||
|
# define __cpp_lib_format 202110L
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <scwx/awips/pvtec.hpp>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
#include <boost/assign.hpp>
|
||||||
|
#include <boost/bimap.hpp>
|
||||||
|
#include <boost/bimap/unordered_set_of.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
namespace scwx
|
||||||
|
{
|
||||||
|
namespace awips
|
||||||
|
{
|
||||||
|
|
||||||
|
static const std::string logPrefix_ = "[scwx::awips::pvtec] ";
|
||||||
|
|
||||||
|
typedef boost::bimap<boost::bimaps::unordered_set_of<PVtec::ProductType>,
|
||||||
|
boost::bimaps::unordered_set_of<std::string>>
|
||||||
|
ProductTypeCodesBimap;
|
||||||
|
|
||||||
|
static const ProductTypeCodesBimap productTypeCodes_ =
|
||||||
|
boost::assign::list_of<ProductTypeCodesBimap::relation> //
|
||||||
|
(PVtec::ProductType::Operational, "O") //
|
||||||
|
(PVtec::ProductType::Test, "T") //
|
||||||
|
(PVtec::ProductType::Experimental, "E") //
|
||||||
|
(PVtec::ProductType::OperationalWithExperimentalVtec, "X") //
|
||||||
|
(PVtec::ProductType::Unknown, "??");
|
||||||
|
|
||||||
|
typedef boost::bimap<boost::bimaps::unordered_set_of<PVtec::Action>,
|
||||||
|
boost::bimaps::unordered_set_of<std::string>>
|
||||||
|
ActionCodesBimap;
|
||||||
|
|
||||||
|
static const ActionCodesBimap actionCodes_ =
|
||||||
|
boost::assign::list_of<ActionCodesBimap::relation> //
|
||||||
|
(PVtec::Action::New, "NEW") //
|
||||||
|
(PVtec::Action::Continued, "CON") //
|
||||||
|
(PVtec::Action::ExtendedInArea, "EXA") //
|
||||||
|
(PVtec::Action::ExtendedInTime, "EXT") //
|
||||||
|
(PVtec::Action::ExtendedInAreaAndTime, "EXB") //
|
||||||
|
(PVtec::Action::Upgraded, "UPG") //
|
||||||
|
(PVtec::Action::Canceled, "CAN") //
|
||||||
|
(PVtec::Action::Expired, "EXP") //
|
||||||
|
(PVtec::Action::Routine, "ROU") //
|
||||||
|
(PVtec::Action::Correction, "COR") //
|
||||||
|
(PVtec::Action::Unknown, "???");
|
||||||
|
|
||||||
|
class PVtecImpl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PVtecImpl() :
|
||||||
|
pVtecString_ {},
|
||||||
|
fixedIdentifier_ {PVtec::ProductType::Unknown},
|
||||||
|
action_ {PVtec::Action::Unknown},
|
||||||
|
officeId_ {"????"},
|
||||||
|
phenomenon_ {Phenomenon::Unknown},
|
||||||
|
significance_ {Significance::Unknown},
|
||||||
|
eventTrackingNumber_ {-1},
|
||||||
|
eventBegin_ {},
|
||||||
|
eventEnd_ {},
|
||||||
|
valid_ {false}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~PVtecImpl() {}
|
||||||
|
|
||||||
|
std::string pVtecString_;
|
||||||
|
|
||||||
|
PVtec::ProductType fixedIdentifier_;
|
||||||
|
PVtec::Action action_;
|
||||||
|
std::string officeId_;
|
||||||
|
Phenomenon phenomenon_;
|
||||||
|
Significance significance_;
|
||||||
|
int16_t eventTrackingNumber_;
|
||||||
|
|
||||||
|
std::chrono::system_clock::time_point eventBegin_;
|
||||||
|
std::chrono::system_clock::time_point eventEnd_;
|
||||||
|
|
||||||
|
bool valid_;
|
||||||
|
};
|
||||||
|
|
||||||
|
PVtec::PVtec() : p(std::make_unique<PVtecImpl>()) {}
|
||||||
|
PVtec::~PVtec() = default;
|
||||||
|
|
||||||
|
PVtec::PVtec(PVtec&&) noexcept = default;
|
||||||
|
PVtec& PVtec::operator=(PVtec&&) noexcept = default;
|
||||||
|
|
||||||
|
PVtec::ProductType PVtec::fixed_identifier() const
|
||||||
|
{
|
||||||
|
return p->fixedIdentifier_;
|
||||||
|
}
|
||||||
|
|
||||||
|
PVtec::Action PVtec::action() const
|
||||||
|
{
|
||||||
|
return p->action_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PVtec::office_id() const
|
||||||
|
{
|
||||||
|
return p->officeId_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Phenomenon PVtec::phenomenon() const
|
||||||
|
{
|
||||||
|
return p->phenomenon_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Significance PVtec::significance() const
|
||||||
|
{
|
||||||
|
return p->significance_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t PVtec::event_tracking_number() const
|
||||||
|
{
|
||||||
|
return p->eventTrackingNumber_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::system_clock::time_point PVtec::event_begin() const
|
||||||
|
{
|
||||||
|
return p->eventBegin_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::system_clock::time_point PVtec::event_end() const
|
||||||
|
{
|
||||||
|
return p->eventEnd_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PVtec::Parse(const std::string& s)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
// P-VTEC takes the form:
|
||||||
|
// /k.aaa.cccc.pp.s.####.yymmddThhnnZ-yymmddThhnnZ/
|
||||||
|
// 012345678901234567890123456789012345678901234567
|
||||||
|
static constexpr size_t pVtecLength_ = 48u;
|
||||||
|
static constexpr size_t pVtecOffsetStart_ = 0u;
|
||||||
|
static constexpr size_t pVtecOffsetIdentifier_ = 1u;
|
||||||
|
static constexpr size_t pVtecOffsetAction_ = 3u;
|
||||||
|
static constexpr size_t pVtecOffsetOfficeId_ = 7u;
|
||||||
|
static constexpr size_t pVtecOffsetPhenomenon_ = 12u;
|
||||||
|
static constexpr size_t pVtecOffsetSignificance_ = 15u;
|
||||||
|
static constexpr size_t pVtecOffsetEventNumber_ = 17u;
|
||||||
|
static constexpr size_t pVtecOffsetEventBegin_ = 22u;
|
||||||
|
static constexpr size_t pVtecOffsetEventEnd_ = 35u;
|
||||||
|
static constexpr size_t pVtecOffsetEnd_ = 47u;
|
||||||
|
|
||||||
|
bool dataValid = (s.length() >= pVtecLength_ && //
|
||||||
|
s.at(pVtecOffsetStart_) == '/' && //
|
||||||
|
s.at(pVtecOffsetEnd_) == '/');
|
||||||
|
|
||||||
|
if (dataValid)
|
||||||
|
{
|
||||||
|
p->pVtecString_ = s.substr(0, pVtecLength_);
|
||||||
|
|
||||||
|
p->fixedIdentifier_ = GetProductType(s.substr(pVtecOffsetIdentifier_, 1));
|
||||||
|
p->action_ = GetAction(s.substr(pVtecOffsetAction_, 3));
|
||||||
|
p->officeId_ = s.substr(pVtecOffsetOfficeId_, 4);
|
||||||
|
p->phenomenon_ = GetPhenomenon(s.substr(pVtecOffsetPhenomenon_, 2));
|
||||||
|
p->significance_ = GetSignificance(s.substr(pVtecOffsetSignificance_, 1));
|
||||||
|
|
||||||
|
std::string eventNumberString = s.substr(pVtecOffsetEventNumber_, 4);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
p->eventTrackingNumber_ =
|
||||||
|
static_cast<int16_t>(std::stoi(eventNumberString));
|
||||||
|
}
|
||||||
|
catch (const std::exception& ex)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(warning)
|
||||||
|
<< logPrefix_ << "Error parsing event tracking number: \""
|
||||||
|
<< eventNumberString << "\" (" << ex.what() << ")";
|
||||||
|
|
||||||
|
p->eventTrackingNumber_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::string dateTimeFormat {"%y%m%dT%H%MZ"};
|
||||||
|
|
||||||
|
std::string sEventBegin = s.substr(pVtecOffsetEventBegin_, 12);
|
||||||
|
std::string sEventEnd = s.substr(pVtecOffsetEventEnd_, 12);
|
||||||
|
|
||||||
|
std::istringstream ssEventBegin {sEventBegin};
|
||||||
|
std::istringstream ssEventEnd {sEventEnd};
|
||||||
|
|
||||||
|
sys_time<minutes> eventBegin;
|
||||||
|
sys_time<minutes> eventEnd;
|
||||||
|
|
||||||
|
ssEventBegin >> parse(dateTimeFormat, eventBegin);
|
||||||
|
ssEventEnd >> parse(dateTimeFormat, eventEnd);
|
||||||
|
|
||||||
|
if (!ssEventBegin.fail())
|
||||||
|
{
|
||||||
|
p->eventBegin_ = eventBegin;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Time parsing expected to fail if time is "000000T0000Z"
|
||||||
|
p->eventBegin_ = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ssEventEnd.fail())
|
||||||
|
{
|
||||||
|
p->eventEnd_ = eventEnd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Time parsing expected to fail if time is "000000T0000Z"
|
||||||
|
p->eventEnd_ = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(warning)
|
||||||
|
<< logPrefix_ << "Invalid P-VTEC: \"" << s << "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
p->valid_ = dataValid;
|
||||||
|
|
||||||
|
return dataValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
PVtec::ProductType PVtec::GetProductType(const std::string& code)
|
||||||
|
{
|
||||||
|
ProductType productType;
|
||||||
|
|
||||||
|
if (productTypeCodes_.right.find(code) != productTypeCodes_.right.end())
|
||||||
|
{
|
||||||
|
productType = productTypeCodes_.right.at(code);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
productType = ProductType::Unknown;
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(debug)
|
||||||
|
<< logPrefix_ << "Unrecognized product code: \"" << code << "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
return productType;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PVtec::GetProductTypeCode(PVtec::ProductType productType)
|
||||||
|
{
|
||||||
|
return productTypeCodes_.left.at(productType);
|
||||||
|
}
|
||||||
|
|
||||||
|
PVtec::Action PVtec::GetAction(const std::string& code)
|
||||||
|
{
|
||||||
|
Action action;
|
||||||
|
|
||||||
|
if (actionCodes_.right.find(code) != actionCodes_.right.end())
|
||||||
|
{
|
||||||
|
action = actionCodes_.right.at(code);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
action = Action::Unknown;
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(debug)
|
||||||
|
<< logPrefix_ << "Unrecognized action code: \"" << code << "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PVtec::GetActionCode(PVtec::Action action)
|
||||||
|
{
|
||||||
|
return actionCodes_.left.at(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace awips
|
||||||
|
} // namespace scwx
|
||||||
70
wxdata/source/scwx/awips/significance.cpp
Normal file
70
wxdata/source/scwx/awips/significance.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
#include <scwx/awips/significance.hpp>
|
||||||
|
|
||||||
|
#include <boost/assign.hpp>
|
||||||
|
#include <boost/bimap.hpp>
|
||||||
|
#include <boost/bimap/unordered_set_of.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
namespace scwx
|
||||||
|
{
|
||||||
|
namespace awips
|
||||||
|
{
|
||||||
|
|
||||||
|
static const std::string logPrefix_ = "[scwx::awips::significance] ";
|
||||||
|
|
||||||
|
typedef boost::bimap<boost::bimaps::unordered_set_of<Significance>,
|
||||||
|
boost::bimaps::unordered_set_of<std::string>>
|
||||||
|
SignificanceCodesBimap;
|
||||||
|
|
||||||
|
static const SignificanceCodesBimap significanceCodes_ =
|
||||||
|
boost::assign::list_of<SignificanceCodesBimap::relation> //
|
||||||
|
(Significance::Warning, "W") //
|
||||||
|
(Significance::Watch, "A") //
|
||||||
|
(Significance::Advisory, "Y") //
|
||||||
|
(Significance::Statement, "S") //
|
||||||
|
(Significance::Forecast, "F") //
|
||||||
|
(Significance::Outlook, "O") //
|
||||||
|
(Significance::Synopsis, "N") //
|
||||||
|
(Significance::Unknown, "?");
|
||||||
|
|
||||||
|
static const std::unordered_map<Significance, std::string> significanceText_ {
|
||||||
|
{Significance::Warning, "Warning"}, //
|
||||||
|
{Significance::Watch, "Watch"}, //
|
||||||
|
{Significance::Advisory, "Advisory"}, //
|
||||||
|
{Significance::Statement, "Statement"}, //
|
||||||
|
{Significance::Forecast, "Forecast"}, //
|
||||||
|
{Significance::Outlook, "Outlook"}, //
|
||||||
|
{Significance::Synopsis, "Synopsis"}, //
|
||||||
|
{Significance::Unknown, "Unknown"}};
|
||||||
|
|
||||||
|
Significance GetSignificance(const std::string& code)
|
||||||
|
{
|
||||||
|
Significance significance;
|
||||||
|
|
||||||
|
if (significanceCodes_.right.find(code) != significanceCodes_.right.end())
|
||||||
|
{
|
||||||
|
significance = significanceCodes_.right.at(code);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
significance = Significance::Unknown;
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(debug)
|
||||||
|
<< logPrefix_ << "Unrecognized code: \"" << code << "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
return significance;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetSignificanceCode(Significance significance)
|
||||||
|
{
|
||||||
|
return significanceCodes_.left.at(significance);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetSignificanceText(Significance significance)
|
||||||
|
{
|
||||||
|
return significanceText_.at(significance);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace awips
|
||||||
|
} // namespace scwx
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include <scwx/awips/text_product_message.hpp>
|
#include <scwx/awips/text_product_message.hpp>
|
||||||
|
#include <scwx/awips/pvtec.hpp>
|
||||||
#include <scwx/awips/wmo_header.hpp>
|
#include <scwx/awips/wmo_header.hpp>
|
||||||
#include <scwx/common/characters.hpp>
|
#include <scwx/common/characters.hpp>
|
||||||
#include <scwx/util/streams.hpp>
|
#include <scwx/util/streams.hpp>
|
||||||
|
|
@ -26,7 +27,7 @@ static const std::regex reDateTimeString {"^[0-9]{3,4} ([AP]M|UTC)"};
|
||||||
|
|
||||||
struct Vtec
|
struct Vtec
|
||||||
{
|
{
|
||||||
std::string pVtec_;
|
PVtec pVtec_;
|
||||||
std::string hVtec_;
|
std::string hVtec_;
|
||||||
|
|
||||||
Vtec() : pVtec_ {}, hVtec_ {} {}
|
Vtec() : pVtec_ {}, hVtec_ {} {}
|
||||||
|
|
@ -330,7 +331,7 @@ std::optional<Vtec> TryParseVtecString(std::istream& is)
|
||||||
if (std::regex_search(line, rePVtecString))
|
if (std::regex_search(line, rePVtecString))
|
||||||
{
|
{
|
||||||
vtec = Vtec();
|
vtec = Vtec();
|
||||||
vtec->pVtec_.swap(line);
|
vtec->pVtec_.Parse(line);
|
||||||
|
|
||||||
isBegin = is.tellg();
|
isBegin = is.tellg();
|
||||||
|
|
||||||
|
|
@ -342,8 +343,8 @@ std::optional<Vtec> TryParseVtecString(std::istream& is)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// H-VTEC was not found, so reset the istream to the beginning of the
|
// H-VTEC was not found, so reset the istream to the beginning of
|
||||||
// line
|
// the line
|
||||||
is.seekg(isBegin, std::ios_base::beg);
|
is.seekg(isBegin, std::ios_base::beg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,16 @@ project(scwx-data)
|
||||||
find_package(Boost)
|
find_package(Boost)
|
||||||
|
|
||||||
set(HDR_AWIPS include/scwx/awips/message.hpp
|
set(HDR_AWIPS include/scwx/awips/message.hpp
|
||||||
|
include/scwx/awips/phenomenon.hpp
|
||||||
|
include/scwx/awips/pvtec.hpp
|
||||||
|
include/scwx/awips/significance.hpp
|
||||||
include/scwx/awips/text_product_file.hpp
|
include/scwx/awips/text_product_file.hpp
|
||||||
include/scwx/awips/text_product_message.hpp
|
include/scwx/awips/text_product_message.hpp
|
||||||
include/scwx/awips/wmo_header.hpp)
|
include/scwx/awips/wmo_header.hpp)
|
||||||
set(SRC_AWIPS source/scwx/awips/message.cpp
|
set(SRC_AWIPS source/scwx/awips/message.cpp
|
||||||
|
source/scwx/awips/phenomenon.cpp
|
||||||
|
source/scwx/awips/pvtec.cpp
|
||||||
|
source/scwx/awips/significance.cpp
|
||||||
source/scwx/awips/text_product_file.cpp
|
source/scwx/awips/text_product_file.cpp
|
||||||
source/scwx/awips/text_product_message.cpp
|
source/scwx/awips/text_product_message.cpp
|
||||||
source/scwx/awips/wmo_header.cpp)
|
source/scwx/awips/wmo_header.cpp)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue