mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 13:30:06 +00:00
Finish parsing storm tracking tabular block
This commit is contained in:
parent
6eb9caf819
commit
c03947d604
3 changed files with 184 additions and 44 deletions
|
|
@ -30,10 +30,14 @@ std::vector<std::string> ParseTokens(const std::string& s,
|
|||
|
||||
std::string ToString(const std::vector<std::string>& v);
|
||||
|
||||
std::optional<float> TryParseFloat(const std::string& str);
|
||||
|
||||
template<typename T>
|
||||
std::optional<T> TryParseUnsignedLong(const std::string& str);
|
||||
std::optional<T> TryParseNumeric(const std::string& str);
|
||||
|
||||
#if defined(STRINGS_IMPLEMENTATION)
|
||||
template std::optional<std::uint16_t> TryParseNumeric(const std::string& str);
|
||||
template std::optional<std::uint32_t> TryParseNumeric(const std::string& str);
|
||||
template std::optional<float> TryParseNumeric(const std::string& str);
|
||||
#endif
|
||||
|
||||
} // namespace util
|
||||
} // namespace scwx
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
#define STRINGS_IMPLEMENTATION
|
||||
|
||||
#include <scwx/util/strings.hpp>
|
||||
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
namespace scwx
|
||||
{
|
||||
|
|
@ -88,29 +91,15 @@ std::string ToString(const std::vector<std::string>& v)
|
|||
return value;
|
||||
}
|
||||
|
||||
std::optional<float> TryParseFloat(const std::string& str)
|
||||
{
|
||||
std::optional<float> value = std::nullopt;
|
||||
|
||||
try
|
||||
{
|
||||
value = static_cast<float>(std::stof(str));
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::optional<T> TryParseUnsignedLong(const std::string& str)
|
||||
std::optional<T> TryParseNumeric(const std::string& str)
|
||||
{
|
||||
std::optional<T> value = std::nullopt;
|
||||
|
||||
try
|
||||
{
|
||||
value = static_cast<T>(std::stoul(str));
|
||||
auto trimmed = boost::algorithm::trim_copy(str);
|
||||
value = boost::lexical_cast<T>(trimmed);
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
|
|
@ -119,8 +108,5 @@ std::optional<T> TryParseUnsignedLong(const std::string& str)
|
|||
return value;
|
||||
}
|
||||
|
||||
template std::optional<std::uint16_t>
|
||||
TryParseUnsignedLong<std::uint16_t>(const std::string& str);
|
||||
|
||||
} // namespace util
|
||||
} // namespace scwx
|
||||
|
|
|
|||
|
|
@ -35,6 +35,24 @@ public:
|
|||
std::optional<std::chrono::sys_time<std::chrono::seconds>> dateTime_ {};
|
||||
std::optional<std::uint16_t> numStormCells_ {};
|
||||
|
||||
// STORM CELL TRACKING/FORECAST ADAPTATION DATA
|
||||
std::optional<units::angle::degrees<std::uint16_t>> defaultDirection_ {};
|
||||
std::optional<units::velocity::meters_per_second<float>> minimumSpeed_ {};
|
||||
std::optional<units::velocity::knots<float>> defaultSpeed_ {};
|
||||
std::optional<units::length::kilometers<std::uint16_t>> allowableError_ {};
|
||||
std::optional<std::chrono::minutes> maximumTime_ {};
|
||||
std::optional<std::chrono::minutes> forecastInterval_ {};
|
||||
std::optional<std::uint16_t> numberOfPastVolumes_ {};
|
||||
std::optional<std::uint16_t> numberOfIntervals_ {};
|
||||
std::optional<units::velocity::meters_per_second<float>>
|
||||
correlationSpeed_ {};
|
||||
std::optional<std::chrono::minutes> errorInterval_ {};
|
||||
|
||||
// SCIT REFLECTIVITY MEDIAN FILTER
|
||||
std::optional<units::length::kilometers<float>> filterKernelSize_ {};
|
||||
std::optional<float> filterFraction_ {};
|
||||
std::optional<bool> reflectivityFiltered_ {};
|
||||
|
||||
std::unordered_map<std::string, StiRecord> stiRecords_ {};
|
||||
};
|
||||
|
||||
|
|
@ -122,8 +140,8 @@ void StormTrackingInformationMessage::Impl::ParseStormPositionForecastPage(
|
|||
{
|
||||
if (radarId_ == std::nullopt)
|
||||
{
|
||||
radarId_ =
|
||||
util::TryParseUnsignedLong<std::uint16_t>(line.substr(14, 3));
|
||||
// Radar ID (I3)
|
||||
radarId_ = util::TryParseNumeric<std::uint16_t>(line.substr(14, 3));
|
||||
}
|
||||
if (dateTime_ == std::nullopt)
|
||||
{
|
||||
|
|
@ -134,8 +152,9 @@ void StormTrackingInformationMessage::Impl::ParseStormPositionForecastPage(
|
|||
}
|
||||
if (numStormCells_ == std::nullopt)
|
||||
{
|
||||
// Number of Storm Cells (I3)
|
||||
numStormCells_ =
|
||||
util::TryParseUnsignedLong<std::uint16_t>(line.substr(71, 3));
|
||||
util::TryParseNumeric<std::uint16_t>(line.substr(71, 3));
|
||||
}
|
||||
}
|
||||
// clang-format off
|
||||
|
|
@ -151,9 +170,9 @@ void StormTrackingInformationMessage::Impl::ParseStormPositionForecastPage(
|
|||
|
||||
if (record.currentPosition_.azimuth_ == std::nullopt)
|
||||
{
|
||||
// Current Position: Azimuth (Degrees)
|
||||
// Current Position: Azimuth (Degrees) (I3)
|
||||
auto azimuth =
|
||||
util::TryParseUnsignedLong<std::uint16_t>(line.substr(9, 3));
|
||||
util::TryParseNumeric<std::uint16_t>(line.substr(9, 3));
|
||||
if (azimuth.has_value())
|
||||
{
|
||||
record.currentPosition_.azimuth_ =
|
||||
|
|
@ -162,9 +181,9 @@ void StormTrackingInformationMessage::Impl::ParseStormPositionForecastPage(
|
|||
}
|
||||
if (record.currentPosition_.range_ == std::nullopt)
|
||||
{
|
||||
// Current Position: Range (Nautical Miles)
|
||||
// Current Position: Range (Nautical Miles) (I3)
|
||||
auto range =
|
||||
util::TryParseUnsignedLong<std::uint16_t>(line.substr(13, 3));
|
||||
util::TryParseNumeric<std::uint16_t>(line.substr(13, 3));
|
||||
if (range.has_value())
|
||||
{
|
||||
record.currentPosition_.range_ =
|
||||
|
|
@ -174,9 +193,9 @@ void StormTrackingInformationMessage::Impl::ParseStormPositionForecastPage(
|
|||
}
|
||||
if (record.direction_ == std::nullopt)
|
||||
{
|
||||
// Movement: Direction (Degrees)
|
||||
// Movement: Direction (Degrees) (I3)
|
||||
auto direction =
|
||||
util::TryParseUnsignedLong<std::uint16_t>(line.substr(19, 3));
|
||||
util::TryParseNumeric<std::uint16_t>(line.substr(19, 3));
|
||||
if (direction.has_value())
|
||||
{
|
||||
record.direction_ =
|
||||
|
|
@ -185,9 +204,9 @@ void StormTrackingInformationMessage::Impl::ParseStormPositionForecastPage(
|
|||
}
|
||||
if (record.speed_ == std::nullopt)
|
||||
{
|
||||
// Movement: Speed (Knots)
|
||||
// Movement: Speed (Knots) (I3)
|
||||
auto speed =
|
||||
util::TryParseUnsignedLong<std::uint16_t>(line.substr(23, 3));
|
||||
util::TryParseNumeric<std::uint16_t>(line.substr(23, 3));
|
||||
if (speed.has_value())
|
||||
{
|
||||
record.speed_ =
|
||||
|
|
@ -200,10 +219,10 @@ void StormTrackingInformationMessage::Impl::ParseStormPositionForecastPage(
|
|||
|
||||
if (record.forecastPosition_[j].azimuth_ == std::nullopt)
|
||||
{
|
||||
// Forecast Position: Azimuth (Degrees)
|
||||
// Forecast Position: Azimuth (Degrees) (I3)
|
||||
std::size_t offset = 31 + positionOffset;
|
||||
|
||||
auto azimuth = util::TryParseUnsignedLong<std::uint16_t>(
|
||||
auto azimuth = util::TryParseNumeric<std::uint16_t>(
|
||||
line.substr(offset, 3));
|
||||
if (azimuth.has_value())
|
||||
{
|
||||
|
|
@ -213,10 +232,10 @@ void StormTrackingInformationMessage::Impl::ParseStormPositionForecastPage(
|
|||
}
|
||||
if (record.forecastPosition_[j].range_ == std::nullopt)
|
||||
{
|
||||
// Forecast Position: Range (Nautical Miles)
|
||||
// Forecast Position: Range (Nautical Miles) (I3)
|
||||
std::size_t offset = 35 + positionOffset;
|
||||
|
||||
auto range = util::TryParseUnsignedLong<std::uint16_t>(
|
||||
auto range = util::TryParseNumeric<std::uint16_t>(
|
||||
line.substr(offset, 3));
|
||||
if (range.has_value())
|
||||
{
|
||||
|
|
@ -228,8 +247,9 @@ void StormTrackingInformationMessage::Impl::ParseStormPositionForecastPage(
|
|||
}
|
||||
if (record.forecastError_ == std::nullopt)
|
||||
{
|
||||
// Forecast Error (Nautical Miles)
|
||||
auto forecastError = util::TryParseFloat(line.substr(71, 4));
|
||||
// Forecast Error (Nautical Miles) (F4.1)
|
||||
auto forecastError =
|
||||
util::TryParseNumeric<float>(line.substr(71, 4));
|
||||
if (forecastError.has_value())
|
||||
{
|
||||
record.forecastError_ = units::length::nautical_miles<float> {
|
||||
|
|
@ -238,8 +258,9 @@ void StormTrackingInformationMessage::Impl::ParseStormPositionForecastPage(
|
|||
}
|
||||
if (record.meanError_ == std::nullopt)
|
||||
{
|
||||
// Mean Error (Nautical Miles)
|
||||
auto meanError = util::TryParseFloat(line.substr(76, 4));
|
||||
// Mean Error (Nautical Miles) (F4.1)
|
||||
auto meanError =
|
||||
util::TryParseNumeric<float>(line.substr(76, 4));
|
||||
if (meanError.has_value())
|
||||
{
|
||||
record.meanError_ =
|
||||
|
|
@ -254,8 +275,137 @@ void StormTrackingInformationMessage::Impl::ParseStormPositionForecastPage(
|
|||
void StormTrackingInformationMessage::Impl::ParseStormCellTrackingDataPage(
|
||||
const std::vector<std::string>& page)
|
||||
{
|
||||
// TODO
|
||||
(void) (page);
|
||||
for (std::size_t i = 1; i < page.size(); ++i)
|
||||
{
|
||||
const std::string& line = page[i];
|
||||
|
||||
// clang-format off
|
||||
// " 260 (DEG) DEFAULT (DIRECTION) 2.5 (M/S) THRESH (MINIMUM SPEED)"
|
||||
// clang-format on
|
||||
if (i == 2 && line.size() >= 75)
|
||||
{
|
||||
// Default Direction (Degrees) (I3)
|
||||
auto direction =
|
||||
util::TryParseNumeric<std::uint16_t>(line.substr(4, 3));
|
||||
if (direction.has_value())
|
||||
{
|
||||
defaultDirection_ =
|
||||
units::angle::degrees<std::uint16_t> {direction.value()};
|
||||
}
|
||||
|
||||
// Minimum Speed (Threshold) (m/s) (F4.1)
|
||||
auto threshold = util::TryParseNumeric<float>(line.substr(40, 4));
|
||||
if (threshold.has_value())
|
||||
{
|
||||
minimumSpeed_ =
|
||||
units::velocity::meters_per_second<float> {threshold.value()};
|
||||
}
|
||||
}
|
||||
// clang-format off
|
||||
// " 36.0 (KTS) DEFAULT (SPEED) 20 (KM) ALLOWABLE ERROR"
|
||||
// clang-format on
|
||||
if (i == 3 && line.size() >= 68)
|
||||
{
|
||||
// Default Speed (Knots) (F4.1)
|
||||
auto speed = util::TryParseNumeric<float>(line.substr(3, 4));
|
||||
if (speed.has_value())
|
||||
{
|
||||
defaultSpeed_ = units::velocity::knots<float> {speed.value()};
|
||||
}
|
||||
|
||||
// Allowable Error (Kilometers) (I2)
|
||||
auto error = util::TryParseNumeric<std::uint16_t>(line.substr(42, 2));
|
||||
if (error.has_value())
|
||||
{
|
||||
allowableError_ =
|
||||
units::length::kilometers<std::uint16_t> {error.value()};
|
||||
}
|
||||
}
|
||||
// clang-format off
|
||||
// " 20 (MIN) TIME (MAXIMUM) 15 (MIN) FORECAST INTERVAL"
|
||||
// clang-format on
|
||||
if (i == 4 && line.size() >= 70)
|
||||
{
|
||||
// Maximum Time (Minutes) (I5)
|
||||
auto time = util::TryParseNumeric<std::uint32_t>(line.substr(2, 5));
|
||||
if (time.has_value())
|
||||
{
|
||||
maximumTime_ = std::chrono::minutes {time.value()};
|
||||
}
|
||||
|
||||
// Forecast Interval (Minutes) (I2)
|
||||
auto interval =
|
||||
util::TryParseNumeric<std::uint16_t>(line.substr(42, 2));
|
||||
if (interval.has_value())
|
||||
{
|
||||
forecastInterval_ = std::chrono::minutes {interval.value()};
|
||||
}
|
||||
}
|
||||
// clang-format off
|
||||
// " 10 NUMBER OF PAST VOLUMES 4 NUMBER OF INTERVALS"
|
||||
// clang-format on
|
||||
if (i == 5 && line.size() >= 72)
|
||||
{
|
||||
// Number of Past Volumes (I2)
|
||||
numberOfPastVolumes_ =
|
||||
util::TryParseNumeric<std::uint16_t>(line.substr(5, 2));
|
||||
|
||||
// Number of Intervals (I2)
|
||||
numberOfIntervals_ =
|
||||
util::TryParseNumeric<std::uint16_t>(line.substr(42, 2));
|
||||
}
|
||||
// clang-format off
|
||||
// " 30.0 (M/S) CORRELATION SPEED 15 (MIN) ERROR INTERVAL"
|
||||
// clang-format on
|
||||
if (i == 6 && line.size() >= 67)
|
||||
{
|
||||
// Correlation Speed (m/s) (F4.1)
|
||||
auto speed = util::TryParseNumeric<float>(line.substr(3, 4));
|
||||
if (speed.has_value())
|
||||
{
|
||||
correlationSpeed_ =
|
||||
units::velocity::meters_per_second<float> {speed.value()};
|
||||
}
|
||||
|
||||
// Error Interval (Minutes) (I2)
|
||||
auto interval =
|
||||
util::TryParseNumeric<std::uint16_t>(line.substr(42, 2));
|
||||
if (interval.has_value())
|
||||
{
|
||||
errorInterval_ = std::chrono::minutes {interval.value()};
|
||||
}
|
||||
}
|
||||
// clang-format off
|
||||
// " 7.0 (KM) FILTER KERNEL SIZE 0.5 THRESH (FILTER FRACTION)"
|
||||
// clang-format on
|
||||
if (i == 11 && line.size() >= 77)
|
||||
{
|
||||
// Filter Kernel Size (Kilometers) (F4.1)
|
||||
auto kernelSize = util::TryParseNumeric<float>(line.substr(3, 4));
|
||||
if (kernelSize.has_value())
|
||||
{
|
||||
filterKernelSize_ =
|
||||
units::length::kilometers<float> {kernelSize.value()};
|
||||
}
|
||||
|
||||
// Minimum Speed (Threshold) (m/s) (F4.1)
|
||||
filterFraction_ = util::TryParseNumeric<float>(line.substr(40, 4));
|
||||
}
|
||||
// clang-format off
|
||||
// " Yes REFLECTIVITY FILTERED"
|
||||
// clang-format on
|
||||
if (i == 12 && line.size() >= 37)
|
||||
{
|
||||
if (line.substr(4, 3) == "Yes")
|
||||
{
|
||||
reflectivityFiltered_ = true;
|
||||
}
|
||||
else if (line.substr(5, 2) == "No")
|
||||
{
|
||||
reflectivityFiltered_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<StormTrackingInformationMessage>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue