diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index 651f454e..8fbf23bc 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -98,10 +98,12 @@ set(HDR_TYPES source/scwx/qt/types/radar_product_record.hpp) set(SRC_TYPES source/scwx/qt/types/radar_product_record.cpp) set(HDR_UI source/scwx/qt/ui/flow_layout.hpp source/scwx/qt/ui/level2_products_widget.hpp - source/scwx/qt/ui/level2_settings_widget.hpp) + source/scwx/qt/ui/level2_settings_widget.hpp + source/scwx/qt/ui/level3_products_widget.hpp) set(SRC_UI source/scwx/qt/ui/flow_layout.cpp source/scwx/qt/ui/level2_products_widget.cpp - source/scwx/qt/ui/level2_settings_widget.cpp) + source/scwx/qt/ui/level2_settings_widget.cpp + source/scwx/qt/ui/level3_products_widget.cpp) set(HDR_UTIL source/scwx/qt/util/font.hpp source/scwx/qt/util/font_buffer.hpp source/scwx/qt/util/json.hpp) diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index 6a0356a1..f1d27568 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.cpp +++ b/scwx-qt/source/scwx/qt/main/main_window.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -93,6 +94,8 @@ public: ui::Level2ProductsWidget* level2ProductsWidget_; ui::Level2SettingsWidget* level2SettingsWidget_; + ui::Level3ProductsWidget* level3ProductsWidget_; + std::vector maps_; std::vector elevationCuts_; @@ -127,6 +130,13 @@ MainWindow::MainWindow(QWidget* parent) : delete ui->level2ProductFrame; ui->level2ProductFrame = p->level2ProductsWidget_; + // Add Level 3 Products + p->level3ProductsWidget_ = new ui::Level3ProductsWidget(this); + ui->radarProductGroupBox->layout()->replaceWidget(ui->level3ProductFrame, + p->level3ProductsWidget_); + delete ui->level3ProductFrame; + ui->level3ProductFrame = p->level3ProductsWidget_; + // Add Level 2 Settings p->level2SettingsWidget_ = new ui::Level2SettingsWidget(ui->settingsFrame); ui->settingsFrame->layout()->addWidget(p->level2SettingsWidget_); @@ -169,6 +179,15 @@ MainWindow::MainWindow(QWidget* parent) : p->SelectRadarProduct( p->activeMap_, group, productName, productCode); }); + connect(p->level3ProductsWidget_, + &ui::Level3ProductsWidget::RadarProductSelected, + this, + [&](common::RadarProductGroup group, + const std::string& productName, + int16_t productCode) { + p->SelectRadarProduct( + p->activeMap_, group, productName, productCode); + }); connect(p->level2SettingsWidget_, &ui::Level2SettingsWidget::ElevationSelected, this, @@ -409,6 +428,7 @@ void MainWindowImpl::UpdateRadarProductSelection( common::RadarProductGroup group, const std::string& product) { level2ProductsWidget_->UpdateProductSelection(group, product); + level3ProductsWidget_->UpdateProductSelection(group, product); } void MainWindowImpl::UpdateRadarProductSettings() diff --git a/scwx-qt/source/scwx/qt/main/main_window.ui b/scwx-qt/source/scwx/qt/main/main_window.ui index 6359edc9..56ee6e30 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.ui +++ b/scwx-qt/source/scwx/qt/main/main_window.ui @@ -178,14 +178,7 @@ - - - QFrame::StyledPanel - - - QFrame::Raised - - + diff --git a/scwx-qt/source/scwx/qt/ui/level3_products_widget.cpp b/scwx-qt/source/scwx/qt/ui/level3_products_widget.cpp new file mode 100644 index 00000000..5f72aa43 --- /dev/null +++ b/scwx-qt/source/scwx/qt/ui/level3_products_widget.cpp @@ -0,0 +1,150 @@ +#include +#include +#include + +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace ui +{ + +static const std::string logPrefix_ = "scwx::qt::ui::level3_products_widget"; +static const auto logger_ = util::Logger::Create(logPrefix_); + +class Level3ProductsWidgetImpl : public QObject +{ + Q_OBJECT + +public: + explicit Level3ProductsWidgetImpl(Level3ProductsWidget* self) : + self_ {self}, layout_ {new ui::FlowLayout(self)}, categoryButtons_ {} + { + layout_->setContentsMargins(0, 0, 0, 0); + + for (common::Level3ProductCategory category : + common::Level3ProductCategoryIterator()) + { + QToolButton* toolButton = new QToolButton(); + toolButton->setText( + QString::fromStdString(common::GetLevel3CategoryName(category))); + toolButton->setStatusTip( + tr(common::GetLevel3CategoryDescription(category).c_str())); + toolButton->setPopupMode(QToolButton::MenuButtonPopup); + layout_->addWidget(toolButton); + categoryButtons_.push_back(toolButton); + + QObject::connect(toolButton, + &QToolButton::clicked, + this, + [=]() { SelectProductCategory(category); }); + } + } + ~Level3ProductsWidgetImpl() = default; + + void NormalizeProductButtons(); + void SelectProductCategory(common::Level3ProductCategory category); + void UpdateProductSelection(common::Level3ProductCategory category); + + Level3ProductsWidget* self_; + QLayout* layout_; + std::list categoryButtons_; +}; + +Level3ProductsWidget::Level3ProductsWidget(QWidget* parent) : + QWidget(parent), p {std::make_shared(this)} +{ +} + +Level3ProductsWidget::~Level3ProductsWidget() = default; + +void Level3ProductsWidget::showEvent(QShowEvent* event) +{ + QWidget::showEvent(event); + + p->NormalizeProductButtons(); +} + +void Level3ProductsWidgetImpl::NormalizeProductButtons() +{ + int level3MaxWidth = 0; + + // Set each level 2 product's tool button to the same size + std::for_each(categoryButtons_.cbegin(), + categoryButtons_.cend(), + [&](auto& toolButton) + { + if (toolButton->isVisible()) + { + level3MaxWidth = + std::max(level3MaxWidth, toolButton->width()); + } + }); + + if (level3MaxWidth > 0) + { + std::for_each(categoryButtons_.cbegin(), + categoryButtons_.cend(), + [&](auto& toolButton) + { toolButton->setMinimumWidth(level3MaxWidth); }); + } +} + +void Level3ProductsWidgetImpl::SelectProductCategory( + common::Level3ProductCategory category) +{ + UpdateProductSelection(category); + + emit self_->RadarProductSelected( + common::RadarProductGroup::Level3, + common::GetLevel3CategoryDefaultProduct(category), + 0); +} + +void Level3ProductsWidget::UpdateProductSelection( + common::RadarProductGroup group, const std::string& productName) +{ + if (group == common::RadarProductGroup::Level3) + { + common::Level3ProductCategory category = + common::GetLevel3CategoryByProduct(productName); + p->UpdateProductSelection(category); + } + else + { + p->UpdateProductSelection(common::Level3ProductCategory::Unknown); + } +} + +void Level3ProductsWidgetImpl::UpdateProductSelection( + common::Level3ProductCategory category) +{ + const std::string& categoryName = common::GetLevel3CategoryName(category); + + std::for_each(std::execution::par_unseq, + categoryButtons_.cbegin(), + categoryButtons_.cend(), + [&](auto& toolButton) + { + if (toolButton->text().toStdString() == categoryName) + { + toolButton->setCheckable(true); + toolButton->setChecked(true); + } + else + { + toolButton->setChecked(false); + toolButton->setCheckable(false); + } + }); +} + +} // namespace ui +} // namespace qt +} // namespace scwx + +#include "level3_products_widget.moc" diff --git a/scwx-qt/source/scwx/qt/ui/level3_products_widget.hpp b/scwx-qt/source/scwx/qt/ui/level3_products_widget.hpp new file mode 100644 index 00000000..591c0e6a --- /dev/null +++ b/scwx-qt/source/scwx/qt/ui/level3_products_widget.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace ui +{ + +class Level3ProductsWidgetImpl; + +class Level3ProductsWidget : public QWidget +{ + Q_OBJECT + +public: + explicit Level3ProductsWidget(QWidget* parent = nullptr); + ~Level3ProductsWidget(); + + void showEvent(QShowEvent* event) override; + + void UpdateProductSelection(common::RadarProductGroup group, + const std::string& productName); + +signals: + void RadarProductSelected(common::RadarProductGroup group, + const std::string& productName, + int16_t productCode); + +private: + std::shared_ptr p; +}; + +} // namespace ui +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/ts/scwx_en_US.ts b/scwx-qt/ts/scwx_en_US.ts index 12fba785..a5ac2368 100644 --- a/scwx-qt/ts/scwx_en_US.ts +++ b/scwx-qt/ts/scwx_en_US.ts @@ -64,22 +64,22 @@ - + E&xit - + About &Supercell Wx... - + &Open... - + Ctrl+O @@ -87,7 +87,7 @@ scwx::qt::main::MainWindow - + Unrecognized NEXRAD Product: diff --git a/wxdata/include/scwx/common/products.hpp b/wxdata/include/scwx/common/products.hpp index e228e80e..669a31d0 100644 --- a/wxdata/include/scwx/common/products.hpp +++ b/wxdata/include/scwx/common/products.hpp @@ -36,6 +36,22 @@ typedef util::Iterator Level2ProductIterator; +enum class Level3ProductCategory +{ + Reflectivity, + Velocity, + StormRelativeVelocity, + SpectrumWidth, + DifferentialReflectivity, + SpecificDifferentialPhase, + CorrelationCoefficient, + Unknown +}; +typedef util::Iterator + Level3ProductCategoryIterator; + const std::string& GetRadarProductGroupName(RadarProductGroup group); RadarProductGroup GetRadarProductGroup(const std::string& name); @@ -44,6 +60,13 @@ const std::string& GetLevel2Description(Level2Product product); const std::string& GetLevel2Palette(Level2Product product); Level2Product GetLevel2Product(const std::string& name); +const std::string& GetLevel3CategoryName(Level3ProductCategory category); +const std::string& GetLevel3CategoryDescription(Level3ProductCategory category); +const std::string& +GetLevel3CategoryDefaultProduct(Level3ProductCategory category); +Level3ProductCategory GetLevel3Category(const std::string& categoryName); +Level3ProductCategory + GetLevel3CategoryByProduct(const std::string& productName); const std::string& GetLevel3Palette(int16_t productCode); } // namespace common diff --git a/wxdata/source/scwx/common/products.cpp b/wxdata/source/scwx/common/products.cpp index 57cee160..fb916e8a 100644 --- a/wxdata/source/scwx/common/products.cpp +++ b/wxdata/source/scwx/common/products.cpp @@ -42,6 +42,41 @@ static const std::unordered_map level2Palette_ { {Level2Product::ClutterFilterPowerRemoved, "???"}, {Level2Product::Unknown, "???"}}; +static const std::unordered_map + level3CategoryName_ { + {Level3ProductCategory::Reflectivity, "REF"}, + {Level3ProductCategory::Velocity, "VEL"}, + {Level3ProductCategory::StormRelativeVelocity, "SRM"}, + {Level3ProductCategory::SpectrumWidth, "SW"}, + {Level3ProductCategory::DifferentialReflectivity, "ZDR"}, + {Level3ProductCategory::SpecificDifferentialPhase, "KDP"}, + {Level3ProductCategory::CorrelationCoefficient, "CC"}, + {Level3ProductCategory::Unknown, "?"}}; + +static const std::unordered_map + level3CategoryDescription_ { + {Level3ProductCategory::Reflectivity, "Reflectivity"}, + {Level3ProductCategory::Velocity, "Velocity"}, + {Level3ProductCategory::StormRelativeVelocity, "Storm Relative Velocity"}, + {Level3ProductCategory::SpectrumWidth, "Spectrum Width"}, + {Level3ProductCategory::DifferentialReflectivity, + "Differential Reflectivity"}, + {Level3ProductCategory::SpecificDifferentialPhase, + "Specific Differential Phase"}, + {Level3ProductCategory::CorrelationCoefficient, + "Correlation Coefficient"}, + {Level3ProductCategory::Unknown, "?"}}; + +static const std::unordered_map + level3CategoryDefaultProduct_ { + {Level3ProductCategory::Reflectivity, "N0B"}, + {Level3ProductCategory::Velocity, "N0G"}, + {Level3ProductCategory::StormRelativeVelocity, "N0S"}, + {Level3ProductCategory::SpectrumWidth, "NSW"}, + {Level3ProductCategory::DifferentialReflectivity, "N0X"}, + {Level3ProductCategory::SpecificDifferentialPhase, "N0K"}, + {Level3ProductCategory::CorrelationCoefficient, "N0C"}}; + static const std::unordered_map level3Palette_ { {19, "BR"}, {20, "BR"}, {27, "BV"}, {30, "SW"}, {31, "STPIN"}, {32, "BR"}, {37, "BR"}, {38, "BR"}, @@ -99,19 +134,6 @@ const std::string& GetLevel2Palette(Level2Product product) return level2Palette_.at(product); } -const std::string& GetLevel3Palette(int16_t productCode) -{ - auto it = level3Palette_.find(productCode); - if (it != level3Palette_.cend()) - { - return it->second; - } - else - { - return level3Palette_.at(-1); - } -} - Level2Product GetLevel2Product(const std::string& name) { auto result = std::find_if( @@ -130,5 +152,70 @@ Level2Product GetLevel2Product(const std::string& name) } } +const std::string& GetLevel3CategoryName(Level3ProductCategory category) +{ + return level3CategoryName_.at(category); +} + +const std::string& GetLevel3CategoryDescription(Level3ProductCategory category) +{ + return level3CategoryDescription_.at(category); +} + +const std::string& +GetLevel3CategoryDefaultProduct(Level3ProductCategory category) +{ + return level3CategoryDefaultProduct_.at(category); +} + +Level3ProductCategory GetLevel3Category(const std::string& categoryName) +{ + auto result = std::find_if( + level3CategoryName_.cbegin(), + level3CategoryName_.cend(), + [&](const std::pair& pair) -> bool + { return pair.second == categoryName; }); + + if (result != level3CategoryName_.cend()) + { + return result->first; + } + else + { + return Level3ProductCategory::Unknown; + } +} + +Level3ProductCategory GetLevel3CategoryByProduct(const std::string& productName) +{ + auto result = std::find_if( + level3CategoryDefaultProduct_.cbegin(), + level3CategoryDefaultProduct_.cend(), + [&](const std::pair& pair) -> bool + { return pair.second == productName; }); + + if (result != level3CategoryDefaultProduct_.cend()) + { + return result->first; + } + else + { + return Level3ProductCategory::Unknown; + } +} + +const std::string& GetLevel3Palette(int16_t productCode) +{ + auto it = level3Palette_.find(productCode); + if (it != level3Palette_.cend()) + { + return it->second; + } + else + { + return level3Palette_.at(-1); + } +} + } // namespace common } // namespace scwx