From 80baa8350d798cdd7ab0ab09e946d7c8bd8ca264 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Wed, 19 Oct 2022 15:08:32 -0500 Subject: [PATCH] Draw bounding boxes around alerts --- scwx-qt/scwx-qt.cmake | 6 +- scwx-qt/source/scwx/qt/map/alert_layer.cpp | 227 +++++++++++++++++++++ scwx-qt/source/scwx/qt/map/alert_layer.hpp | 32 +++ scwx-qt/source/scwx/qt/map/map_widget.cpp | 4 + 4 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 scwx-qt/source/scwx/qt/map/alert_layer.cpp create mode 100644 scwx-qt/source/scwx/qt/map/alert_layer.hpp diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index 4373e6fb..21cb8ce2 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -64,7 +64,8 @@ set(SRC_MANAGER source/scwx/qt/manager/radar_product_manager.cpp source/scwx/qt/manager/resource_manager.cpp source/scwx/qt/manager/settings_manager.cpp source/scwx/qt/manager/text_event_manager.cpp) -set(HDR_MAP source/scwx/qt/map/color_table_layer.hpp +set(HDR_MAP source/scwx/qt/map/alert_layer.hpp + source/scwx/qt/map/color_table_layer.hpp source/scwx/qt/map/draw_layer.hpp source/scwx/qt/map/generic_layer.hpp source/scwx/qt/map/layer_wrapper.hpp @@ -74,7 +75,8 @@ set(HDR_MAP source/scwx/qt/map/color_table_layer.hpp source/scwx/qt/map/overlay_layer.hpp source/scwx/qt/map/radar_product_layer.hpp source/scwx/qt/map/radar_range_layer.hpp) -set(SRC_MAP source/scwx/qt/map/color_table_layer.cpp +set(SRC_MAP source/scwx/qt/map/alert_layer.cpp + source/scwx/qt/map/color_table_layer.cpp source/scwx/qt/map/draw_layer.cpp source/scwx/qt/map/generic_layer.cpp source/scwx/qt/map/layer_wrapper.cpp diff --git a/scwx-qt/source/scwx/qt/map/alert_layer.cpp b/scwx-qt/source/scwx/qt/map/alert_layer.cpp new file mode 100644 index 00000000..97663612 --- /dev/null +++ b/scwx-qt/source/scwx/qt/map/alert_layer.cpp @@ -0,0 +1,227 @@ +#include +#include +#include + +namespace scwx +{ +namespace qt +{ +namespace map +{ + +static const std::string logPrefix_ = "scwx::qt::map::alert_layer"; +static const auto logger_ = scwx::util::Logger::Create(logPrefix_); + +static QMapbox::Coordinate +GetMapboxCoordinate(const common::Coordinate& coordinate); +static QMapbox::Coordinates +GetMapboxCoordinates(const awips::CodedLocation& codedLocation); + +class AlertLayerHandler : public QObject +{ + Q_OBJECT +public: + explicit AlertLayerHandler() : + alertSource_ {{"type", "geojson"}, + {"data", QVariant::fromValue(QList {})}} + { + connect(&manager::TextEventManager::Instance(), + &manager::TextEventManager::AlertUpdated, + this, + &AlertLayerHandler::HandleAlert); + } + ~AlertLayerHandler() = default; + + static AlertLayerHandler& Instance(); + + QList* FeatureList(); + void HandleAlert(const types::TextEventKey& key, size_t messageIndex); + + QVariantMap alertSource_; + +signals: + void AlertsUpdated(); +}; + +class AlertLayerImpl : public QObject +{ + Q_OBJECT +public: + explicit AlertLayerImpl(std::shared_ptr context) : + context_ {context} + { + connect(&AlertLayerHandler::Instance(), + &AlertLayerHandler::AlertsUpdated, + this, + &AlertLayerImpl::UpdateSource); + } + ~AlertLayerImpl() = default; + + void UpdateSource(); + + std::shared_ptr context_; +}; + +AlertLayer::AlertLayer(std::shared_ptr context) : + DrawLayer(context), p(std::make_unique(context)) +{ +} + +AlertLayer::~AlertLayer() = default; + +void AlertLayer::Initialize() +{ + logger_->debug("Initialize()"); + + DrawLayer::Initialize(); +} + +void AlertLayer::Render(const QMapbox::CustomLayerRenderParameters& params) +{ + gl::OpenGLFunctions& gl = context()->gl(); + + DrawLayer::Render(params); + + SCWX_GL_CHECK_ERROR(); +} + +void AlertLayer::Deinitialize() +{ + logger_->debug("Deinitialize()"); + + DrawLayer::Deinitialize(); +} + +void AlertLayer::AddLayers(const std::string& before) +{ + logger_->debug("AddLayers()"); + + auto map = p->context_->map().lock(); + if (map == nullptr) + { + return; + } + + if (map->layerExists("alertPolygonLayerBg")) + { + map->removeLayer("alertPolygonLayerBg"); + } + if (map->layerExists("alertPolygonLayerFg")) + { + map->removeLayer("alertPolygonLayerFg"); + } + if (map->sourceExists("alertPolygon")) + { + map->removeSource("alertPolygon"); + } + + map->addSource("alertPolygon", AlertLayerHandler::Instance().alertSource_); + + map->addLayer({{"id", "alertPolygonLayerBg"}, + {"type", "line"}, + {"source", "alertPolygon"}}, + QString::fromStdString(before)); + map->setLayoutProperty("alertPolygonLayerBg", "line-join", "round"); + map->setLayoutProperty("alertPolygonLayerBg", "line-cap", "round"); + map->setPaintProperty( + "alertPolygonLayerBg", "line-color", "rgba(0, 0, 0, 255)"); + map->setPaintProperty("alertPolygonLayerBg", "line-width", "5"); + + map->addLayer({{"id", "alertPolygonLayerFg"}, + {"type", "line"}, + {"source", "alertPolygon"}}, + QString::fromStdString(before)); + map->setLayoutProperty("alertPolygonLayerFg", "line-join", "round"); + map->setLayoutProperty("alertPolygonLayerFg", "line-cap", "round"); + map->setPaintProperty( + "alertPolygonLayerFg", "line-color", "rgba(255, 0, 0, 255)"); + map->setPaintProperty("alertPolygonLayerFg", "line-width", "3"); +} + +QList* AlertLayerHandler::FeatureList() +{ + return reinterpret_cast*>( + alertSource_["data"].data()); +} + +void AlertLayerHandler::HandleAlert(const types::TextEventKey& key, + size_t messageIndex) +{ + auto message = + manager::TextEventManager::Instance().message_list(key).at(messageIndex); + bool alertUpdated = false; + + // TODO: Remove previous items + + for (auto segment : message->segments()) + { + if (!segment->codedLocation_.has_value()) + { + continue; + } + + // Add alert location to polygon list + auto mapboxCoordinates = + GetMapboxCoordinates(segment->codedLocation_.value()); + + FeatureList()->push_back( + {QMapbox::Feature::PolygonType, + std::initializer_list { + std::initializer_list { + {mapboxCoordinates}}}}); + + alertUpdated = true; + } + + if (alertUpdated) + { + emit AlertsUpdated(); + } +} + +void AlertLayerImpl::UpdateSource() +{ + auto map = context_->map().lock(); + if (map == nullptr) + { + return; + } + + map->updateSource("alertPolygon", + AlertLayerHandler::Instance().alertSource_); +} + +AlertLayerHandler& AlertLayerHandler::Instance() +{ + static AlertLayerHandler alertLayerHandler {}; + return alertLayerHandler; +} + +static QMapbox::Coordinate +GetMapboxCoordinate(const common::Coordinate& coordinate) +{ + return {coordinate.latitude_, coordinate.longitude_}; +} + +static QMapbox::Coordinates +GetMapboxCoordinates(const awips::CodedLocation& codedLocation) +{ + auto scwxCoordinates = codedLocation.coordinates(); + QMapbox::Coordinates mapboxCoordinates(scwxCoordinates.size() + 1u); + + std::transform(scwxCoordinates.cbegin(), + scwxCoordinates.cend(), + mapboxCoordinates.begin(), + [](auto& coordinate) -> QMapbox::Coordinate + { return GetMapboxCoordinate(coordinate); }); + + mapboxCoordinates.back() = GetMapboxCoordinate(scwxCoordinates.front()); + + return mapboxCoordinates; +} + +} // namespace map +} // namespace qt +} // namespace scwx + +#include "alert_layer.moc" diff --git a/scwx-qt/source/scwx/qt/map/alert_layer.hpp b/scwx-qt/source/scwx/qt/map/alert_layer.hpp new file mode 100644 index 00000000..57c74eb3 --- /dev/null +++ b/scwx-qt/source/scwx/qt/map/alert_layer.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace scwx +{ +namespace qt +{ +namespace map +{ + +class AlertLayerImpl; + +class AlertLayer : public DrawLayer +{ +public: + explicit AlertLayer(std::shared_ptr context); + ~AlertLayer(); + + void Initialize() override final; + void Render(const QMapbox::CustomLayerRenderParameters&) override final; + void Deinitialize() override final; + + void AddLayers(const std::string& before = {}); + +private: + std::unique_ptr p; +}; + +} // namespace map +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/map/map_widget.cpp b/scwx-qt/source/scwx/qt/map/map_widget.cpp index a543f2f4..c1c6ceac 100644 --- a/scwx-qt/source/scwx/qt/map/map_widget.cpp +++ b/scwx-qt/source/scwx/qt/map/map_widget.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,7 @@ public: layerList_ {}, radarProductManager_ {nullptr}, radarProductLayer_ {nullptr}, + alertLayer_ {std::make_shared(context_)}, overlayLayer_ {nullptr}, colorTableLayer_ {nullptr}, autoRefreshEnabled_ {true}, @@ -104,6 +106,7 @@ public: std::shared_ptr colorTable_; std::shared_ptr radarProductLayer_; + std::shared_ptr alertLayer_; std::shared_ptr overlayLayer_; std::shared_ptr colorTableLayer_; @@ -522,6 +525,7 @@ void MapWidget::AddLayers() p->AddLayer("colorTable", p->colorTableLayer_); } + p->alertLayer_->AddLayers("colorTable"); p->overlayLayer_ = std::make_shared(p->context_); p->AddLayer("overlay", p->overlayLayer_); }