diff --git a/scwx-qt/source/scwx/qt/map/alert_layer.cpp b/scwx-qt/source/scwx/qt/map/alert_layer.cpp index 6996333c..e281c055 100644 --- a/scwx-qt/source/scwx/qt/map/alert_layer.cpp +++ b/scwx-qt/source/scwx/qt/map/alert_layer.cpp @@ -1,6 +1,15 @@ #include +#include #include +#include +#include +#include +#include + +#include +#include + namespace scwx { namespace qt @@ -11,6 +20,81 @@ namespace map static const std::string logPrefix_ = "scwx::qt::map::alert_layer"; static const auto logger_ = scwx::util::Logger::Create(logPrefix_); +template +struct AlertTypeHash; + +template<> +struct AlertTypeHash> +{ + size_t operator()(const std::pair& x) const; +}; + +class AlertLayerHandler : QObject +{ + Q_OBJECT +public: + struct SegmentRecord + { + std::shared_ptr segment_; + types::TextEventKey key_; + std::shared_ptr message_; + std::chrono::system_clock::time_point segmentBegin_; + std::chrono::system_clock::time_point segmentEnd_; + + SegmentRecord( + const std::shared_ptr& segment, + const types::TextEventKey& key, + const std::shared_ptr& message) : + segment_ {segment}, + key_ {key}, + message_ {message}, + segmentBegin_ {segment->event_begin()}, + segmentEnd_ {segment->event_end()} + { + } + }; + + explicit AlertLayerHandler() + { + connect(textEventManager_.get(), + &manager::TextEventManager::AlertUpdated, + this, + [this](const types::TextEventKey& key, std::size_t messageIndex) + { HandleAlert(key, messageIndex); }); + } + ~AlertLayerHandler() + { + disconnect(textEventManager_.get(), nullptr, this, nullptr); + + std::unique_lock lock(alertMutex_); + } + + // NOTE: iterators are no longer stable if the stable vector moves + std::unordered_map< + std::pair, + boost::container::stable_vector>, + AlertTypeHash>> + segmentsByType_ {}; + + std::unordered_map< + types::TextEventKey, + boost::container::stable_vector>, + types::TextEventHash> + segmentsByKey_ {}; + + void HandleAlert(const types::TextEventKey& key, size_t messageIndex); + + static AlertLayerHandler& Instance(); + + std::shared_ptr textEventManager_ { + manager::TextEventManager::Instance()}; + + std::mutex alertMutex_ {}; + +signals: + void AlertsUpdated(awips::Phenomenon phenomenon, bool alertActive); +}; + class AlertLayer::Impl { public: @@ -71,6 +155,91 @@ bool AlertLayer::RunMousePicking( eventHandler); } +void AlertLayerHandler::HandleAlert(const types::TextEventKey& key, + size_t messageIndex) +{ + logger_->trace("HandleAlert: {}", key.ToString()); + + std::unordered_set, + AlertTypeHash>> + alertsUpdated {}; + + auto message = textEventManager_->message_list(key).at(messageIndex); + + // Determine start time for first segment + std::chrono::system_clock::time_point segmentBegin {}; + if (message->segment_count() > 0) + { + segmentBegin = message->segment(0)->event_begin(); + } + + // Take a unique mutex before modifying segments + std::unique_lock lock {alertMutex_}; + + // Update any existing segments with new end time + auto& segmentsForKey = segmentsByKey_[key]; + for (auto& segmentRecord : segmentsForKey) + { + if (segmentRecord->segmentEnd_ > segmentBegin) + { + segmentRecord->segmentEnd_ = segmentBegin; + } + } + + // Process new segments + for (auto& segment : message->segments()) + { + if (!segment->codedLocation_.has_value()) + { + // Cannot handle a segment without a location + continue; + } + + auto& vtec = segment->header_->vtecString_.front(); + auto action = vtec.pVtec_.action(); + awips::Phenomenon phenomenon = vtec.pVtec_.phenomenon(); + auto eventEnd = vtec.pVtec_.event_end(); + bool alertActive = (action != awips::PVtec::Action::Canceled); + + auto& segmentsForType = segmentsByType_[{key.phenomenon_, alertActive}]; + + // Insert segment into lists + std::shared_ptr segmentRecord = + std::make_shared(segment, key, message); + + segmentsForKey.push_back(segmentRecord); + segmentsForType.push_back(segmentRecord); + + alertsUpdated.emplace(phenomenon, alertActive); + } + + // Release the lock after completing segment updates + lock.unlock(); + + for (auto& alert : alertsUpdated) + { + // Emit signal for each updated alert type + Q_EMIT AlertsUpdated(alert.first, alert.second); + } +} + +AlertLayerHandler& AlertLayerHandler::Instance() +{ + static AlertLayerHandler alertLayerHandler_ {}; + return alertLayerHandler_; +} + +size_t AlertTypeHash>::operator()( + const std::pair& x) const +{ + size_t seed = 0; + boost::hash_combine(seed, x.first); + boost::hash_combine(seed, x.second); + return seed; +} + } // namespace map } // namespace qt } // namespace scwx + +#include "alert_layer.moc" diff --git a/scwx-qt/source/scwx/qt/map/alert_layer_old.cpp b/scwx-qt/source/scwx/qt/map/alert_layer_old.cpp index d1e73d78..ff01d4c5 100644 --- a/scwx-qt/source/scwx/qt/map/alert_layer_old.cpp +++ b/scwx-qt/source/scwx/qt/map/alert_layer_old.cpp @@ -48,18 +48,19 @@ static const std::vector kAlertPhenomena_ { awips::Phenomenon::Tornado}; template -struct AlertTypeHash; +struct AlertTypeOldHash; template<> -struct AlertTypeHash> +struct AlertTypeOldHash> { size_t operator()(const std::pair& x) const; }; class AlertLayerOldHandler : public QObject { - Q_OBJECT public : - explicit AlertLayerOldHandler() : + Q_OBJECT +public: + explicit AlertLayerOldHandler() : textEventManager_ {manager::TextEventManager::Instance()}, alertUpdateTimer_ {scwx::util::io_context()}, alertSourceMap_ {}, @@ -98,7 +99,7 @@ class AlertLayerOldHandler : public QObject boost::asio::steady_timer alertUpdateTimer_; std::unordered_map, QVariantMap, - AlertTypeHash>> + AlertTypeOldHash>> alertSourceMap_; std::unordered_multimapmessage_list(key).at(messageIndex); std::unordered_set, - AlertTypeHash>> + AlertTypeOldHash>> alertsUpdated {}; // Take a unique lock before modifying feature lists @@ -274,7 +275,7 @@ void AlertLayerOldHandler::UpdateAlerts() std::unique_lock lock(alertMutex_); std::unordered_set, - AlertTypeHash>> + AlertTypeOldHash>> alertsUpdated {}; // Evaluate each rendered feature for expiration @@ -481,7 +482,7 @@ static QString GetSuffix(awips::Phenomenon phenomenon, bool alertActive) .arg(alertActive); } -size_t AlertTypeHash>::operator()( +size_t AlertTypeOldHash>::operator()( const std::pair& x) const { size_t seed = 0;