Merge branch 'dpaulat:develop' into audio_location_methods

This commit is contained in:
Aden Koperczak 2024-07-06 17:02:40 -04:00 committed by GitHub
commit 1a37dbff27
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 238 additions and 32 deletions

View file

@ -60,7 +60,7 @@ jobs:
env: env:
CC: ${{ matrix.env_cc }} CC: ${{ matrix.env_cc }}
CXX: ${{ matrix.env_cxx }} CXX: ${{ matrix.env_cxx }}
SCWX_VERSION: v0.4.4 SCWX_VERSION: v0.4.5
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:

View file

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.21) cmake_minimum_required(VERSION 3.21)
set(PROJECT_NAME supercell-wx) set(PROJECT_NAME supercell-wx)
project(${PROJECT_NAME} project(${PROJECT_NAME}
VERSION 0.4.3 VERSION 0.4.5
DESCRIPTION "Supercell Wx is a free, open source advanced weather radar viewer." DESCRIPTION "Supercell Wx is a free, open source advanced weather radar viewer."
HOMEPAGE_URL "https://github.com/dpaulat/supercell-wx" HOMEPAGE_URL "https://github.com/dpaulat/supercell-wx"
LANGUAGES C CXX) LANGUAGES C CXX)
@ -44,7 +44,7 @@ conan_basic_setup(TARGETS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBOOST_ALL_NO_LIB") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBOOST_ALL_NO_LIB")
set(SCWX_DIR ${PROJECT_SOURCE_DIR}) set(SCWX_DIR ${PROJECT_SOURCE_DIR})
set(SCWX_VERSION "0.4.4") set(SCWX_VERSION "0.4.5")
option(SCWX_ADDRESS_SANITIZER "Build with Address Sanitizer" OFF) option(SCWX_ADDRESS_SANITIZER "Build with Address Sanitizer" OFF)

View file

@ -3,8 +3,8 @@
IDI_ICON1 ICON "icons\\scwx-256.ico" IDI_ICON1 ICON "icons\\scwx-256.ico"
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,4,4,0 FILEVERSION 0,4,5,0
PRODUCTVERSION 0,4,4,0 PRODUCTVERSION 0,4,5,0
FILEFLAGS 0x0L FILEFLAGS 0x0L
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
FILEOS 0x00040004L FILEOS 0x00040004L
@ -17,12 +17,12 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Dan Paulat" VALUE "CompanyName", "Dan Paulat"
VALUE "FileDescription", "Supercell Wx" VALUE "FileDescription", "Supercell Wx"
VALUE "FileVersion", "0.4.4.0" VALUE "FileVersion", "0.4.5.0"
VALUE "LegalCopyright", "Copyright (C) 2021-2024 Dan Paulat" VALUE "LegalCopyright", "Copyright (C) 2021-2024 Dan Paulat"
VALUE "InternalName", "scwx" VALUE "InternalName", "scwx"
VALUE "OriginalFilename", "supercell-wx.exe" VALUE "OriginalFilename", "supercell-wx.exe"
VALUE "ProductName", "Supercell Wx" VALUE "ProductName", "Supercell Wx"
VALUE "ProductVersion", "0.4.4.0" VALUE "ProductVersion", "0.4.5.0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

@ -34,6 +34,10 @@ public:
explicit AlertModelImpl(); explicit AlertModelImpl();
~AlertModelImpl() = default; ~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 GetCounties(const types::TextEventKey& key);
static std::string GetState(const types::TextEventKey& key); static std::string GetState(const types::TextEventKey& key);
static std::chrono::system_clock::time_point static std::chrono::system_clock::time_point
@ -49,6 +53,18 @@ public:
const GeographicLib::Geodesic& geodesic_; 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, std::unordered_map<types::TextEventKey,
common::Coordinate, common::Coordinate,
types::TextEventHash<types::TextEventKey>> types::TextEventHash<types::TextEventKey>>
@ -125,6 +141,29 @@ QVariant AlertModel::data(const QModelIndex& index, int role) const
return QString::fromStdString( return QString::fromStdString(
awips::GetSignificanceText(textEventKey.significance_)); 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): case static_cast<int>(Column::State):
return QString::fromStdString(AlertModelImpl::GetState(textEventKey)); 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): case static_cast<int>(Column::Significance):
return tr("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): case static_cast<int>(Column::State):
return tr("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')); contentsSize = fontMetrics.size(0, QString(10, 'W'));
break; 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): case static_cast<int>(Column::State):
contentsSize = fontMetrics.size(0, "WW, WW"); contentsSize = fontMetrics.size(0, "WW, WW");
break; break;
@ -285,6 +338,12 @@ void AlertModel::HandleAlert(const types::TextEventKey& alertKey,
std::shared_ptr<const awips::Segment> alertSegment = std::shared_ptr<const awips::Segment> alertSegment =
alertMessages[messageIndex]->segments().back(); 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()) if (alertSegment->codedLocation_.has_value())
{ {
// Update centroid and distance // 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) std::string AlertModelImpl::GetCounties(const types::TextEventKey& key)
{ {
auto messageList = manager::TextEventManager::Instance()->message_list(key); auto messageList = manager::TextEventManager::Instance()->message_list(key);

View file

@ -21,15 +21,17 @@ class AlertModel : public QAbstractTableModel
public: public:
enum class Column : int enum class Column : int
{ {
Etn = 0, Etn = 0,
OfficeId = 1, OfficeId = 1,
Phenomenon = 2, Phenomenon = 2,
Significance = 3, Significance = 3,
State = 4, ThreatCategory = 4,
Counties = 5, Tornado = 5,
StartTime = 6, State = 6,
EndTime = 7, Counties = 7,
Distance = 8 StartTime = 8,
EndTime = 9,
Distance = 10
}; };
explicit AlertModel(QObject* parent = nullptr); 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_location.hpp>
#include <scwx/awips/coded_time_motion_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/message.hpp>
#include <scwx/awips/pvtec.hpp> #include <scwx/awips/pvtec.hpp>
#include <scwx/awips/ugc.hpp> #include <scwx/awips/ugc.hpp>
@ -56,15 +57,16 @@ struct SegmentHeader
struct Segment struct Segment
{ {
std::optional<SegmentHeader> header_; std::optional<SegmentHeader> header_ {};
std::vector<std::string> productContent_; std::vector<std::string> productContent_ {};
std::optional<CodedLocation> codedLocation_; std::optional<CodedLocation> codedLocation_ {};
std::optional<CodedTimeMotionLocation> codedMotion_; std::optional<CodedTimeMotionLocation> codedMotion_ {};
Segment() : bool observed_ {false};
header_ {}, productContent_ {}, codedLocation_ {}, codedMotion_ {} ThreatCategory threatCategory_ {ThreatCategory::Base};
{ bool tornadoPossible_ {false};
}
Segment() = default;
Segment(const Segment&) = delete; Segment(const Segment&) = delete;
Segment& operator=(const Segment&) = delete; Segment& operator=(const Segment&) = delete;

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <cstdint>
#include <string> #include <string>
namespace scwx namespace scwx
@ -7,7 +8,7 @@ namespace scwx
namespace common namespace common
{ {
std::string GetVcpDescription(uint16_t vcp); std::string GetVcpDescription(std::uint16_t vcp);
} // namespace common } // namespace common
} // namespace scwx } // namespace scwx

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <cstdint>
#include <istream> #include <istream>
#include <vector> #include <vector>

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <cstdint>
#include <optional> #include <optional>
#include <string> #include <string>
#include <vector> #include <vector>

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/logger.hpp>
#include <scwx/util/streams.hpp> #include <scwx/util/streams.hpp>
#include <algorithm>
#include <istream> #include <istream>
#include <string> #include <string>
@ -304,6 +305,14 @@ void ParseCodedInformation(std::shared_ptr<Segment> segment,
{ {
typedef std::vector<std::string>::const_iterator StringIterator; 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_; std::vector<std::string>& productContent = segment->productContent_;
StringIterator codedLocationBegin = productContent.cend(); StringIterator codedLocationBegin = productContent.cend();
@ -325,8 +334,8 @@ void ParseCodedInformation(std::shared_ptr<Segment> segment,
codedLocationEnd = it; codedLocationEnd = it;
} }
if (codedMotionBegin == productContent.cend() && else if (codedMotionBegin == productContent.cend() &&
it->starts_with("TIME...MOT...LOC")) it->starts_with("TIME...MOT...LOC"))
{ {
codedMotionBegin = it; codedMotionBegin = it;
} }
@ -338,6 +347,37 @@ void ParseCodedInformation(std::shared_ptr<Segment> segment,
{ {
codedMotionEnd = it; 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()) if (codedLocationBegin != productContent.cend())

View file

@ -62,10 +62,13 @@ std::string TimeString(std::chrono::system_clock::time_point time,
#if defined(_MSC_VER) #if defined(_MSC_VER)
# define FORMAT_STRING_24_HOUR "{:%Y-%m-%d %H:%M:%S %Z}" # define FORMAT_STRING_24_HOUR "{:%Y-%m-%d %H:%M:%S %Z}"
# define FORMAT_STRING_12_HOUR "{:%Y-%m-%d %I:%M:%S %p %Z}" # define FORMAT_STRING_12_HOUR "{:%Y-%m-%d %I:%M:%S %p %Z}"
namespace date = std::chrono;
namespace df = std;
#else #else
# define FORMAT_STRING_24_HOUR "%Y-%m-%d %H:%M:%S %Z" # define FORMAT_STRING_24_HOUR "%Y-%m-%d %H:%M:%S %Z"
# define FORMAT_STRING_12_HOUR "%Y-%m-%d %I:%M:%S %p %Z" # define FORMAT_STRING_12_HOUR "%Y-%m-%d %I:%M:%S %p %Z"
using namespace date; using namespace date;
namespace df = date;
#endif #endif
auto timeInSeconds = time_point_cast<seconds>(time); auto timeInSeconds = time_point_cast<seconds>(time);
@ -77,15 +80,15 @@ std::string TimeString(std::chrono::system_clock::time_point time,
{ {
try try
{ {
zoned_time zt = {timeZone, timeInSeconds}; date::zoned_time zt = {timeZone, timeInSeconds};
if (clockFormat == ClockFormat::_24Hour) if (clockFormat == ClockFormat::_24Hour)
{ {
os << format(FORMAT_STRING_24_HOUR, zt); os << df::format(FORMAT_STRING_24_HOUR, zt);
} }
else else
{ {
os << format(FORMAT_STRING_12_HOUR, zt); os << df::format(FORMAT_STRING_12_HOUR, zt);
} }
} }
catch (const std::exception& ex) catch (const std::exception& ex)
@ -107,11 +110,11 @@ std::string TimeString(std::chrono::system_clock::time_point time,
{ {
if (clockFormat == ClockFormat::_24Hour) if (clockFormat == ClockFormat::_24Hour)
{ {
os << format(FORMAT_STRING_24_HOUR, timeInSeconds); os << df::format(FORMAT_STRING_24_HOUR, timeInSeconds);
} }
else else
{ {
os << format(FORMAT_STRING_12_HOUR, timeInSeconds); os << df::format(FORMAT_STRING_12_HOUR, timeInSeconds);
} }
} }
} }

View file

@ -14,6 +14,7 @@ endif()
set(HDR_AWIPS include/scwx/awips/coded_location.hpp set(HDR_AWIPS include/scwx/awips/coded_location.hpp
include/scwx/awips/coded_time_motion_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/message.hpp
include/scwx/awips/phenomenon.hpp include/scwx/awips/phenomenon.hpp
include/scwx/awips/pvtec.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) include/scwx/awips/wmo_header.hpp)
set(SRC_AWIPS source/scwx/awips/coded_location.cpp set(SRC_AWIPS source/scwx/awips/coded_location.cpp
source/scwx/awips/coded_time_motion_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/message.cpp
source/scwx/awips/phenomenon.cpp source/scwx/awips/phenomenon.cpp
source/scwx/awips/pvtec.cpp source/scwx/awips/pvtec.cpp