Finish parsing storm tracking tabular block

This commit is contained in:
Dan Paulat 2024-02-22 22:32:38 -06:00
parent 6eb9caf819
commit c03947d604
3 changed files with 184 additions and 44 deletions

View file

@ -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

View file

@ -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>