From e76ac3bc368edc57482121b4ec8e7b937982b99e Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 14 Nov 2021 22:41:25 -0600 Subject: [PATCH] Selectable elevation cuts --- scwx-qt/source/scwx/qt/main/main_window.cpp | 84 ++++++++++++++++++- .../scwx/qt/manager/radar_product_manager.cpp | 13 +-- .../scwx/qt/manager/radar_product_manager.hpp | 4 +- scwx-qt/source/scwx/qt/map/map_widget.cpp | 45 +++++++--- scwx-qt/source/scwx/qt/map/map_widget.hpp | 8 +- .../scwx/qt/view/level2_product_view.cpp | 55 +++++++++--- .../scwx/qt/view/level2_product_view.hpp | 4 + .../scwx/qt/view/radar_product_view.cpp | 7 ++ .../scwx/qt/view/radar_product_view.hpp | 2 + .../qt/view/radar_product_view_factory.cpp | 6 +- .../qt/view/radar_product_view_factory.hpp | 2 + wxdata/include/scwx/common/characters.hpp | 14 ++++ wxdata/include/scwx/wsr88d/ar2v_file.hpp | 2 +- wxdata/source/scwx/wsr88d/ar2v_file.cpp | 32 +++---- wxdata/wxdata.cmake | 3 +- 15 files changed, 230 insertions(+), 51 deletions(-) create mode 100644 wxdata/include/scwx/common/characters.hpp diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index ddb2cdb7..a990dc65 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.cpp +++ b/scwx-qt/source/scwx/qt/main/main_window.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -18,16 +19,25 @@ namespace main static const std::string logPrefix_ = "[scwx::qt::main::main_window] "; -class MainWindowImpl +class MainWindowImpl : public QObject { + Q_OBJECT + public: - explicit MainWindowImpl(MainWindow* mainWindow) : mainWindow_(mainWindow) {} + explicit MainWindowImpl(MainWindow* mainWindow) : + mainWindow_ {mainWindow}, map_ {nullptr}, elevationCuts_ {} + { + } ~MainWindowImpl() = default; + void InitializeConnections(); void SelectRadarProduct(common::Level2Product product); + void UpdateRadarProductSettings(map::MapWidget* mapWidget); MainWindow* mainWindow_; map::MapWidget* map_; + + std::vector elevationCuts_; }; MainWindow::MainWindow(QWidget* parent) : @@ -62,6 +72,11 @@ MainWindow::MainWindow(QWidget* parent) : p->SelectRadarProduct(product); }); } + + QLayout* elevationLayout = new ui::FlowLayout(); + ui->elevationSettings->setLayout(elevationLayout); + + p->InitializeConnections(); } MainWindow::~MainWindow() @@ -96,6 +111,16 @@ void MainWindow::showEvent(QShowEvent* event) resizeDocks({ui->radarToolboxDock}, {150}, Qt::Horizontal); } +void MainWindowImpl::InitializeConnections() +{ + connect( + map_, + &map::MapWidget::RadarSweepUpdated, + this, + [this]() { UpdateRadarProductSettings(map_); }, + Qt::QueuedConnection); +} + void MainWindowImpl::SelectRadarProduct(common::Level2Product product) { const std::string& productName = common::GetLevel2Name(product); @@ -121,6 +146,61 @@ void MainWindowImpl::SelectRadarProduct(common::Level2Product product) map_->SelectRadarProduct(product); } +void MainWindowImpl::UpdateRadarProductSettings(map::MapWidget* mapWidget) +{ + float currentElevation = mapWidget->GetElevation(); + std::vector elevationCuts = mapWidget->GetElevationCuts(); + + if (elevationCuts_ == elevationCuts) + { + return; + } + + for (QToolButton* toolButton : + mainWindow_->ui->elevationSettings->findChildren()) + { + delete toolButton; + } + + QLayout* layout = mainWindow_->ui->elevationSettings->layout(); + + // Create elevation cut tool buttons + for (float elevationCut : elevationCuts) + { + QToolButton* toolButton = new QToolButton(); + toolButton->setText(QString::number(elevationCut, 'f', 1) + + common::Characters::DEGREE); + layout->addWidget(toolButton); + + connect(toolButton, &QToolButton::clicked, this, [=]() { + mapWidget->SelectElevation(elevationCut); + }); + } + + // Update toolbox active item to render + QToolBox* toolbox = mainWindow_->ui->radarToolbox; + int currentIndex = toolbox->currentIndex(); + toolbox->setCurrentWidget(mainWindow_->ui->productSettingsPage); + toolbox->setCurrentIndex(currentIndex); + + // Set each elevation cut's tool button to the same size + int elevationCutMaxWidth = 0; + for (QToolButton* widget : + mainWindow_->ui->elevationSettings->findChildren()) + { + elevationCutMaxWidth = std::max(elevationCutMaxWidth, widget->width()); + } + for (QToolButton* widget : + mainWindow_->ui->elevationSettings->findChildren()) + { + widget->setMinimumWidth(elevationCutMaxWidth); + } + + elevationCuts_ = elevationCuts; +} + } // namespace main } // namespace qt } // namespace scwx + +#include "main_window.moc" diff --git a/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp b/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp index c706da82..f89c29ae 100644 --- a/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp @@ -180,17 +180,20 @@ void RadarProductManager::LoadLevel2Data(const std::string& filename) emit Level2DataLoaded(); } -std::pair> +std::tuple, + float, + std::vector> RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType, float elevation, std::chrono::system_clock::time_point time) { - float elevationFound = 0.0f; - std::shared_ptr radarData = nullptr; + std::shared_ptr radarData = nullptr; + float elevationCut = 0.0f; + std::vector elevationCuts; if (p->level2VolumeScans_.size() > 0) { - std::tie(elevationFound, radarData) = + std::tie(radarData, elevationCut, elevationCuts) = p->level2VolumeScans_.crbegin()->second->GetElevationScan( dataBlockType, elevation, time); } @@ -205,7 +208,7 @@ RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType, }); } - return std::make_pair(elevationFound, radarData); + return std::tie(radarData, elevationCut, elevationCuts); } } // namespace manager diff --git a/scwx-qt/source/scwx/qt/manager/radar_product_manager.hpp b/scwx-qt/source/scwx/qt/manager/radar_product_manager.hpp index 32ad3baa..ab38abc3 100644 --- a/scwx-qt/source/scwx/qt/manager/radar_product_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/radar_product_manager.hpp @@ -33,7 +33,9 @@ public: void Initialize(); void LoadLevel2Data(const std::string& filename); - std::pair> + std::tuple, + float, + std::vector> GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType, float elevation, std::chrono::system_clock::time_point time = {}); diff --git a/scwx-qt/source/scwx/qt/map/map_widget.cpp b/scwx-qt/source/scwx/qt/map/map_widget.cpp index ab94ddd6..a84f1f1c 100644 --- a/scwx-qt/source/scwx/qt/map/map_widget.cpp +++ b/scwx-qt/source/scwx/qt/map/map_widget.cpp @@ -88,23 +88,33 @@ MapWidget::~MapWidget() makeCurrent(); } +float MapWidget::GetElevation() const +{ + return p->radarProductView_->elevation(); +} + +std::vector MapWidget::GetElevationCuts() const +{ + return p->radarProductView_->GetElevationCuts(); +} + +void MapWidget::SelectElevation(float elevation) +{ + p->radarProductView_->SelectElevation(elevation); +} + void MapWidget::SelectRadarProduct(common::Level2Product product) { - p->radarProductView_ = - view::RadarProductViewFactory::Create(product, p->radarProductManager_); + float currentElevation = 0.0f; - p->radarProductView_->Initialize(); - - std::string colorTableFile = - manager::SettingsManager::palette_settings()->palette( - common::GetLevel2Palette(product)); - if (!colorTableFile.empty()) + if (p->radarProductView_ != nullptr) { - std::shared_ptr colorTable = - common::ColorTable::Load(colorTableFile); - p->radarProductView_->LoadColorTable(colorTable); + currentElevation = p->radarProductView_->elevation(); } + p->radarProductView_ = view::RadarProductViewFactory::Create( + product, currentElevation, p->radarProductManager_); + connect( p->radarProductView_.get(), &view::RadarProductView::ColorTableUpdated, @@ -118,9 +128,22 @@ void MapWidget::SelectRadarProduct(common::Level2Product product) [&]() { RadarRangeLayer::Update(p->map_, p->radarProductView_->range()); update(); + emit RadarSweepUpdated(); }, Qt::QueuedConnection); + p->radarProductView_->Initialize(); + + std::string colorTableFile = + manager::SettingsManager::palette_settings()->palette( + common::GetLevel2Palette(product)); + if (!colorTableFile.empty()) + { + std::shared_ptr colorTable = + common::ColorTable::Load(colorTableFile); + p->radarProductView_->LoadColorTable(colorTable); + } + if (p->map_ != nullptr) { AddLayers(); diff --git a/scwx-qt/source/scwx/qt/map/map_widget.hpp b/scwx-qt/source/scwx/qt/map/map_widget.hpp index 8bcff99c..385f586a 100644 --- a/scwx-qt/source/scwx/qt/map/map_widget.hpp +++ b/scwx-qt/source/scwx/qt/map/map_widget.hpp @@ -31,7 +31,10 @@ public: explicit MapWidget(const QMapboxGLSettings&); ~MapWidget(); - void SelectRadarProduct(common::Level2Product product); + float GetElevation() const; + std::vector GetElevationCuts() const; + void SelectElevation(float elevation); + void SelectRadarProduct(common::Level2Product product); private: void changeStyle(); @@ -53,6 +56,9 @@ private: private slots: void mapChanged(QMapboxGL::MapChange); + +signals: + void RadarSweepUpdated(); }; } // namespace map diff --git a/scwx-qt/source/scwx/qt/view/level2_product_view.cpp b/scwx-qt/source/scwx/qt/view/level2_product_view.cpp index eac5c777..fe0d65c5 100644 --- a/scwx-qt/source/scwx/qt/view/level2_product_view.cpp +++ b/scwx-qt/source/scwx/qt/view/level2_product_view.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -41,13 +42,17 @@ class Level2ProductViewImpl public: explicit Level2ProductViewImpl( common::Level2Product product, + float elevation, std::shared_ptr radarProductManager) : product_ {product}, radarProductManager_ {radarProductManager}, + selectedElevation_ {elevation}, + elevationScan_ {nullptr}, momentDataBlock0_ {nullptr}, latitude_ {}, longitude_ {}, - elevation_ {}, + elevationCut_ {}, + elevationCuts_ {}, range_ {}, sweepTime_ {}, colorTable_ {}, @@ -74,6 +79,9 @@ public: wsr88d::rda::DataBlockType dataBlockType_; std::shared_ptr radarProductManager_; + float selectedElevation_; + + std::shared_ptr elevationScan_; std::shared_ptr momentDataBlock0_; std::vector vertices_; @@ -81,10 +89,11 @@ public: std::vector dataMoments16_; std::vector cfpMoments_; - float latitude_; - float longitude_; - float elevation_; - float range_; + float latitude_; + float longitude_; + float elevationCut_; + std::vector elevationCuts_; + float range_; std::chrono::system_clock::time_point sweepTime_; @@ -100,8 +109,10 @@ public: Level2ProductView::Level2ProductView( common::Level2Product product, + float elevation, std::shared_ptr radarProductManager) : - p(std::make_unique(product, radarProductManager)) + p(std::make_unique( + product, elevation, radarProductManager)) { connect(radarProductManager.get(), &manager::RadarProductManager::Level2DataLoaded, @@ -127,7 +138,7 @@ Level2ProductView::color_table(uint16_t& minValue, uint16_t& maxValue) const float Level2ProductView::elevation() const { - return p->elevation_; + return p->elevationCut_; } float Level2ProductView::range() const @@ -145,6 +156,11 @@ const std::vector& Level2ProductView::vertices() const return p->vertices_; } +std::vector Level2ProductView::GetElevationCuts() const +{ + return p->elevationCuts_; +} + std::tuple Level2ProductView::GetMomentData() const { const void* data; @@ -190,6 +206,12 @@ void Level2ProductView::LoadColorTable( UpdateColorTable(); } +void Level2ProductView::SelectElevation(float elevation) +{ + p->selectedElevation_ = elevation; + util::async([=]() { ComputeSweep(); }); +} + void Level2ProductView::UpdateColorTable() { if (p->momentDataBlock0_ == nullptr || // @@ -246,6 +268,7 @@ void Level2ProductView::UpdateColorTable() std::vector& lut = p->colorTableLut_; lut.resize(rangeMax - rangeMin + 1); + lut.shrink_to_fit(); std::for_each(std::execution::par_unseq, dataRange.begin(), @@ -283,12 +306,12 @@ void Level2ProductView::ComputeSweep() return; } - // TODO: Pick this based on view settings float elevation; std::shared_ptr radarData; - std::tie(elevation, radarData) = - p->radarProductManager_->GetLevel2Data(p->dataBlockType_, 0.0f); - if (radarData == nullptr) + std::tie(radarData, p->elevationCut_, p->elevationCuts_) = + p->radarProductManager_->GetLevel2Data(p->dataBlockType_, + p->selectedElevation_); + if (radarData == nullptr || radarData == p->elevationScan_) { return; } @@ -303,6 +326,7 @@ void Level2ProductView::ComputeSweep() auto radarData0 = (*radarData)[0]; auto momentData0 = radarData0->moment_data_block(p->dataBlockType_); + p->elevationScan_ = radarData; p->momentDataBlock0_ = momentData0; if (momentData0 == nullptr) @@ -318,7 +342,6 @@ void Level2ProductView::ComputeSweep() auto volumeData0 = radarData0->volume_data_block(); p->latitude_ = volumeData0->latitude(); p->longitude_ = volumeData0->longitude(); - p->elevation_ = elevation; p->range_ = momentData0->data_moment_range() + momentData0->data_moment_range_sample_interval() * (gates - 0.5f); @@ -533,19 +556,23 @@ void Level2ProductView::ComputeSweep() } } vertices.resize(vIndex); + vertices.shrink_to_fit(); if (momentData0->data_word_size() == 8) { dataMoments8.resize(mIndex); + dataMoments8.shrink_to_fit(); } else { dataMoments16.resize(mIndex); + dataMoments16.shrink_to_fit(); } if (cfpMoments.size() > 0) { cfpMoments.resize(mIndex); + cfpMoments.shrink_to_fit(); } timer.stop(); @@ -559,9 +586,11 @@ void Level2ProductView::ComputeSweep() std::shared_ptr Level2ProductView::Create( common::Level2Product product, + float elevation, std::shared_ptr radarProductManager) { - return std::make_shared(product, radarProductManager); + return std::make_shared( + product, elevation, radarProductManager); } } // namespace view diff --git a/scwx-qt/source/scwx/qt/view/level2_product_view.hpp b/scwx-qt/source/scwx/qt/view/level2_product_view.hpp index 606511dd..1f47f38b 100644 --- a/scwx-qt/source/scwx/qt/view/level2_product_view.hpp +++ b/scwx-qt/source/scwx/qt/view/level2_product_view.hpp @@ -25,6 +25,7 @@ class Level2ProductView : public RadarProductView public: explicit Level2ProductView( common::Level2Product product, + float elevation, std::shared_ptr radarProductManager); ~Level2ProductView(); @@ -36,12 +37,15 @@ public: const std::vector& vertices() const override; void LoadColorTable(std::shared_ptr colorTable) override; + void SelectElevation(float elevation) override; + std::vector GetElevationCuts() const override; std::tuple GetMomentData() const override; std::tuple GetCfpMomentData() const override; static std::shared_ptr Create(common::Level2Product product, + float elevation, std::shared_ptr radarProductManager); protected: diff --git a/scwx-qt/source/scwx/qt/view/radar_product_view.cpp b/scwx-qt/source/scwx/qt/view/radar_product_view.cpp index ad17f137..a89b4ae7 100644 --- a/scwx-qt/source/scwx/qt/view/radar_product_view.cpp +++ b/scwx-qt/source/scwx/qt/view/radar_product_view.cpp @@ -60,6 +60,13 @@ void RadarProductView::Initialize() ComputeSweep(); } +void RadarProductView::SelectElevation(float elevation) {} + +std::vector RadarProductView::GetElevationCuts() const +{ + return {}; +} + std::tuple RadarProductView::GetCfpMomentData() const { diff --git a/scwx-qt/source/scwx/qt/view/radar_product_view.hpp b/scwx-qt/source/scwx/qt/view/radar_product_view.hpp index 0c375860..7c8155e8 100644 --- a/scwx-qt/source/scwx/qt/view/radar_product_view.hpp +++ b/scwx-qt/source/scwx/qt/view/radar_product_view.hpp @@ -35,7 +35,9 @@ public: void Initialize(); virtual void LoadColorTable(std::shared_ptr colorTable) = 0; + virtual void SelectElevation(float elevation); + virtual std::vector GetElevationCuts() const; virtual std::tuple GetMomentData() const = 0; virtual std::tuple GetCfpMomentData() const; diff --git a/scwx-qt/source/scwx/qt/view/radar_product_view_factory.cpp b/scwx-qt/source/scwx/qt/view/radar_product_view_factory.cpp index 437e0a0a..cabd2ace 100644 --- a/scwx-qt/source/scwx/qt/view/radar_product_view_factory.cpp +++ b/scwx-qt/source/scwx/qt/view/radar_product_view_factory.cpp @@ -21,6 +21,7 @@ typedef std::function( std::shared_ptr RadarProductViewFactory::Create( const std::string& productGroup, const std::string& productName, + float elevation, std::shared_ptr radarProductManager) { std::shared_ptr view = nullptr; @@ -36,7 +37,7 @@ std::shared_ptr RadarProductViewFactory::Create( } else { - view = Create(product, radarProductManager); + view = Create(product, elevation, radarProductManager); } } else @@ -50,9 +51,10 @@ std::shared_ptr RadarProductViewFactory::Create( std::shared_ptr RadarProductViewFactory::Create( common::Level2Product product, + float elevation, std::shared_ptr radarProductManager) { - return Level2ProductView::Create(product, radarProductManager); + return Level2ProductView::Create(product, elevation, radarProductManager); } } // namespace view diff --git a/scwx-qt/source/scwx/qt/view/radar_product_view_factory.hpp b/scwx-qt/source/scwx/qt/view/radar_product_view_factory.hpp index b00dd585..aa85000d 100644 --- a/scwx-qt/source/scwx/qt/view/radar_product_view_factory.hpp +++ b/scwx-qt/source/scwx/qt/view/radar_product_view_factory.hpp @@ -30,9 +30,11 @@ public: static std::shared_ptr Create(const std::string& productGroup, const std::string& productName, + float elevation, std::shared_ptr radarProductManager); static std::shared_ptr Create(common::Level2Product product, + float elevation, std::shared_ptr radarProductManager); }; diff --git a/wxdata/include/scwx/common/characters.hpp b/wxdata/include/scwx/common/characters.hpp new file mode 100644 index 00000000..fd73462d --- /dev/null +++ b/wxdata/include/scwx/common/characters.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace scwx +{ +namespace common +{ +namespace Characters +{ + +constexpr char DEGREE = static_cast(0xb0); + +} // namespace Characters +} // namespace common +} // namespace scwx diff --git a/wxdata/include/scwx/wsr88d/ar2v_file.hpp b/wxdata/include/scwx/wsr88d/ar2v_file.hpp index a6d8a9ca..8ebeec4b 100644 --- a/wxdata/include/scwx/wsr88d/ar2v_file.hpp +++ b/wxdata/include/scwx/wsr88d/ar2v_file.hpp @@ -40,7 +40,7 @@ public: std::map> radar_data() const; std::shared_ptr vcp_data() const; - std::pair> + std::tuple, float, std::vector> GetElevationScan(rda::DataBlockType dataBlockType, float elevation, std::chrono::system_clock::time_point time) const; diff --git a/wxdata/source/scwx/wsr88d/ar2v_file.cpp b/wxdata/source/scwx/wsr88d/ar2v_file.cpp index 8133f0bb..43f0ac80 100644 --- a/wxdata/source/scwx/wsr88d/ar2v_file.cpp +++ b/wxdata/source/scwx/wsr88d/ar2v_file.cpp @@ -106,15 +106,19 @@ std::shared_ptr Ar2vFile::vcp_data() const return p->vcpData_; } -std::pair> +std::tuple, float, std::vector> Ar2vFile::GetElevationScan(rda::DataBlockType dataBlockType, float elevation, std::chrono::system_clock::time_point time) const { + BOOST_LOG_TRIVIAL(debug) + << logPrefix_ << "GetElevationScan: " << elevation << " degrees"; + constexpr float scaleFactor = 8.0f / 0.043945f; - float elevationFound = 0.0f; - std::shared_ptr elevationScan = nullptr; + std::shared_ptr elevationScan = nullptr; + float elevationCut = 0.0f; + std::vector elevationCuts; uint16_t codedElevation = static_cast(std::lroundf(elevation * scaleFactor)); @@ -128,14 +132,16 @@ Ar2vFile::GetElevationScan(rda::DataBlockType dataBlockType, for (auto scan : scans) { - if (scan.first > lowerBound && scan.first < codedElevation) + if (scan.first > lowerBound && scan.first <= codedElevation) { lowerBound = scan.first; } - if (scan.first < upperBound && scan.first > codedElevation) + if (scan.first < upperBound && scan.first >= codedElevation) { upperBound = scan.first; } + + elevationCuts.push_back(scan.first / scaleFactor); } uint16_t lowerDelta = std::abs(static_cast(codedElevation) - @@ -145,17 +151,17 @@ Ar2vFile::GetElevationScan(rda::DataBlockType dataBlockType, if (lowerDelta < upperDelta) { - elevationFound = lowerBound / scaleFactor; - elevationScan = scans.at(lowerBound); + elevationScan = scans.at(lowerBound); + elevationCut = lowerBound / scaleFactor; } else { - elevationFound = upperBound / scaleFactor; - elevationScan = scans.at(upperBound); + elevationScan = scans.at(upperBound); + elevationCut = upperBound / scaleFactor; } } - return std::make_pair(elevationFound, elevationScan); + return std::tie(elevationScan, elevationCut, elevationCuts); } bool Ar2vFile::LoadFile(const std::string& filename) @@ -373,10 +379,8 @@ void Ar2vFileImpl::IndexFile() rda::MomentDataBlockTypeIterator()) { if (dataBlockType == rda::DataBlockType::MomentRef && - (waveformType == - rda::WaveformType::ContiguousDopplerWithAmbiguityResolution || - waveformType == rda::WaveformType:: - ContiguousDopplerWithoutAmbiguityResolution)) + waveformType == + rda::WaveformType::ContiguousDopplerWithAmbiguityResolution) { // Reflectivity data is contained within both surveillance and // doppler modes. Surveillance mode produces a better image. diff --git a/wxdata/wxdata.cmake b/wxdata/wxdata.cmake index 67567b35..5ccbc097 100644 --- a/wxdata/wxdata.cmake +++ b/wxdata/wxdata.cmake @@ -2,7 +2,8 @@ project(scwx-data) find_package(Boost) -set(HDR_COMMON include/scwx/common/color_table.hpp +set(HDR_COMMON include/scwx/common/characters.hpp + include/scwx/common/color_table.hpp include/scwx/common/constants.hpp include/scwx/common/products.hpp include/scwx/common/types.hpp)