Merge pull request #240 from dpaulat/feature/enhanced-alerts

Show severity in Alert Dock
This commit is contained in:
Dan Paulat 2024-06-30 22:09:32 -05:00 committed by GitHub
commit 86bd48a12f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 219 additions and 19 deletions

View file

@ -34,6 +34,10 @@ public:
explicit AlertModelImpl();
~AlertModelImpl() = default;
bool GetObserved(const types::TextEventKey& key);
awips::ThreatCategory GetThreatCategory(const types::TextEventKey& key);
bool GetTornadoPossible(const types::TextEventKey& key);
static std::string GetCounties(const types::TextEventKey& key);
static std::string GetState(const types::TextEventKey& key);
static std::chrono::system_clock::time_point
@ -49,6 +53,18 @@ public:
const GeographicLib::Geodesic& geodesic_;
std::unordered_map<types::TextEventKey,
bool,
types::TextEventHash<types::TextEventKey>>
observedMap_;
std::unordered_map<types::TextEventKey,
awips::ThreatCategory,
types::TextEventHash<types::TextEventKey>>
threatCategoryMap_;
std::unordered_map<types::TextEventKey,
bool,
types::TextEventHash<types::TextEventKey>>
tornadoPossibleMap_;
std::unordered_map<types::TextEventKey,
common::Coordinate,
types::TextEventHash<types::TextEventKey>>
@ -125,6 +141,29 @@ QVariant AlertModel::data(const QModelIndex& index, int role) const
return QString::fromStdString(
awips::GetSignificanceText(textEventKey.significance_));
case static_cast<int>(Column::Tornado):
if (textEventKey.phenomenon_ == awips::Phenomenon::Tornado &&
p->GetObserved(textEventKey))
{
return tr("Observed");
}
if (p->GetTornadoPossible(textEventKey))
{
return tr("Possible");
}
break;
case static_cast<int>(Column::ThreatCategory):
if (role == Qt::DisplayRole)
{
return QString::fromStdString(awips::GetThreatCategoryName(
p->GetThreatCategory(textEventKey)));
}
else
{
return static_cast<int>(p->GetThreatCategory(textEventKey));
}
case static_cast<int>(Column::State):
return QString::fromStdString(AlertModelImpl::GetState(textEventKey));
@ -204,6 +243,12 @@ AlertModel::headerData(int section, Qt::Orientation orientation, int role) const
case static_cast<int>(Column::Significance):
return tr("Significance");
case static_cast<int>(Column::ThreatCategory):
return tr("Category");
case static_cast<int>(Column::Tornado):
return tr("Tornado");
case static_cast<int>(Column::State):
return tr("State");
@ -240,6 +285,14 @@ AlertModel::headerData(int section, Qt::Orientation orientation, int role) const
contentsSize = fontMetrics.size(0, QString(10, 'W'));
break;
case static_cast<int>(Column::ThreatCategory):
contentsSize = fontMetrics.size(0, QString(6, 'W'));
break;
case static_cast<int>(Column::Tornado):
contentsSize = fontMetrics.size(0, QString(5, 'W'));
break;
case static_cast<int>(Column::State):
contentsSize = fontMetrics.size(0, "WW, WW");
break;
@ -285,6 +338,12 @@ void AlertModel::HandleAlert(const types::TextEventKey& alertKey,
std::shared_ptr<const awips::Segment> alertSegment =
alertMessages[messageIndex]->segments().back();
p->observedMap_.insert_or_assign(alertKey, alertSegment->observed_);
p->threatCategoryMap_.insert_or_assign(alertKey,
alertSegment->threatCategory_);
p->tornadoPossibleMap_.insert_or_assign(alertKey,
alertSegment->tornadoPossible_);
if (alertSegment->codedLocation_.has_value())
{
// Update centroid and distance
@ -365,6 +424,46 @@ AlertModelImpl::AlertModelImpl() :
{
}
bool AlertModelImpl::GetObserved(const types::TextEventKey& key)
{
bool observed = false;
auto it = observedMap_.find(key);
if (it != observedMap_.cend())
{
observed = it->second;
}
return observed;
}
awips::ThreatCategory
AlertModelImpl::GetThreatCategory(const types::TextEventKey& key)
{
awips::ThreatCategory threatCategory = awips::ThreatCategory::Base;
auto it = threatCategoryMap_.find(key);
if (it != threatCategoryMap_.cend())
{
threatCategory = it->second;
}
return threatCategory;
}
bool AlertModelImpl::GetTornadoPossible(const types::TextEventKey& key)
{
bool tornadoPossible = false;
auto it = tornadoPossibleMap_.find(key);
if (it != tornadoPossibleMap_.cend())
{
tornadoPossible = it->second;
}
return tornadoPossible;
}
std::string AlertModelImpl::GetCounties(const types::TextEventKey& key)
{
auto messageList = manager::TextEventManager::Instance()->message_list(key);

View file

@ -21,15 +21,17 @@ class AlertModel : public QAbstractTableModel
public:
enum class Column : int
{
Etn = 0,
OfficeId = 1,
Phenomenon = 2,
Significance = 3,
State = 4,
Counties = 5,
StartTime = 6,
EndTime = 7,
Distance = 8
Etn = 0,
OfficeId = 1,
Phenomenon = 2,
Significance = 3,
ThreatCategory = 4,
Tornado = 5,
State = 6,
Counties = 7,
StartTime = 8,
EndTime = 9,
Distance = 10
};
explicit AlertModel(QObject* parent = nullptr);

View file

@ -0,0 +1,24 @@
#pragma once
#include <string>
namespace scwx
{
namespace awips
{
enum class ThreatCategory : int
{
Base = 0,
Significant = 1,
Considerable = 2,
Destructive = 3,
Catastrophic = 4,
Unknown
};
ThreatCategory GetThreatCategory(const std::string& name);
const std::string& GetThreatCategoryName(ThreatCategory threatCategory);
} // namespace awips
} // namespace scwx

View file

@ -2,6 +2,7 @@
#include <scwx/awips/coded_location.hpp>
#include <scwx/awips/coded_time_motion_location.hpp>
#include <scwx/awips/impact_based_warnings.hpp>
#include <scwx/awips/message.hpp>
#include <scwx/awips/pvtec.hpp>
#include <scwx/awips/ugc.hpp>
@ -56,15 +57,16 @@ struct SegmentHeader
struct Segment
{
std::optional<SegmentHeader> header_;
std::vector<std::string> productContent_;
std::optional<CodedLocation> codedLocation_;
std::optional<CodedTimeMotionLocation> codedMotion_;
std::optional<SegmentHeader> header_ {};
std::vector<std::string> productContent_ {};
std::optional<CodedLocation> codedLocation_ {};
std::optional<CodedTimeMotionLocation> codedMotion_ {};
Segment() :
header_ {}, productContent_ {}, codedLocation_ {}, codedMotion_ {}
{
}
bool observed_ {false};
ThreatCategory threatCategory_ {ThreatCategory::Base};
bool tornadoPossible_ {false};
Segment() = default;
Segment(const Segment&) = delete;
Segment& operator=(const Segment&) = delete;

View file

@ -0,0 +1,31 @@
#include <scwx/awips/impact_based_warnings.hpp>
#include <scwx/util/enum.hpp>
#include <unordered_map>
#include <boost/algorithm/string.hpp>
namespace scwx
{
namespace awips
{
static const std::string logPrefix_ = "scwx::awips::impact_based_warnings";
static const std::unordered_map<ThreatCategory, std::string>
threatCategoryName_ {{ThreatCategory::Base, "Base"},
{ThreatCategory::Significant, "Significant"},
{ThreatCategory::Considerable, "Considerable"},
{ThreatCategory::Destructive, "Destructive"},
{ThreatCategory::Catastrophic, "Catastrophic"},
{ThreatCategory::Unknown, "?"}};
SCWX_GET_ENUM(ThreatCategory, GetThreatCategory, threatCategoryName_)
const std::string& GetThreatCategoryName(ThreatCategory threatCategory)
{
return threatCategoryName_.at(threatCategory);
}
} // namespace awips
} // namespace scwx

View file

@ -3,6 +3,7 @@
#include <scwx/util/logger.hpp>
#include <scwx/util/streams.hpp>
#include <algorithm>
#include <istream>
#include <string>
@ -304,6 +305,14 @@ void ParseCodedInformation(std::shared_ptr<Segment> segment,
{
typedef std::vector<std::string>::const_iterator StringIterator;
static constexpr std::size_t kThreatCategoryTagCount = 4;
static const std::array<std::string, kThreatCategoryTagCount>
kThreatCategoryTags {"FLASH FLOOD DAMAGE THREAT...",
"SNOW SQUALL IMPACT...",
"THUNDERSTORM DAMAGE THREAT...",
"TORNADO DAMAGE THREAT..."};
std::array<std::string, kThreatCategoryTagCount>::const_iterator threatTagIt;
std::vector<std::string>& productContent = segment->productContent_;
StringIterator codedLocationBegin = productContent.cend();
@ -325,8 +334,8 @@ void ParseCodedInformation(std::shared_ptr<Segment> segment,
codedLocationEnd = it;
}
if (codedMotionBegin == productContent.cend() &&
it->starts_with("TIME...MOT...LOC"))
else if (codedMotionBegin == productContent.cend() &&
it->starts_with("TIME...MOT...LOC"))
{
codedMotionBegin = it;
}
@ -338,6 +347,37 @@ void ParseCodedInformation(std::shared_ptr<Segment> segment,
{
codedMotionEnd = it;
}
else if (!segment->observed_ &&
it->find("...OBSERVED") != std::string::npos)
{
segment->observed_ = true;
}
else if (!segment->tornadoPossible_ && *it == "TORNADO...POSSIBLE")
{
segment->tornadoPossible_ = true;
}
else if (segment->threatCategory_ == ThreatCategory::Base &&
(threatTagIt = std::find_if(kThreatCategoryTags.cbegin(),
kThreatCategoryTags.cend(),
[&it](const std::string& tag) {
return it->starts_with(tag);
})) != kThreatCategoryTags.cend() &&
it->length() > threatTagIt->length())
{
const std::string threatCategoryName =
it->substr(threatTagIt->length());
ThreatCategory threatCategory = GetThreatCategory(threatCategoryName);
if (threatCategory == ThreatCategory::Unknown)
{
threatCategory = ThreatCategory::Base;
}
segment->threatCategory_ = threatCategory;
}
}
if (codedLocationBegin != productContent.cend())

View file

@ -14,6 +14,7 @@ endif()
set(HDR_AWIPS include/scwx/awips/coded_location.hpp
include/scwx/awips/coded_time_motion_location.hpp
include/scwx/awips/impact_based_warnings.hpp
include/scwx/awips/message.hpp
include/scwx/awips/phenomenon.hpp
include/scwx/awips/pvtec.hpp
@ -24,6 +25,7 @@ set(HDR_AWIPS include/scwx/awips/coded_location.hpp
include/scwx/awips/wmo_header.hpp)
set(SRC_AWIPS source/scwx/awips/coded_location.cpp
source/scwx/awips/coded_time_motion_location.cpp
source/scwx/awips/impact_based_warnings.cpp
source/scwx/awips/message.cpp
source/scwx/awips/phenomenon.cpp
source/scwx/awips/pvtec.cpp