Generate hover text for storm tracking information

This commit is contained in:
Dan Paulat 2024-02-23 23:53:59 -06:00
parent b4b1706587
commit 29e87fc11e
3 changed files with 158 additions and 63 deletions

View file

@ -21,8 +21,9 @@ static const boost::gil::rgba32f_pixel_t kBlack {0.0f, 0.0f, 0.0f, 1.0f};
struct LinkedVectorDrawItem struct LinkedVectorDrawItem
{ {
LinkedVectorDrawItem( LinkedVectorDrawItem(
const common::Coordinate& center, const common::Coordinate& center,
const std::shared_ptr<wsr88d::rpg::LinkedVectorPacket>& vectorPacket) const std::shared_ptr<const wsr88d::rpg::LinkedVectorPacket>&
vectorPacket)
{ {
coordinates_.push_back(util::GeographicLib::GetCoordinate( coordinates_.push_back(util::GeographicLib::GetCoordinate(
center, vectorPacket->start_i_km(), vectorPacket->start_j_km())); center, vectorPacket->start_i_km(), vectorPacket->start_j_km()));
@ -136,8 +137,8 @@ void LinkedVectors::StartVectors()
} }
std::shared_ptr<LinkedVectorDrawItem> LinkedVectors::AddVector( std::shared_ptr<LinkedVectorDrawItem> LinkedVectors::AddVector(
const common::Coordinate& center, const common::Coordinate& center,
const std::shared_ptr<wsr88d::rpg::LinkedVectorPacket>& vectorPacket) const std::shared_ptr<const wsr88d::rpg::LinkedVectorPacket>& vectorPacket)
{ {
return p->vectorList_.emplace_back( return p->vectorList_.emplace_back(
std::make_shared<LinkedVectorDrawItem>(center, vectorPacket)); std::make_shared<LinkedVectorDrawItem>(center, vectorPacket));

View file

@ -82,9 +82,10 @@ public:
* *
* @return Linked vector draw item * @return Linked vector draw item
*/ */
std::shared_ptr<LinkedVectorDrawItem> AddVector( std::shared_ptr<LinkedVectorDrawItem>
const common::Coordinate& center, AddVector(const common::Coordinate& center,
const std::shared_ptr<wsr88d::rpg::LinkedVectorPacket>& vectorPacket); const std::shared_ptr<const wsr88d::rpg::LinkedVectorPacket>&
vectorPacket);
/** /**
* Sets the modulate color of a linked vector. * Sets the modulate color of a linked vector.

View file

@ -2,12 +2,13 @@
#include <scwx/qt/gl/draw/linked_vectors.hpp> #include <scwx/qt/gl/draw/linked_vectors.hpp>
#include <scwx/qt/manager/radar_product_manager.hpp> #include <scwx/qt/manager/radar_product_manager.hpp>
#include <scwx/qt/view/overlay_product_view.hpp> #include <scwx/qt/view/overlay_product_view.hpp>
#include <scwx/wsr88d/rpg/graphic_product_message.hpp>
#include <scwx/wsr88d/rpg/linked_vector_packet.hpp> #include <scwx/wsr88d/rpg/linked_vector_packet.hpp>
#include <scwx/wsr88d/rpg/rpg_types.hpp> #include <scwx/wsr88d/rpg/rpg_types.hpp>
#include <scwx/wsr88d/rpg/scit_data_packet.hpp> #include <scwx/wsr88d/rpg/scit_data_packet.hpp>
#include <scwx/wsr88d/rpg/storm_id_symbol_packet.hpp> #include <scwx/wsr88d/rpg/storm_id_symbol_packet.hpp>
#include <scwx/wsr88d/rpg/storm_tracking_information_message.hpp>
#include <scwx/util/logger.hpp> #include <scwx/util/logger.hpp>
#include <scwx/util/time.hpp>
namespace scwx namespace scwx
{ {
@ -33,20 +34,33 @@ public:
void UpdateStormTrackingInformation(); void UpdateStormTrackingInformation();
static void HandleLinkedVectorPacket( static void HandleLinkedVectorPacket(
const std::shared_ptr<wsr88d::rpg::Packet>& packet, const std::shared_ptr<const wsr88d::rpg::Packet>& packet,
const common::Coordinate& center, const common::Coordinate& center,
const std::string& hoverText, const std::string& hoverText,
boost::gil::rgba32f_pixel_t color, boost::gil::rgba32f_pixel_t color,
bool tickRadiusIncrement, units::length::nautical_miles<float> tickRadius,
std::shared_ptr<gl::draw::LinkedVectors>& linkedVectors); units::length::nautical_miles<float> tickRadiusIncrement,
std::shared_ptr<gl::draw::LinkedVectors>& linkedVectors);
static void HandleScitDataPacket( static void HandleScitDataPacket(
const std::shared_ptr<wsr88d::rpg::Packet>& packet, const std::shared_ptr<const wsr88d::rpg::StormTrackingInformationMessage>&
const common::Coordinate& center, sti,
const std::string& stormId, const std::shared_ptr<const wsr88d::rpg::Packet>& packet,
std::shared_ptr<gl::draw::LinkedVectors>& linkedVectors); const common::Coordinate& center,
static void const std::string& stormId,
HandleStormIdPacket(const std::shared_ptr<wsr88d::rpg::Packet>& packet, const std::string& hoverText,
std::string& stormId); std::shared_ptr<gl::draw::LinkedVectors>& linkedVectors);
static void HandleStormIdPacket(
const std::shared_ptr<const wsr88d::rpg::StormTrackingInformationMessage>&
sti,
const std::shared_ptr<const wsr88d::rpg::Packet>& packet,
std::string& stormId,
std::string& hoverText);
static std::string BuildHoverText(
const std::shared_ptr<
const scwx::wsr88d::rpg::StormTrackingInformationMessage>& sti,
std::string& stormId);
OverlayProductLayer* self_; OverlayProductLayer* self_;
@ -122,21 +136,21 @@ void OverlayProductLayer::Impl::UpdateStormTrackingInformation()
float latitude = 0.0f; float latitude = 0.0f;
float longitude = 0.0f; float longitude = 0.0f;
std::shared_ptr<wsr88d::Level3File> l3File = nullptr; std::shared_ptr<wsr88d::Level3File> l3File = nullptr;
std::shared_ptr<wsr88d::rpg::GraphicProductMessage> gpm = nullptr; std::shared_ptr<wsr88d::rpg::StormTrackingInformationMessage> sti = nullptr;
std::shared_ptr<wsr88d::rpg::ProductSymbologyBlock> psb = nullptr; std::shared_ptr<wsr88d::rpg::ProductSymbologyBlock> psb = nullptr;
if (record != nullptr) if (record != nullptr)
{ {
l3File = record->level3_file(); l3File = record->level3_file();
} }
if (l3File != nullptr) if (l3File != nullptr)
{ {
gpm = std::dynamic_pointer_cast<wsr88d::rpg::GraphicProductMessage>( sti = std::dynamic_pointer_cast<
l3File->message()); wsr88d::rpg::StormTrackingInformationMessage>(l3File->message());
} }
if (gpm != nullptr) if (sti != nullptr)
{ {
psb = gpm->symbology_block(); psb = sti->symbology_block();
} }
linkedVectors_->StartVectors(); linkedVectors_->StartVectors();
@ -155,6 +169,7 @@ void OverlayProductLayer::Impl::UpdateStormTrackingInformation()
} }
std::string stormId = "?"; std::string stormId = "?";
std::string hoverText {};
for (std::size_t i = 0; i < psb->number_of_layers(); ++i) for (std::size_t i = 0; i < psb->number_of_layers(); ++i)
{ {
@ -164,15 +179,19 @@ void OverlayProductLayer::Impl::UpdateStormTrackingInformation()
switch (packet->packet_code()) switch (packet->packet_code())
{ {
case static_cast<std::uint16_t>(wsr88d::rpg::PacketCode::StormId): case static_cast<std::uint16_t>(wsr88d::rpg::PacketCode::StormId):
HandleStormIdPacket(packet, stormId); HandleStormIdPacket(sti, packet, stormId, hoverText);
break; break;
case static_cast<std::uint16_t>( case static_cast<std::uint16_t>(
wsr88d::rpg::PacketCode::ScitPastData): wsr88d::rpg::PacketCode::ScitPastData):
case static_cast<std::uint16_t>( case static_cast<std::uint16_t>(
wsr88d::rpg::PacketCode::ScitForecastData): wsr88d::rpg::PacketCode::ScitForecastData):
HandleScitDataPacket( HandleScitDataPacket(sti,
packet, {latitude, longitude}, stormId, linkedVectors_); packet,
{latitude, longitude},
stormId,
hoverText,
linkedVectors_);
break; break;
default: default:
@ -192,41 +211,63 @@ void OverlayProductLayer::Impl::UpdateStormTrackingInformation()
} }
void OverlayProductLayer::Impl::HandleStormIdPacket( void OverlayProductLayer::Impl::HandleStormIdPacket(
const std::shared_ptr<wsr88d::rpg::Packet>& packet, std::string& stormId) const std::shared_ptr<const wsr88d::rpg::StormTrackingInformationMessage>&
sti,
const std::shared_ptr<const wsr88d::rpg::Packet>& packet,
std::string& stormId,
std::string& hoverText)
{ {
auto stormIdPacket = auto stormIdPacket =
std::dynamic_pointer_cast<wsr88d::rpg::StormIdSymbolPacket>(packet); std::dynamic_pointer_cast<const wsr88d::rpg::StormIdSymbolPacket>(packet);
if (stormIdPacket != nullptr && stormIdPacket->RecordCount() > 0) if (stormIdPacket != nullptr && stormIdPacket->RecordCount() > 0)
{ {
stormId = stormIdPacket->storm_id(0); stormId = stormIdPacket->storm_id(0);
hoverText = BuildHoverText(sti, stormId);
} }
else else
{ {
logger_->warn("Invalid Storm ID Packet"); logger_->warn("Invalid Storm ID Packet");
stormId = "?"; stormId = "?";
hoverText.clear();
} }
} }
void OverlayProductLayer::Impl::HandleScitDataPacket( void OverlayProductLayer::Impl::HandleScitDataPacket(
const std::shared_ptr<wsr88d::rpg::Packet>& packet, const std::shared_ptr<const wsr88d::rpg::StormTrackingInformationMessage>&
const common::Coordinate& center, sti,
const std::string& stormId, const std::shared_ptr<const wsr88d::rpg::Packet>& packet,
std::shared_ptr<gl::draw::LinkedVectors>& linkedVectors) const common::Coordinate& center,
const std::string& stormId,
const std::string& hoverText,
std::shared_ptr<gl::draw::LinkedVectors>& linkedVectors)
{ {
auto scitDataPacket = auto scitDataPacket =
std::dynamic_pointer_cast<wsr88d::rpg::ScitDataPacket>(packet); std::dynamic_pointer_cast<const wsr88d::rpg::ScitDataPacket>(packet);
if (scitDataPacket != nullptr) if (scitDataPacket != nullptr)
{ {
boost::gil::rgba32f_pixel_t color {1.0f, 1.0f, 1.0f, 1.0f}; boost::gil::rgba32f_pixel_t color {1.0f, 1.0f, 1.0f, 1.0f};
bool tickRadiusIncrement = true;
units::length::nautical_miles<float> tickRadius {0.5f};
units::length::nautical_miles<float> tickRadiusIncrement {0.0f};
auto stiRecord = sti->sti_record(stormId);
if (scitDataPacket->packet_code() == if (scitDataPacket->packet_code() ==
static_cast<std::uint16_t>(wsr88d::rpg::PacketCode::ScitPastData)) static_cast<std::uint16_t>(wsr88d::rpg::PacketCode::ScitPastData))
{ {
color = {0.5f, 0.5f, 0.5f, 1.0f}; // If this is past data, the default tick radius and increment with a
tickRadiusIncrement = false; // darker color
color = {0.5f, 0.5f, 0.5f, 1.0f};
}
else if (stiRecord != nullptr && stiRecord->meanError_.has_value())
{
// If this is forecast data, use the mean error as the radius (minimum
// of the default value), incrementing by the mean error
tickRadiusIncrement = stiRecord->meanError_.value();
tickRadius = std::max(tickRadius, tickRadiusIncrement);
} }
for (auto& subpacket : scitDataPacket->packet_list()) for (auto& subpacket : scitDataPacket->packet_list())
@ -237,8 +278,9 @@ void OverlayProductLayer::Impl::HandleScitDataPacket(
wsr88d::rpg::PacketCode::LinkedVectorNoValue): wsr88d::rpg::PacketCode::LinkedVectorNoValue):
HandleLinkedVectorPacket(subpacket, HandleLinkedVectorPacket(subpacket,
center, center,
stormId, hoverText,
color, color,
tickRadius,
tickRadiusIncrement, tickRadiusIncrement,
linkedVectors); linkedVectors);
break; break;
@ -257,15 +299,16 @@ void OverlayProductLayer::Impl::HandleScitDataPacket(
} }
void OverlayProductLayer::Impl::HandleLinkedVectorPacket( void OverlayProductLayer::Impl::HandleLinkedVectorPacket(
const std::shared_ptr<wsr88d::rpg::Packet>& packet, const std::shared_ptr<const wsr88d::rpg::Packet>& packet,
const common::Coordinate& center, const common::Coordinate& center,
const std::string& hoverText, const std::string& hoverText,
boost::gil::rgba32f_pixel_t color, boost::gil::rgba32f_pixel_t color,
bool tickRadiusIncrement, units::length::nautical_miles<float> tickRadius,
std::shared_ptr<gl::draw::LinkedVectors>& linkedVectors) units::length::nautical_miles<float> tickRadiusIncrement,
std::shared_ptr<gl::draw::LinkedVectors>& linkedVectors)
{ {
auto linkedVectorPacket = auto linkedVectorPacket =
std::dynamic_pointer_cast<wsr88d::rpg::LinkedVectorPacket>(packet); std::dynamic_pointer_cast<const wsr88d::rpg::LinkedVectorPacket>(packet);
if (linkedVectorPacket != nullptr) if (linkedVectorPacket != nullptr)
{ {
@ -274,19 +317,9 @@ void OverlayProductLayer::Impl::HandleLinkedVectorPacket(
gl::draw::LinkedVectors::SetVectorModulate(di, color); gl::draw::LinkedVectors::SetVectorModulate(di, color);
gl::draw::LinkedVectors::SetVectorHoverText(di, hoverText); gl::draw::LinkedVectors::SetVectorHoverText(di, hoverText);
gl::draw::LinkedVectors::SetVectorTicksEnabled(di, true); gl::draw::LinkedVectors::SetVectorTicksEnabled(di, true);
gl::draw::LinkedVectors::SetVectorTickRadius( gl::draw::LinkedVectors::SetVectorTickRadius(di, tickRadius);
di, units::length::nautical_miles<double> {1.0}); gl::draw::LinkedVectors::SetVectorTickRadiusIncrement(
di, tickRadiusIncrement);
if (tickRadiusIncrement)
{
gl::draw::LinkedVectors::SetVectorTickRadiusIncrement(
di, units::length::nautical_miles<double> {1.0});
}
else
{
gl::draw::LinkedVectors::SetVectorTickRadiusIncrement(
di, units::length::nautical_miles<double> {0.0});
}
} }
else else
{ {
@ -294,6 +327,66 @@ void OverlayProductLayer::Impl::HandleLinkedVectorPacket(
} }
} }
std::string OverlayProductLayer::Impl::BuildHoverText(
const std::shared_ptr<
const scwx::wsr88d::rpg::StormTrackingInformationMessage>& sti,
std::string& stormId)
{
std::string hoverText = fmt::format("Storm ID: {}", stormId);
auto stiRecord = sti->sti_record(stormId);
if (stiRecord != nullptr)
{
if (stiRecord->direction_.has_value() && stiRecord->speed_.has_value())
{
hoverText +=
fmt::format("\nMovement: {} @ {}",
units::to_string(stiRecord->direction_.value()),
units::to_string(stiRecord->speed_.value()));
}
if (stiRecord->maxDbz_.has_value() &&
stiRecord->maxDbzHeight_.has_value())
{
hoverText +=
fmt::format("\nMax dBZ: {} ({} kft)",
stiRecord->maxDbz_.value(),
stiRecord->maxDbzHeight_.value().value() / 1000.0f);
}
if (stiRecord->forecastError_.has_value())
{
hoverText +=
fmt::format("\nForecast Error: {}",
units::to_string(stiRecord->forecastError_.value()));
}
if (stiRecord->meanError_.has_value())
{
hoverText +=
fmt::format("\nMean Error: {}",
units::to_string(stiRecord->meanError_.value()));
}
}
auto dateTime = sti->date_time();
if (dateTime.has_value())
{
hoverText +=
fmt::format("\nDate/Time: {}", util::TimeString(dateTime.value()));
}
auto forecastInterval = sti->forecast_interval();
if (forecastInterval.has_value())
{
hoverText += fmt::format("\nForecast Interval: {} min",
forecastInterval.value().count());
}
return hoverText;
}
bool OverlayProductLayer::RunMousePicking( bool OverlayProductLayer::RunMousePicking(
const QMapLibreGL::CustomLayerRenderParameters& params, const QMapLibreGL::CustomLayerRenderParameters& params,
const QPointF& mouseLocalPos, const QPointF& mouseLocalPos,