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:
CC: ${{ matrix.env_cc }}
CXX: ${{ matrix.env_cxx }}
SCWX_VERSION: v0.4.4
SCWX_VERSION: v0.4.5
runs-on: ${{ matrix.os }}
steps:

View file

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.21)
set(PROJECT_NAME supercell-wx)
project(${PROJECT_NAME}
VERSION 0.4.3
VERSION 0.4.5
DESCRIPTION "Supercell Wx is a free, open source advanced weather radar viewer."
HOMEPAGE_URL "https://github.com/dpaulat/supercell-wx"
LANGUAGES C CXX)
@ -44,7 +44,7 @@ conan_basic_setup(TARGETS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBOOST_ALL_NO_LIB")
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)

View file

@ -3,8 +3,8 @@
IDI_ICON1 ICON "icons\\scwx-256.ico"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,4,4,0
PRODUCTVERSION 0,4,4,0
FILEVERSION 0,4,5,0
PRODUCTVERSION 0,4,5,0
FILEFLAGS 0x0L
FILEFLAGSMASK 0x3fL
FILEOS 0x00040004L
@ -17,12 +17,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Dan Paulat"
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 "InternalName", "scwx"
VALUE "OriginalFilename", "supercell-wx.exe"
VALUE "ProductName", "Supercell Wx"
VALUE "ProductVersion", "0.4.4.0"
VALUE "ProductVersion", "0.4.5.0"
END
END
BLOCK "VarFileInfo"

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

@ -25,11 +25,13 @@ public:
OfficeId = 1,
Phenomenon = 2,
Significance = 3,
State = 4,
Counties = 5,
StartTime = 6,
EndTime = 7,
Distance = 8
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

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

View file

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

View file

@ -1,5 +1,6 @@
#pragma once
#include <cstdint>
#include <optional>
#include <string>
#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/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,7 +334,7 @@ void ParseCodedInformation(std::shared_ptr<Segment> segment,
codedLocationEnd = it;
}
if (codedMotionBegin == productContent.cend() &&
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

@ -62,10 +62,13 @@ std::string TimeString(std::chrono::system_clock::time_point time,
#if defined(_MSC_VER)
# 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}"
namespace date = std::chrono;
namespace df = std;
#else
# 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"
using namespace date;
namespace df = date;
#endif
auto timeInSeconds = time_point_cast<seconds>(time);
@ -77,15 +80,15 @@ std::string TimeString(std::chrono::system_clock::time_point time,
{
try
{
zoned_time zt = {timeZone, timeInSeconds};
date::zoned_time zt = {timeZone, timeInSeconds};
if (clockFormat == ClockFormat::_24Hour)
{
os << format(FORMAT_STRING_24_HOUR, zt);
os << df::format(FORMAT_STRING_24_HOUR, zt);
}
else
{
os << format(FORMAT_STRING_12_HOUR, zt);
os << df::format(FORMAT_STRING_12_HOUR, zt);
}
}
catch (const std::exception& ex)
@ -107,11 +110,11 @@ std::string TimeString(std::chrono::system_clock::time_point time,
{
if (clockFormat == ClockFormat::_24Hour)
{
os << format(FORMAT_STRING_24_HOUR, timeInSeconds);
os << df::format(FORMAT_STRING_24_HOUR, timeInSeconds);
}
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
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