Merge pull request #267 from AdenKoperczak/wfo-audio-location-method

Add WFO audio location method
This commit is contained in:
Dan Paulat 2024-09-10 08:19:47 -05:00 committed by GitHub
commit 21cb6859a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 623 additions and 147 deletions

2
data

@ -1 +1 @@
Subproject commit 1d93f4106761cabbe61e1265bb1d2b8501600177 Subproject commit 8eb89b19fdd1c78e896cc6cb47e07425bb473699

View file

@ -240,6 +240,7 @@ set(HDR_UI source/scwx/qt/ui/about_dialog.hpp
source/scwx/qt/ui/animation_dock_widget.hpp source/scwx/qt/ui/animation_dock_widget.hpp
source/scwx/qt/ui/collapsible_group.hpp source/scwx/qt/ui/collapsible_group.hpp
source/scwx/qt/ui/county_dialog.hpp source/scwx/qt/ui/county_dialog.hpp
source/scwx/qt/ui/wfo_dialog.hpp
source/scwx/qt/ui/download_dialog.hpp source/scwx/qt/ui/download_dialog.hpp
source/scwx/qt/ui/flow_layout.hpp source/scwx/qt/ui/flow_layout.hpp
source/scwx/qt/ui/gps_info_dialog.hpp source/scwx/qt/ui/gps_info_dialog.hpp
@ -265,6 +266,7 @@ set(SRC_UI source/scwx/qt/ui/about_dialog.cpp
source/scwx/qt/ui/animation_dock_widget.cpp source/scwx/qt/ui/animation_dock_widget.cpp
source/scwx/qt/ui/collapsible_group.cpp source/scwx/qt/ui/collapsible_group.cpp
source/scwx/qt/ui/county_dialog.cpp source/scwx/qt/ui/county_dialog.cpp
source/scwx/qt/ui/wfo_dialog.cpp
source/scwx/qt/ui/download_dialog.cpp source/scwx/qt/ui/download_dialog.cpp
source/scwx/qt/ui/flow_layout.cpp source/scwx/qt/ui/flow_layout.cpp
source/scwx/qt/ui/gps_info_dialog.cpp source/scwx/qt/ui/gps_info_dialog.cpp
@ -290,6 +292,7 @@ set(UI_UI source/scwx/qt/ui/about_dialog.ui
source/scwx/qt/ui/animation_dock_widget.ui source/scwx/qt/ui/animation_dock_widget.ui
source/scwx/qt/ui/collapsible_group.ui source/scwx/qt/ui/collapsible_group.ui
source/scwx/qt/ui/county_dialog.ui source/scwx/qt/ui/county_dialog.ui
source/scwx/qt/ui/wfo_dialog.ui
source/scwx/qt/ui/gps_info_dialog.ui source/scwx/qt/ui/gps_info_dialog.ui
source/scwx/qt/ui/imgui_debug_dialog.ui source/scwx/qt/ui/imgui_debug_dialog.ui
source/scwx/qt/ui/layer_dialog.ui source/scwx/qt/ui/layer_dialog.ui
@ -388,6 +391,7 @@ set(ZONE_DBF_FILES ${SCWX_DIR}/data/db/fz05mr24.dbf
${SCWX_DIR}/data/db/oz05mr24.dbf ${SCWX_DIR}/data/db/oz05mr24.dbf
${SCWX_DIR}/data/db/z_05mr24.dbf) ${SCWX_DIR}/data/db/z_05mr24.dbf)
set(STATE_DBF_FILES ${SCWX_DIR}/data/db/s_05mr24.dbf) set(STATE_DBF_FILES ${SCWX_DIR}/data/db/s_05mr24.dbf)
set(WFO_DBF_FILES ${SCWX_DIR}/data/db/w_05mr24.dbf)
set(COUNTIES_SQLITE_DB ${scwx-qt_BINARY_DIR}/res/db/counties.db) set(COUNTIES_SQLITE_DB ${scwx-qt_BINARY_DIR}/res/db/counties.db)
set(RESOURCE_INPUT ${scwx-qt_SOURCE_DIR}/res/scwx-qt.rc.in) set(RESOURCE_INPUT ${scwx-qt_SOURCE_DIR}/res/scwx-qt.rc.in)
@ -483,11 +487,13 @@ add_custom_command(OUTPUT ${COUNTIES_SQLITE_DB}
-c ${COUNTY_DBF_FILES} -c ${COUNTY_DBF_FILES}
-z ${ZONE_DBF_FILES} -z ${ZONE_DBF_FILES}
-s ${STATE_DBF_FILES} -s ${STATE_DBF_FILES}
-w ${WFO_DBF_FILES}
-o ${COUNTIES_SQLITE_DB} -o ${COUNTIES_SQLITE_DB}
DEPENDS ${scwx-qt_SOURCE_DIR}/tools/generate_counties_db.py DEPENDS ${scwx-qt_SOURCE_DIR}/tools/generate_counties_db.py
${COUNTY_DB_FILES} ${COUNTY_DB_FILES}
${STATE_DBF_FILES} ${STATE_DBF_FILES}
${ZONE_DBF_FILES}) ${ZONE_DBF_FILES}
${WFO_DBF_FILES})
add_custom_target(scwx-qt_generate_counties_db ALL add_custom_target(scwx-qt_generate_counties_db ALL
DEPENDS ${COUNTIES_SQLITE_DB}) DEPENDS ${COUNTIES_SQLITE_DB})

View file

@ -31,6 +31,7 @@ typedef std::unordered_map<char, StateMap> FormatMap;
static bool initialized_ {false}; static bool initialized_ {false};
static FormatMap countyDatabase_; static FormatMap countyDatabase_;
static std::unordered_map<std::string, std::string> stateMap_; static std::unordered_map<std::string, std::string> stateMap_;
static std::unordered_map<std::string, std::string> wfoMap_;
void Initialize() void Initialize()
{ {
@ -168,6 +169,39 @@ void Initialize()
sqlite3_free(errorMessage); sqlite3_free(errorMessage);
} }
// Query database for WFOs
rc = sqlite3_exec(
db,
"SELECT id, city_state FROM wfos",
[](void* /* param */,
int columns,
char** columnText,
char** /* columnName */) -> int
{
int status = 0;
if (columns == 2)
{
wfoMap_.emplace(columnText[0], columnText[1]);
}
else
{
logger_->error(
"WFO database format error, invalid number of columns: {}",
columns);
status = -1;
}
return status;
},
nullptr,
&errorMessage);
if (rc != SQLITE_OK)
{
logger_->error("SQL error: {}", errorMessage);
sqlite3_free(errorMessage);
}
// Close database // Close database
sqlite3_close(db); sqlite3_close(db);
@ -230,6 +264,22 @@ const std::unordered_map<std::string, std::string>& GetStates()
return stateMap_; return stateMap_;
} }
const std::unordered_map<std::string, std::string>& GetWFOs()
{
return wfoMap_;
}
const std::string& GetWFOName(const std::string& wfoId)
{
auto wfo = wfoMap_.find(wfoId);
if (wfo == wfoMap_.end())
{
return wfoId;
}
return wfo->second;
}
} // namespace CountyDatabase } // namespace CountyDatabase
} // namespace config } // namespace config
} // namespace qt } // namespace qt

View file

@ -19,6 +19,8 @@ std::string GetCountyName(const std::string& id);
std::unordered_map<std::string, std::string> std::unordered_map<std::string, std::string>
GetCounties(const std::string& state); GetCounties(const std::string& state);
const std::unordered_map<std::string, std::string>& GetStates(); const std::unordered_map<std::string, std::string>& GetStates();
const std::unordered_map<std::string, std::string>& GetWFOs();
const std::string& GetWFOName(const std::string& wfoId);
} // namespace CountyDatabase } // namespace CountyDatabase
} // namespace config } // namespace config

View file

@ -151,6 +151,7 @@ void AlertManager::Impl::HandleAlert(const types::TextEventKey& key,
std::string alertCounty = audioSettings.alert_county().GetValue(); std::string alertCounty = audioSettings.alert_county().GetValue();
auto alertRadius = units::length::kilometers<double>( auto alertRadius = units::length::kilometers<double>(
audioSettings.alert_radius().GetValue()); audioSettings.alert_radius().GetValue());
std::string alertWFO = audioSettings.alert_wfo().GetValue();
auto message = textEventManager_->message_list(key).at(messageIndex); auto message = textEventManager_->message_list(key).at(messageIndex);
@ -185,9 +186,7 @@ void AlertManager::Impl::HandleAlert(const types::TextEventKey& key,
auto alertCoordinates = segment->codedLocation_->coordinates(); auto alertCoordinates = segment->codedLocation_->coordinates();
activeAtLocation = util::GeographicLib::AreaInRangeOfPoint( activeAtLocation = util::GeographicLib::AreaInRangeOfPoint(
alertCoordinates, alertCoordinates, currentCoordinate, alertRadius);
currentCoordinate,
alertRadius);
} }
else if (locationMethod == types::LocationMethod::County) else if (locationMethod == types::LocationMethod::County)
{ {
@ -196,6 +195,12 @@ void AlertManager::Impl::HandleAlert(const types::TextEventKey& key,
auto it = std::find(fipsIds.cbegin(), fipsIds.cend(), alertCounty); auto it = std::find(fipsIds.cbegin(), fipsIds.cend(), alertCounty);
activeAtLocation = it != fipsIds.cend(); activeAtLocation = it != fipsIds.cend();
} }
else if (locationMethod == types::LocationMethod::WFO)
{
std::string wfoId = vtec.pVtec_.office_id();
activeAtLocation = wfoId == alertWFO;
}
if (activeAtLocation) if (activeAtLocation)
{ {

View file

@ -39,6 +39,7 @@ public:
alertLongitude_.SetDefault(0.0); alertLongitude_.SetDefault(0.0);
alertRadius_.SetDefault(0.0); alertRadius_.SetDefault(0.0);
alertRadarSite_.SetDefault("default"); alertRadarSite_.SetDefault("default");
alertWFO_.SetDefault("");
ignoreMissingCodecs_.SetDefault(false); ignoreMissingCodecs_.SetDefault(false);
alertLatitude_.SetMinimum(-90.0); alertLatitude_.SetMinimum(-90.0);
@ -62,6 +63,14 @@ public:
config::CountyDatabase::GetCountyName(value) != value; config::CountyDatabase::GetCountyName(value) != value;
}); });
alertWFO_.SetValidator(
[](const std::string& value)
{
return value.empty() ||
config::CountyDatabase::GetWFOs().count(value) != 0;
});
auto& alertAudioPhenomena = types::GetAlertAudioPhenomena(); auto& alertAudioPhenomena = types::GetAlertAudioPhenomena();
alertEnabled_.reserve(alertAudioPhenomena.size() + 1); alertEnabled_.reserve(alertAudioPhenomena.size() + 1);
@ -94,6 +103,7 @@ public:
SettingsVariable<std::string> alertRadarSite_ {"alert_radar_site"}; SettingsVariable<std::string> alertRadarSite_ {"alert_radar_site"};
SettingsVariable<double> alertRadius_ {"alert_radius"}; SettingsVariable<double> alertRadius_ {"alert_radius"};
SettingsVariable<std::string> alertCounty_ {"alert_county"}; SettingsVariable<std::string> alertCounty_ {"alert_county"};
SettingsVariable<std::string> alertWFO_ {"alert_wfo"};
SettingsVariable<bool> ignoreMissingCodecs_ {"ignore_missing_codecs"}; SettingsVariable<bool> ignoreMissingCodecs_ {"ignore_missing_codecs"};
std::unordered_map<awips::Phenomenon, SettingsVariable<bool>> std::unordered_map<awips::Phenomenon, SettingsVariable<bool>>
@ -111,6 +121,7 @@ AudioSettings::AudioSettings() :
&p->alertRadarSite_, &p->alertRadarSite_,
&p->alertRadius_, &p->alertRadius_,
&p->alertCounty_, &p->alertCounty_,
&p->alertWFO_,
&p->ignoreMissingCodecs_}); &p->ignoreMissingCodecs_});
RegisterVariables(p->variables_); RegisterVariables(p->variables_);
SetDefaults(); SetDefaults();
@ -157,6 +168,11 @@ SettingsVariable<std::string>& AudioSettings::alert_county() const
return p->alertCounty_; return p->alertCounty_;
} }
SettingsVariable<std::string>& AudioSettings::alert_wfo() const
{
return p->alertWFO_;
}
SettingsVariable<bool>& SettingsVariable<bool>&
AudioSettings::alert_enabled(awips::Phenomenon phenomenon) const AudioSettings::alert_enabled(awips::Phenomenon phenomenon) const
{ {
@ -188,6 +204,7 @@ bool operator==(const AudioSettings& lhs, const AudioSettings& rhs)
lhs.p->alertRadarSite_ == rhs.p->alertRadarSite_ && lhs.p->alertRadarSite_ == rhs.p->alertRadarSite_ &&
lhs.p->alertRadius_ == rhs.p->alertRadius_ && lhs.p->alertRadius_ == rhs.p->alertRadius_ &&
lhs.p->alertCounty_ == rhs.p->alertCounty_ && lhs.p->alertCounty_ == rhs.p->alertCounty_ &&
lhs.p->alertWFO_ == rhs.p->alertWFO_ &&
lhs.p->alertEnabled_ == rhs.p->alertEnabled_); lhs.p->alertEnabled_ == rhs.p->alertEnabled_);
} }

View file

@ -33,6 +33,7 @@ public:
SettingsVariable<double>& alert_radius() const; SettingsVariable<double>& alert_radius() const;
SettingsVariable<std::string>& alert_radar_site() const; SettingsVariable<std::string>& alert_radar_site() const;
SettingsVariable<std::string>& alert_county() const; SettingsVariable<std::string>& alert_county() const;
SettingsVariable<std::string>& alert_wfo() const;
SettingsVariable<bool>& alert_enabled(awips::Phenomenon phenomenon) const; SettingsVariable<bool>& alert_enabled(awips::Phenomenon phenomenon) const;
SettingsVariable<bool>& ignore_missing_codecs() const; SettingsVariable<bool>& ignore_missing_codecs() const;

View file

@ -17,6 +17,7 @@ static const std::unordered_map<LocationMethod, std::string>
{LocationMethod::Track, "Track"}, {LocationMethod::Track, "Track"},
{LocationMethod::RadarSite, "Radar Site"}, {LocationMethod::RadarSite, "Radar Site"},
{LocationMethod::County, "County"}, {LocationMethod::County, "County"},
{LocationMethod::WFO, "WFO"},
{LocationMethod::All, "All"}, {LocationMethod::All, "All"},
{LocationMethod::Unknown, "?"}}; {LocationMethod::Unknown, "?"}};

View file

@ -17,6 +17,7 @@ enum class LocationMethod
Track, Track,
RadarSite, RadarSite,
County, County,
WFO,
All, All,
Unknown Unknown
}; };

View file

@ -23,6 +23,7 @@
#include <scwx/qt/types/time_types.hpp> #include <scwx/qt/types/time_types.hpp>
#include <scwx/qt/types/unit_types.hpp> #include <scwx/qt/types/unit_types.hpp>
#include <scwx/qt/ui/county_dialog.hpp> #include <scwx/qt/ui/county_dialog.hpp>
#include <scwx/qt/ui/wfo_dialog.hpp>
#include <scwx/qt/ui/radar_site_dialog.hpp> #include <scwx/qt/ui/radar_site_dialog.hpp>
#include <scwx/qt/ui/serial_port_dialog.hpp> #include <scwx/qt/ui/serial_port_dialog.hpp>
#include <scwx/qt/ui/settings/hotkey_settings_widget.hpp> #include <scwx/qt/ui/settings/hotkey_settings_widget.hpp>
@ -108,6 +109,7 @@ public:
alertAudioRadarSiteDialog_ {new RadarSiteDialog(self)}, alertAudioRadarSiteDialog_ {new RadarSiteDialog(self)},
gpsSourceDialog_ {new SerialPortDialog(self)}, gpsSourceDialog_ {new SerialPortDialog(self)},
countyDialog_ {new CountyDialog(self)}, countyDialog_ {new CountyDialog(self)},
wfoDialog_ {new WFODialog(self)},
fontDialog_ {new QFontDialog(self)}, fontDialog_ {new QFontDialog(self)},
fontCategoryModel_ {new QStandardItemModel(self)}, fontCategoryModel_ {new QStandardItemModel(self)},
settings_ {std::initializer_list<settings::SettingsInterfaceBase*> { settings_ {std::initializer_list<settings::SettingsInterfaceBase*> {
@ -140,6 +142,7 @@ public:
&alertAudioRadarSite_, &alertAudioRadarSite_,
&alertAudioRadius_, &alertAudioRadius_,
&alertAudioCounty_, &alertAudioCounty_,
&alertAudioWFO_,
&hoverTextWrap_, &hoverTextWrap_,
&tooltipMethod_, &tooltipMethod_,
&placefileTextDropShadowEnabled_, &placefileTextDropShadowEnabled_,
@ -208,6 +211,7 @@ public:
RadarSiteDialog* alertAudioRadarSiteDialog_; RadarSiteDialog* alertAudioRadarSiteDialog_;
SerialPortDialog* gpsSourceDialog_; SerialPortDialog* gpsSourceDialog_;
CountyDialog* countyDialog_; CountyDialog* countyDialog_;
WFODialog* wfoDialog_;
QFontDialog* fontDialog_; QFontDialog* fontDialog_;
QStandardItemModel* fontCategoryModel_; QStandardItemModel* fontCategoryModel_;
@ -262,6 +266,7 @@ public:
settings::SettingsInterface<std::string> alertAudioRadarSite_ {}; settings::SettingsInterface<std::string> alertAudioRadarSite_ {};
settings::SettingsInterface<double> alertAudioRadius_ {}; settings::SettingsInterface<double> alertAudioRadius_ {};
settings::SettingsInterface<std::string> alertAudioCounty_ {}; settings::SettingsInterface<std::string> alertAudioCounty_ {};
settings::SettingsInterface<std::string> alertAudioWFO_ {};
std::unordered_map<awips::Phenomenon, settings::SettingsInterface<bool>> std::unordered_map<awips::Phenomenon, settings::SettingsInterface<bool>>
alertAudioEnabled_ {}; alertAudioEnabled_ {};
@ -948,6 +953,8 @@ void SettingsDialogImpl::SetupAudioTab()
locationMethod == types::LocationMethod::RadarSite; locationMethod == types::LocationMethod::RadarSite;
bool countyEntryEnabled = bool countyEntryEnabled =
locationMethod == types::LocationMethod::County; locationMethod == types::LocationMethod::County;
bool wfoEntryEnabled =
locationMethod == types::LocationMethod::WFO;
self_->ui->alertAudioLatitudeSpinBox->setEnabled( self_->ui->alertAudioLatitudeSpinBox->setEnabled(
coordinateEntryEnabled); coordinateEntryEnabled);
@ -974,6 +981,10 @@ void SettingsDialogImpl::SetupAudioTab()
self_->ui->alertAudioCountySelectButton->setEnabled( self_->ui->alertAudioCountySelectButton->setEnabled(
countyEntryEnabled); countyEntryEnabled);
self_->ui->resetAlertAudioCountyButton->setEnabled(countyEntryEnabled); self_->ui->resetAlertAudioCountyButton->setEnabled(countyEntryEnabled);
self_->ui->alertAudioWFOLineEdit->setEnabled(wfoEntryEnabled);
self_->ui->alertAudioWFOSelectButton->setEnabled(wfoEntryEnabled);
self_->ui->resetAlertAudioWFOButton->setEnabled(wfoEntryEnabled);
}); });
settings::AudioSettings& audioSettings = settings::AudioSettings::Instance(); settings::AudioSettings& audioSettings = settings::AudioSettings::Instance();
@ -1194,6 +1205,44 @@ void SettingsDialogImpl::SetupAudioTab()
alertAudioCounty_.SetSettingsVariable(audioSettings.alert_county()); alertAudioCounty_.SetSettingsVariable(audioSettings.alert_county());
alertAudioCounty_.SetEditWidget(self_->ui->alertAudioCountyLineEdit); alertAudioCounty_.SetEditWidget(self_->ui->alertAudioCountyLineEdit);
alertAudioCounty_.SetResetButton(self_->ui->resetAlertAudioCountyButton); alertAudioCounty_.SetResetButton(self_->ui->resetAlertAudioCountyButton);
QObject::connect(
self_->ui->alertAudioWFOSelectButton,
&QAbstractButton::clicked,
self_,
[this]()
{
wfoDialog_->show();
});
QObject::connect(wfoDialog_,
&WFODialog::accepted,
self_,
[this]()
{
std::string wfoId = wfoDialog_->wfo_id();
QString qWFOId = QString::fromStdString(wfoId);
self_->ui->alertAudioWFOLineEdit->setText(qWFOId);
// setText does not emit the textEdited signal
Q_EMIT self_->ui->alertAudioWFOLineEdit->textEdited(
qWFOId);
});
QObject::connect(self_->ui->alertAudioWFOLineEdit,
&QLineEdit::textChanged,
self_,
[this](const QString& text)
{
std::string wfoName =
config::CountyDatabase::GetWFOName(
text.toStdString());
self_->ui->alertAudioWFOLabel->setText(
QString::fromStdString(wfoName));
});
alertAudioWFO_.SetSettingsVariable(audioSettings.alert_wfo());
alertAudioWFO_.SetEditWidget(self_->ui->alertAudioWFOLineEdit);
alertAudioWFO_.SetResetButton(self_->ui->resetAlertAudioWFOButton);
} }
void SettingsDialogImpl::SetupTextTab() void SettingsDialogImpl::SetupTextTab()

View file

@ -136,8 +136,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>511</width> <width>274</width>
<height>647</height> <height>691</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
@ -610,8 +610,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>66</width> <width>98</width>
<height>18</height> <height>28</height>
</rect> </rect>
</property> </property>
<layout class="QGridLayout" name="gridLayout_3"> <layout class="QGridLayout" name="gridLayout_3">
@ -691,8 +691,15 @@
<string>Alerts</string> <string>Alerts</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_10"> <layout class="QGridLayout" name="gridLayout_10">
<item row="3" column="6"> <item row="7" column="0">
<widget class="QToolButton" name="resetAlertAudioLongitudeButton"> <widget class="QLabel" name="label_19">
<property name="text">
<string>County</string>
</property>
</widget>
</item>
<item row="4" column="6">
<widget class="QToolButton" name="resetAlertAudioRadarSiteButton">
<property name="text"> <property name="text">
<string>...</string> <string>...</string>
</property> </property>
@ -702,6 +709,71 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Location Method</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="alertAudioSoundLineEdit"/>
</item>
<item row="2" column="6">
<widget class="QToolButton" name="resetAlertAudioLatitudeButton">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../scwx-qt.qrc">
<normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Longitude</string>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QToolButton" name="alertAudioRadarSiteSelectButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QLabel" name="alertAudioRadiusUnitsLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Latitude</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QDoubleSpinBox" name="alertAudioLatitudeSpinBox">
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>-90.000000000000000</double>
</property>
<property name="maximum">
<double>90.000000000000000</double>
</property>
<property name="singleStep">
<double>0.000100000000000</double>
</property>
</widget>
</item>
<item row="5" column="1" colspan="2"> <item row="5" column="1" colspan="2">
<widget class="QDoubleSpinBox" name="alertAudioRadiusSpinBox"> <widget class="QDoubleSpinBox" name="alertAudioRadiusSpinBox">
<property name="decimals"> <property name="decimals">
@ -718,8 +790,8 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="6"> <item row="7" column="6">
<widget class="QToolButton" name="resetAlertAudioLatitudeButton"> <widget class="QToolButton" name="resetAlertAudioCountyButton">
<property name="text"> <property name="text">
<string>...</string> <string>...</string>
</property> </property>
@ -729,38 +801,25 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1" colspan="2"> <item row="0" column="4">
<widget class="QComboBox" name="alertAudioLocationMethodComboBox"> <widget class="QToolButton" name="alertAudioSoundTestButton">
<property name="sizePolicy"> <property name="icon">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <iconset resource="../../../../scwx-qt.qrc">
<horstretch>0</horstretch> <normaloff>:/res/icons/font-awesome-6/play-solid.svg</normaloff>:/res/icons/font-awesome-6/play-solid.svg</iconset>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0"> <item row="7" column="3">
<widget class="QLabel" name="label_19"> <widget class="QToolButton" name="alertAudioCountySelectButton">
<property name="text">
<string>County</string>
</property>
</widget>
</item>
<item row="0" column="6">
<widget class="QToolButton" name="resetAlertAudioSoundButton">
<property name="text"> <property name="text">
<string>...</string> <string>...</string>
</property> </property>
<property name="icon">
<iconset resource="../../../../scwx-qt.qrc">
<normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset>
</property>
</widget> </widget>
</item> </item>
<item row="5" column="3"> <item row="5" column="0">
<widget class="QLabel" name="alertAudioRadiusUnitsLabel"> <widget class="QLabel" name="alertAudioRadiusLabel">
<property name="text"> <property name="text">
<string/> <string>Radius</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -780,14 +839,10 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="6"> <item row="4" column="0">
<widget class="QToolButton" name="resetAlertAudioLocationMethodButton"> <widget class="QLabel" name="label_28">
<property name="text"> <property name="text">
<string>...</string> <string>Radar Site</string>
</property>
<property name="icon">
<iconset resource="../../../../scwx-qt.qrc">
<normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset>
</property> </property>
</widget> </widget>
</item> </item>
@ -798,32 +853,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="3">
<widget class="QToolButton" name="alertAudioCountySelectButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="alertAudioSoundLineEdit"/>
</item>
<item row="2" column="1" colspan="2">
<widget class="QDoubleSpinBox" name="alertAudioLatitudeSpinBox">
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>-90.000000000000000</double>
</property>
<property name="maximum">
<double>90.000000000000000</double>
</property>
<property name="singleStep">
<double>0.000100000000000</double>
</property>
</widget>
</item>
<item row="0" column="5"> <item row="0" column="5">
<widget class="QToolButton" name="alertAudioSoundStopButton"> <widget class="QToolButton" name="alertAudioSoundStopButton">
<property name="icon"> <property name="icon">
@ -832,8 +861,8 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="6"> <item row="1" column="6">
<widget class="QToolButton" name="resetAlertAudioRadiusButton"> <widget class="QToolButton" name="resetAlertAudioLocationMethodButton">
<property name="text"> <property name="text">
<string>...</string> <string>...</string>
</property> </property>
@ -843,20 +872,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Latitude</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Sound</string>
</property>
</widget>
</item>
<item row="7" column="2"> <item row="7" column="2">
<widget class="QLabel" name="alertAudioCountyLabel"> <widget class="QLabel" name="alertAudioCountyLabel">
<property name="sizePolicy"> <property name="sizePolicy">
@ -870,11 +885,25 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="4"> <item row="5" column="6">
<widget class="QToolButton" name="alertAudioSoundTestButton"> <widget class="QToolButton" name="resetAlertAudioRadiusButton">
<property name="text">
<string>...</string>
</property>
<property name="icon"> <property name="icon">
<iconset resource="../../../../scwx-qt.qrc"> <iconset resource="../../../../scwx-qt.qrc">
<normaloff>:/res/icons/font-awesome-6/play-solid.svg</normaloff>:/res/icons/font-awesome-6/play-solid.svg</iconset> <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset>
</property>
</widget>
</item>
<item row="0" column="6">
<widget class="QToolButton" name="resetAlertAudioSoundButton">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../scwx-qt.qrc">
<normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset>
</property> </property>
</widget> </widget>
</item> </item>
@ -891,66 +920,72 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Location Method</string>
</property>
</widget>
</item>
<item row="7" column="6">
<widget class="QToolButton" name="resetAlertAudioCountyButton">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../scwx-qt.qrc">
<normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="alertAudioRadiusLabel">
<property name="text">
<string>Radius</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Longitude</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_28">
<property name="text">
<string>Radar Site</string>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QToolButton" name="alertAudioRadarSiteSelectButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="4" column="6">
<widget class="QToolButton" name="resetAlertAudioRadarSiteButton">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../scwx-qt.qrc">
<normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2"> <item row="4" column="1" colspan="2">
<widget class="QComboBox" name="alertAudioRadarSiteComboBox"/> <widget class="QComboBox" name="alertAudioRadarSiteComboBox"/>
</item> </item>
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="alertAudioLocationMethodComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="6">
<widget class="QToolButton" name="resetAlertAudioLongitudeButton">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../scwx-qt.qrc">
<normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Sound</string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_29">
<property name="text">
<string>WFO</string>
</property>
</widget>
</item>
<item row="8" column="6">
<widget class="QToolButton" name="resetAlertAudioWFOButton">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../scwx-qt.qrc">
<normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLineEdit" name="alertAudioWFOLineEdit"/>
</item>
<item row="8" column="2">
<widget class="QLabel" name="alertAudioWFOLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="8" column="3">
<widget class="QToolButton" name="alertAudioWFOSelectButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View file

@ -0,0 +1,142 @@
#include "wfo_dialog.hpp"
#include "ui_wfo_dialog.h"
#include <scwx/qt/config/county_database.hpp>
#include <scwx/util/logger.hpp>
#include <QPushButton>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
namespace scwx
{
namespace qt
{
namespace ui
{
static const std::string logPrefix_ = "scwx::qt::ui::wfo_dialog";
static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
class WFODialog::Impl
{
public:
explicit Impl(WFODialog* self) :
self_ {self},
model_ {new QStandardItemModel(self)},
proxyModel_ {new QSortFilterProxyModel(self)}
{
}
~Impl() = default;
void UpdateModel();
WFODialog* self_;
QStandardItemModel* model_;
QSortFilterProxyModel* proxyModel_;
std::string selectedWFO_ {"?"};
};
WFODialog::WFODialog(QWidget* parent) :
QDialog(parent), p {std::make_unique<Impl>(this)}, ui(new Ui::WFODialog)
{
ui->setupUi(this);
p->proxyModel_->setSourceModel(p->model_);
ui->wfoView->setModel(p->proxyModel_);
ui->wfoView->setEditTriggers(
QAbstractItemView::EditTrigger::NoEditTriggers);
ui->wfoView->sortByColumn(0, Qt::SortOrder::AscendingOrder);
ui->wfoView->header()->setSectionResizeMode(
QHeaderView::ResizeMode::Stretch);
p->UpdateModel();
// Button Box
ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok)
->setEnabled(false);
connect(ui->wfoView,
&QTreeView::doubleClicked,
this,
[this]() { Q_EMIT accept(); });
connect(
ui->wfoView->selectionModel(),
&QItemSelectionModel::selectionChanged,
this,
[this](const QItemSelection& selected, const QItemSelection& deselected)
{
if (selected.size() == 0 && deselected.size() == 0)
{
// Items which stay selected but change their index are not
// included in selected and deselected. Thus, this signal might
// be emitted with both selected and deselected empty, if only
// the indices of selected items change.
return;
}
ui->buttonBox->button(QDialogButtonBox::Ok)
->setEnabled(selected.size() > 0);
if (selected.size() > 0)
{
QModelIndex selectedIndex =
p->proxyModel_->mapToSource(selected[0].indexes()[0]);
selectedIndex = p->model_->index(selectedIndex.row(), 1);
QVariant variantData = p->model_->data(selectedIndex);
if (variantData.typeId() == QMetaType::QString)
{
p->selectedWFO_ = variantData.toString().toStdString();
}
else
{
logger_->warn("Unexpected selection data type");
p->selectedWFO_ = std::string {"?"};
}
}
else
{
p->selectedWFO_ = std::string {"?"};
}
logger_->debug("Selected: {}", p->selectedWFO_);
});
}
WFODialog::~WFODialog()
{
delete ui;
}
std::string WFODialog::wfo_id()
{
return p->selectedWFO_;
}
void WFODialog::Impl::UpdateModel()
{
// Clear existing WFOs
model_->clear();
// Disable OK button
self_->ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok)
->setEnabled(false);
// Reset headers
model_->setHorizontalHeaderLabels({tr("State and City"), tr("ID")});
QStandardItem* root = model_->invisibleRootItem();
// Add each wfo to the model
for (auto& wfo : config::CountyDatabase::GetWFOs())
{
root->appendRow(
{new QStandardItem(QString::fromStdString(wfo.second)),
new QStandardItem(QString::fromStdString(wfo.first))});
}
}
} // namespace ui
} // namespace qt
} // namespace scwx

View file

@ -0,0 +1,34 @@
#pragma once
#include <QDialog>
namespace Ui
{
class WFODialog;
}
namespace scwx
{
namespace qt
{
namespace ui
{
class WFODialog : public QDialog
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(WFODialog)
public:
explicit WFODialog(QWidget* parent = nullptr);
~WFODialog();
std::string wfo_id();
private:
class Impl;
std::unique_ptr<Impl> p;
Ui::WFODialog* ui;
};
} // namespace ui
} // namespace qt
} // namespace scwx

View file

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WFODialog</class>
<widget class="QDialog" name="WFODialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>Select WFO</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeView" name="wfoView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="indentation">
<number>0</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>WFODialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>WFODialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -34,6 +34,14 @@ def ParseArguments():
nargs = "+", nargs = "+",
default = [], default = [],
type = pathlib.Path) type = pathlib.Path)
parser.add_argument("-w", "--wfo_dbf",
metavar = "filename",
help = "input wfo database",
dest = "inputWfoDbs_",
action = "extend",
nargs = "+",
default = [],
type = pathlib.Path)
parser.add_argument("-o", "--output_db", parser.add_argument("-o", "--output_db",
metavar = "filename", metavar = "filename",
help = "output sqlite database", help = "output sqlite database",
@ -49,7 +57,7 @@ def Prepare(dbInfo, outputDb):
# Establish SQLite database connection # Establish SQLite database connection
dbInfo.sqlConnection_ = sqlite3.connect(outputDb) dbInfo.sqlConnection_ = sqlite3.connect(outputDb)
# Set row factory for name-based access to columns # Set row factory for name-based access to columns
dbInfo.sqlConnection_.row_factory = sqlite3.Row dbInfo.sqlConnection_.row_factory = sqlite3.Row
@ -62,6 +70,11 @@ def Prepare(dbInfo, outputDb):
dbInfo.sqlCursor_.execute("""CREATE TABLE states( dbInfo.sqlCursor_.execute("""CREATE TABLE states(
state TEXT NOT NULL PRIMARY KEY, state TEXT NOT NULL PRIMARY KEY,
name TEXT NOT NULL)""") name TEXT NOT NULL)""")
dbInfo.sqlCursor_.execute("""CREATE TABLE wfos(
id TEXT NOT NULL PRIMARY KEY,
city TEXT NOT NULL,
state TEXT NOT NULL,
city_state TEXT NOT NULL)""")
def ProcessCountiesDbf(dbInfo, dbfFilename): def ProcessCountiesDbf(dbInfo, dbfFilename):
# County area type # County area type
@ -130,6 +143,22 @@ def ProcessZoneDbf(dbInfo, dbfFilename):
if resultRow["name"] != row.NAME: if resultRow["name"] != row.NAME:
print("Skipping duplicate zone:", fipsId, row.NAME) print("Skipping duplicate zone:", fipsId, row.NAME)
def ProcessWfoDbf(dbInfo, dbfFilename):
print("Processing WFO file:", dbfFilename)
# Read dataframe
dbfTable = gpd.read_file(filename = dbfFilename,
columns = ["FULLSTAID", "CITY", "STATE", "CITYSTATE"],
ignore_geometry = True)
dbfTable.drop_duplicates(inplace = True)
for row in dbfTable.itertuples():
try:
dbInfo.sqlCursor_.execute("INSERT INTO wfos VALUES (?, ?, ?, ?)",
(row.FULLSTAID, row.CITY, row.STATE, row.CITYSTATE))
except:
print("Error inserting WFO:", row.FULLSTAID, row.CITYSTATE)
def PostProcess(dbInfo): def PostProcess(dbInfo):
# Commit changes and close database # Commit changes and close database
dbInfo.sqlConnection_.commit() dbInfo.sqlConnection_.commit()
@ -148,4 +177,7 @@ for zoneDb in args.inputZoneDbs_:
for stateDb in args.inputStateDbs_: for stateDb in args.inputStateDbs_:
ProcessStateDbf(dbInfo, stateDb) ProcessStateDbf(dbInfo, stateDb)
for wfoDb in args.inputWfoDbs_:
ProcessWfoDbf(dbInfo, wfoDb)
PostProcess(dbInfo) PostProcess(dbInfo)

@ -1 +1 @@
Subproject commit 5115993596cfe6528b7a07c0b16dbd1ae16ce4df Subproject commit 5a91ded677d4032b0de9370ed767a16708c0ecff