diff --git a/scwx-qt/source/scwx/qt/gl/draw/linked_vectors.cpp b/scwx-qt/source/scwx/qt/gl/draw/linked_vectors.cpp index 5fbdcf8c..9e4b664f 100644 --- a/scwx-qt/source/scwx/qt/gl/draw/linked_vectors.cpp +++ b/scwx-qt/source/scwx/qt/gl/draw/linked_vectors.cpp @@ -21,8 +21,9 @@ static const boost::gil::rgba32f_pixel_t kBlack {0.0f, 0.0f, 0.0f, 1.0f}; struct LinkedVectorDrawItem { LinkedVectorDrawItem( - const common::Coordinate& center, - const std::shared_ptr& vectorPacket) + const common::Coordinate& center, + const std::shared_ptr& + vectorPacket) { coordinates_.push_back(util::GeographicLib::GetCoordinate( center, vectorPacket->start_i_km(), vectorPacket->start_j_km())); @@ -136,8 +137,8 @@ void LinkedVectors::StartVectors() } std::shared_ptr LinkedVectors::AddVector( - const common::Coordinate& center, - const std::shared_ptr& vectorPacket) + const common::Coordinate& center, + const std::shared_ptr& vectorPacket) { return p->vectorList_.emplace_back( std::make_shared(center, vectorPacket)); diff --git a/scwx-qt/source/scwx/qt/gl/draw/linked_vectors.hpp b/scwx-qt/source/scwx/qt/gl/draw/linked_vectors.hpp index 564e3853..9e8eef6c 100644 --- a/scwx-qt/source/scwx/qt/gl/draw/linked_vectors.hpp +++ b/scwx-qt/source/scwx/qt/gl/draw/linked_vectors.hpp @@ -82,9 +82,10 @@ public: * * @return Linked vector draw item */ - std::shared_ptr AddVector( - const common::Coordinate& center, - const std::shared_ptr& vectorPacket); + std::shared_ptr + AddVector(const common::Coordinate& center, + const std::shared_ptr& + vectorPacket); /** * Sets the modulate color of a linked vector. diff --git a/scwx-qt/source/scwx/qt/map/overlay_product_layer.cpp b/scwx-qt/source/scwx/qt/map/overlay_product_layer.cpp index 42068b36..6cc6fbb6 100644 --- a/scwx-qt/source/scwx/qt/map/overlay_product_layer.cpp +++ b/scwx-qt/source/scwx/qt/map/overlay_product_layer.cpp @@ -2,12 +2,13 @@ #include #include #include -#include #include #include #include #include +#include #include +#include namespace scwx { @@ -33,20 +34,33 @@ public: void UpdateStormTrackingInformation(); static void HandleLinkedVectorPacket( - const std::shared_ptr& packet, - const common::Coordinate& center, - const std::string& hoverText, - boost::gil::rgba32f_pixel_t color, - bool tickRadiusIncrement, - std::shared_ptr& linkedVectors); + const std::shared_ptr& packet, + const common::Coordinate& center, + const std::string& hoverText, + boost::gil::rgba32f_pixel_t color, + units::length::nautical_miles tickRadius, + units::length::nautical_miles tickRadiusIncrement, + std::shared_ptr& linkedVectors); static void HandleScitDataPacket( - const std::shared_ptr& packet, - const common::Coordinate& center, - const std::string& stormId, - std::shared_ptr& linkedVectors); - static void - HandleStormIdPacket(const std::shared_ptr& packet, - std::string& stormId); + const std::shared_ptr& + sti, + const std::shared_ptr& packet, + const common::Coordinate& center, + const std::string& stormId, + const std::string& hoverText, + std::shared_ptr& linkedVectors); + + static void HandleStormIdPacket( + const std::shared_ptr& + sti, + const std::shared_ptr& 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_; @@ -122,21 +136,21 @@ void OverlayProductLayer::Impl::UpdateStormTrackingInformation() float latitude = 0.0f; float longitude = 0.0f; - std::shared_ptr l3File = nullptr; - std::shared_ptr gpm = nullptr; - std::shared_ptr psb = nullptr; + std::shared_ptr l3File = nullptr; + std::shared_ptr sti = nullptr; + std::shared_ptr psb = nullptr; if (record != nullptr) { l3File = record->level3_file(); } if (l3File != nullptr) { - gpm = std::dynamic_pointer_cast( - l3File->message()); + sti = std::dynamic_pointer_cast< + wsr88d::rpg::StormTrackingInformationMessage>(l3File->message()); } - if (gpm != nullptr) + if (sti != nullptr) { - psb = gpm->symbology_block(); + psb = sti->symbology_block(); } linkedVectors_->StartVectors(); @@ -155,6 +169,7 @@ void OverlayProductLayer::Impl::UpdateStormTrackingInformation() } std::string stormId = "?"; + std::string hoverText {}; for (std::size_t i = 0; i < psb->number_of_layers(); ++i) { @@ -164,15 +179,19 @@ void OverlayProductLayer::Impl::UpdateStormTrackingInformation() switch (packet->packet_code()) { case static_cast(wsr88d::rpg::PacketCode::StormId): - HandleStormIdPacket(packet, stormId); + HandleStormIdPacket(sti, packet, stormId, hoverText); break; case static_cast( wsr88d::rpg::PacketCode::ScitPastData): case static_cast( wsr88d::rpg::PacketCode::ScitForecastData): - HandleScitDataPacket( - packet, {latitude, longitude}, stormId, linkedVectors_); + HandleScitDataPacket(sti, + packet, + {latitude, longitude}, + stormId, + hoverText, + linkedVectors_); break; default: @@ -192,41 +211,63 @@ void OverlayProductLayer::Impl::UpdateStormTrackingInformation() } void OverlayProductLayer::Impl::HandleStormIdPacket( - const std::shared_ptr& packet, std::string& stormId) + const std::shared_ptr& + sti, + const std::shared_ptr& packet, + std::string& stormId, + std::string& hoverText) { auto stormIdPacket = - std::dynamic_pointer_cast(packet); + std::dynamic_pointer_cast(packet); if (stormIdPacket != nullptr && stormIdPacket->RecordCount() > 0) { - stormId = stormIdPacket->storm_id(0); + stormId = stormIdPacket->storm_id(0); + hoverText = BuildHoverText(sti, stormId); } else { logger_->warn("Invalid Storm ID Packet"); stormId = "?"; + hoverText.clear(); } } void OverlayProductLayer::Impl::HandleScitDataPacket( - const std::shared_ptr& packet, - const common::Coordinate& center, - const std::string& stormId, - std::shared_ptr& linkedVectors) + const std::shared_ptr& + sti, + const std::shared_ptr& packet, + const common::Coordinate& center, + const std::string& stormId, + const std::string& hoverText, + std::shared_ptr& linkedVectors) { auto scitDataPacket = - std::dynamic_pointer_cast(packet); + std::dynamic_pointer_cast(packet); if (scitDataPacket != nullptr) { boost::gil::rgba32f_pixel_t color {1.0f, 1.0f, 1.0f, 1.0f}; - bool tickRadiusIncrement = true; + + units::length::nautical_miles tickRadius {0.5f}; + units::length::nautical_miles tickRadiusIncrement {0.0f}; + + auto stiRecord = sti->sti_record(stormId); + if (scitDataPacket->packet_code() == static_cast(wsr88d::rpg::PacketCode::ScitPastData)) { - color = {0.5f, 0.5f, 0.5f, 1.0f}; - tickRadiusIncrement = false; + // If this is past data, the default tick radius and increment with a + // 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()) @@ -237,8 +278,9 @@ void OverlayProductLayer::Impl::HandleScitDataPacket( wsr88d::rpg::PacketCode::LinkedVectorNoValue): HandleLinkedVectorPacket(subpacket, center, - stormId, + hoverText, color, + tickRadius, tickRadiusIncrement, linkedVectors); break; @@ -257,15 +299,16 @@ void OverlayProductLayer::Impl::HandleScitDataPacket( } void OverlayProductLayer::Impl::HandleLinkedVectorPacket( - const std::shared_ptr& packet, - const common::Coordinate& center, - const std::string& hoverText, - boost::gil::rgba32f_pixel_t color, - bool tickRadiusIncrement, - std::shared_ptr& linkedVectors) + const std::shared_ptr& packet, + const common::Coordinate& center, + const std::string& hoverText, + boost::gil::rgba32f_pixel_t color, + units::length::nautical_miles tickRadius, + units::length::nautical_miles tickRadiusIncrement, + std::shared_ptr& linkedVectors) { auto linkedVectorPacket = - std::dynamic_pointer_cast(packet); + std::dynamic_pointer_cast(packet); if (linkedVectorPacket != nullptr) { @@ -274,19 +317,9 @@ void OverlayProductLayer::Impl::HandleLinkedVectorPacket( gl::draw::LinkedVectors::SetVectorModulate(di, color); gl::draw::LinkedVectors::SetVectorHoverText(di, hoverText); gl::draw::LinkedVectors::SetVectorTicksEnabled(di, true); - gl::draw::LinkedVectors::SetVectorTickRadius( - di, units::length::nautical_miles {1.0}); - - if (tickRadiusIncrement) - { - gl::draw::LinkedVectors::SetVectorTickRadiusIncrement( - di, units::length::nautical_miles {1.0}); - } - else - { - gl::draw::LinkedVectors::SetVectorTickRadiusIncrement( - di, units::length::nautical_miles {0.0}); - } + gl::draw::LinkedVectors::SetVectorTickRadius(di, tickRadius); + gl::draw::LinkedVectors::SetVectorTickRadiusIncrement( + di, tickRadiusIncrement); } 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( const QMapLibreGL::CustomLayerRenderParameters& params, const QPointF& mouseLocalPos,