From 9ee52e7a6b25b08abe3256cde59fdb7412a27ea2 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 31 Oct 2021 22:09:06 -0500 Subject: [PATCH] Deriving level 2 product view --- model/scwx.notation | 22 +- model/scwx.uml | 7 +- model/scwx_en_US.properties | 2 +- scwx-qt/scwx-qt.cmake | 8 +- scwx-qt/source/scwx/qt/map/map_widget.cpp | 4 +- scwx-qt/source/scwx/qt/map/overlay_layer.cpp | 2 +- .../scwx/qt/view/level2_product_view.cpp | 404 ++++++++++++++++++ .../scwx/qt/view/level2_product_view.hpp | 59 +++ .../scwx/qt/view/radar_product_view.cpp | 341 +-------------- .../scwx/qt/view/radar_product_view.hpp | 20 +- .../qt/view/radar_product_view_factory.cpp | 51 +++ .../qt/view/radar_product_view_factory.hpp | 36 ++ 12 files changed, 602 insertions(+), 354 deletions(-) create mode 100644 scwx-qt/source/scwx/qt/view/level2_product_view.cpp create mode 100644 scwx-qt/source/scwx/qt/view/level2_product_view.hpp create mode 100644 scwx-qt/source/scwx/qt/view/radar_product_view_factory.cpp create mode 100644 scwx-qt/source/scwx/qt/view/radar_product_view_factory.hpp diff --git a/model/scwx.notation b/model/scwx.notation index 2df9a8b6..78ce3a78 100644 --- a/model/scwx.notation +++ b/model/scwx.notation @@ -62,29 +62,29 @@ - - - - + + + + + + + + - - - - - - - + + + diff --git a/model/scwx.uml b/model/scwx.uml index 5d776c04..7ea7a3e4 100644 --- a/model/scwx.uml +++ b/model/scwx.uml @@ -81,7 +81,7 @@ - + @@ -131,4 +131,9 @@ + + + + + diff --git a/model/scwx_en_US.properties b/model/scwx_en_US.properties index 85333df0..ce527843 100644 --- a/model/scwx_en_US.properties +++ b/model/scwx_en_US.properties @@ -1,2 +1,2 @@ -#Sun Oct 31 15:18:03 CDT 2021 +#Sun Oct 31 22:03:12 CDT 2021 _label_asdf=asdf2 diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index 0f226403..9fad529c 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -82,8 +82,12 @@ set(HDR_UTIL source/scwx/qt/util/font.hpp set(SRC_UTIL source/scwx/qt/util/font.cpp source/scwx/qt/util/font_buffer.cpp source/scwx/qt/util/json.cpp) -set(HDR_VIEW source/scwx/qt/view/radar_product_view.hpp) -set(SRC_VIEW source/scwx/qt/view/radar_product_view.cpp) +set(HDR_VIEW source/scwx/qt/view/level2_product_view.hpp + source/scwx/qt/view/radar_product_view.hpp + source/scwx/qt/view/radar_product_view_factory.hpp) +set(SRC_VIEW source/scwx/qt/view/level2_product_view.cpp + source/scwx/qt/view/radar_product_view.cpp + source/scwx/qt/view/radar_product_view_factory.cpp) set(RESOURCE_FILES scwx-qt.qrc) diff --git a/scwx-qt/source/scwx/qt/map/map_widget.cpp b/scwx-qt/source/scwx/qt/map/map_widget.cpp index 78f081fe..51549933 100644 --- a/scwx-qt/source/scwx/qt/map/map_widget.cpp +++ b/scwx-qt/source/scwx/qt/map/map_widget.cpp @@ -1,8 +1,10 @@ #include #include +#include #include #include #include +#include #include #include @@ -104,7 +106,7 @@ void MapWidget::changeStyle() void MapWidget::AddLayers() { std::shared_ptr radarProductView = - std::make_shared(p->radarProductManager_); + view::RadarProductViewFactory::Create("L2REF", p->radarProductManager_); radarProductView->Initialize(); diff --git a/scwx-qt/source/scwx/qt/map/overlay_layer.cpp b/scwx-qt/source/scwx/qt/map/overlay_layer.cpp index 874c120d..65d8e0a9 100644 --- a/scwx-qt/source/scwx/qt/map/overlay_layer.cpp +++ b/scwx-qt/source/scwx/qt/map/overlay_layer.cpp @@ -137,7 +137,7 @@ void OverlayLayer::render(const QMapbox::CustomLayerRenderParameters& params) { using namespace std::chrono; auto sweepTime = - time_point_cast(p->radarProductView_->SweepTime()); + time_point_cast(p->radarProductView_->sweep_time()); if (sweepTime.time_since_epoch().count() != 0) { diff --git a/scwx-qt/source/scwx/qt/view/level2_product_view.cpp b/scwx-qt/source/scwx/qt/view/level2_product_view.cpp new file mode 100644 index 00000000..aa6d6b4f --- /dev/null +++ b/scwx-qt/source/scwx/qt/view/level2_product_view.cpp @@ -0,0 +1,404 @@ +#include +#include + +#include +#include +#include + +namespace scwx +{ +namespace qt +{ +namespace view +{ + +static const std::string logPrefix_ = "[scwx::qt::view::level2_product_view] "; + +static constexpr uint32_t VERTICES_PER_BIN = 6; +static constexpr uint32_t VALUES_PER_VERTEX = 2; + +static const std::unordered_map + blockTypes_ {{PRODUCT_L2_REF, wsr88d::rda::DataBlockType::MomentRef}, + {PRODUCT_L2_VEL, wsr88d::rda::DataBlockType::MomentVel}, + {PRODUCT_L2_SW, wsr88d::rda::DataBlockType::MomentSw}, + {PRODUCT_L2_ZDR, wsr88d::rda::DataBlockType::MomentZdr}, + {PRODUCT_L2_PHI, wsr88d::rda::DataBlockType::MomentPhi}, + {PRODUCT_L2_RHO, wsr88d::rda::DataBlockType::MomentRho}, + {PRODUCT_L2_CFP, wsr88d::rda::DataBlockType::MomentCfp}}; + +static std::chrono::system_clock::time_point +TimePoint(uint16_t modifiedJulianDate, uint32_t milliseconds); + +class Level2ProductViewImpl +{ +public: + explicit Level2ProductViewImpl( + const std::string& productName, + std::shared_ptr radarProductManager) : + radarProductManager_ {radarProductManager}, sweepTime_ {}, colorTable_ {} + { + auto it = blockTypes_.find(productName); + + if (it != blockTypes_.end()) + { + dataBlockType_ = it->second; + } + else + { + BOOST_LOG_TRIVIAL(warning) + << logPrefix_ << "Unknown product: \"" << productName << "\""; + dataBlockType_ = wsr88d::rda::DataBlockType::Unknown; + } + } + ~Level2ProductViewImpl() = default; + + wsr88d::rda::DataBlockType dataBlockType_; + std::shared_ptr radarProductManager_; + + std::vector vertices_; + std::vector dataMoments8_; + std::vector dataMoments16_; + + std::chrono::system_clock::time_point sweepTime_; + + std::vector colorTable_; +}; + +Level2ProductView::Level2ProductView( + const std::string& productName, + std::shared_ptr radarProductManager) : + p(std::make_unique(productName, radarProductManager)) +{ + connect(radarProductManager.get(), + &manager::RadarProductManager::Level2DataLoaded, + this, + &Level2ProductView::ComputeSweep); +} +Level2ProductView::~Level2ProductView() = default; + +const std::vector& +Level2ProductView::color_table() const +{ + if (p->colorTable_.size() == 0) + { + return RadarProductView::color_table(); + } + else + { + return p->colorTable_; + } +} + +std::chrono::system_clock::time_point Level2ProductView::sweep_time() const +{ + return p->sweepTime_; +} + +const std::vector& Level2ProductView::vertices() const +{ + return p->vertices_; +} + +std::tuple Level2ProductView::GetMomentData() const +{ + const void* data; + size_t dataSize; + size_t componentSize; + + if (p->dataMoments8_.size() > 0) + { + data = p->dataMoments8_.data(); + dataSize = p->dataMoments8_.size() * sizeof(uint8_t); + componentSize = 1; + } + else + { + data = p->dataMoments16_.data(); + dataSize = p->dataMoments16_.size() * sizeof(uint16_t); + componentSize = 2; + } + + return std::tie(data, dataSize, componentSize); +} + +void Level2ProductView::LoadColorTable( + std::shared_ptr colorTable) +{ + // TODO: Make size, offset and scale dynamic + const float offset = 66.0f; + const float scale = 2.0f; + + std::vector& lut = p->colorTable_; + lut.resize(254); + + auto dataRange = boost::irange(2, 255); + + std::for_each(std::execution::par_unseq, + dataRange.begin(), + dataRange.end(), + [&](uint16_t i) { + float f = (i - offset) / scale; + lut[i - *dataRange.begin()] = colorTable->Color(f); + }); + + emit ColorTableLoaded(); +} + +void Level2ProductView::ComputeSweep() +{ + BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "ComputeSweep()"; + + boost::timer::cpu_timer timer; + + if (p->dataBlockType_ == wsr88d::rda::DataBlockType::Unknown) + { + return; + } + + std::shared_ptr level2Data = + p->radarProductManager_->level2_data(); + if (level2Data == nullptr) + { + return; + } + + // TODO: Pick this based on radar data + const std::vector& coordinates = + p->radarProductManager_->coordinates(common::RadialSize::_0_5Degree); + + // TODO: Pick this based on view settings + auto radarData = level2Data->radar_data()[0]; + + p->sweepTime_ = TimePoint(radarData[0]->modified_julian_date(), + radarData[0]->collection_time()); + + // Calculate vertices + timer.start(); + + auto momentData0 = radarData[0]->moment_data_block(p->dataBlockType_); + + // Setup vertex vector + std::vector& vertices = p->vertices_; + const size_t radials = radarData.size(); + const uint32_t gates = momentData0->number_of_data_moment_gates(); + size_t vIndex = 0; + vertices.clear(); + vertices.resize(radials * gates * VERTICES_PER_BIN * VALUES_PER_VERTEX); + + // Setup data moment vector + std::vector& dataMoments8 = p->dataMoments8_; + std::vector& dataMoments16 = p->dataMoments16_; + size_t mIndex = 0; + + if (momentData0->data_word_size() == 8) + { + dataMoments16.resize(0); + dataMoments16.shrink_to_fit(); + + dataMoments8.resize(radials * gates * VERTICES_PER_BIN); + } + else + { + dataMoments8.resize(0); + dataMoments8.shrink_to_fit(); + + dataMoments16.resize(radials * gates * VERTICES_PER_BIN); + } + + // Compute threshold at which to display an individual bin + const float scale = momentData0->scale(); + const float offset = momentData0->offset(); + const uint16_t snrThreshold = + std::lroundf(momentData0->snr_threshold_raw() * scale / 10 + offset); + + // Azimuth resolution spacing: + // 1 = 0.5 degrees + // 2 = 1.0 degrees + const float radialMultiplier = + 2.0f / + std::clamp(radarData[0]->azimuth_resolution_spacing(), 1, 2); + + const float startAngle = radarData[0]->azimuth_angle(); + const uint16_t startRadial = std::lroundf(startAngle * radialMultiplier); + + for (uint16_t radial = 0; radial < radials; ++radial) + { + auto radialData = radarData[radial]; + auto momentData = radarData[radial]->moment_data_block(p->dataBlockType_); + + if (momentData0->data_word_size() != momentData->data_word_size()) + { + BOOST_LOG_TRIVIAL(warning) + << logPrefix_ << "Radial " << radial << " has different word size"; + continue; + } + + // Compute gate interval + const uint16_t dataMomentRange = momentData->data_moment_range_raw(); + const uint16_t dataMomentInterval = + momentData->data_moment_range_sample_interval_raw(); + const uint16_t dataMomentIntervalH = dataMomentInterval / 2; + + // Compute gate size (number of base 250m gates per bin) + const uint16_t gateSize = std::max(1, dataMomentInterval / 250); + + // Compute gate range [startGate, endGate) + const uint16_t startGate = (dataMomentRange - dataMomentIntervalH) / 250; + const uint16_t numberOfDataMomentGates = + std::min(momentData->number_of_data_moment_gates(), + static_cast(gates)); + const uint16_t endGate = + std::min(startGate + numberOfDataMomentGates * gateSize, + common::MAX_DATA_MOMENT_GATES); + + const uint8_t* dataMomentsArray8 = nullptr; + const uint16_t* dataMomentsArray16 = nullptr; + + if (momentData->data_word_size() == 8) + { + dataMomentsArray8 = + reinterpret_cast(momentData->data_moments()); + } + else + { + dataMomentsArray16 = + reinterpret_cast(momentData->data_moments()); + } + + for (uint16_t gate = startGate, i = 0; gate + gateSize <= endGate; + gate += gateSize, ++i) + { + size_t vertexCount = (gate > 0) ? 6 : 3; + + // Store data moment value + if (dataMomentsArray8 != nullptr) + { + uint8_t dataValue = dataMomentsArray8[i]; + if (dataValue < snrThreshold) + { + continue; + } + + for (size_t m = 0; m < vertexCount; m++) + { + dataMoments8[mIndex++] = dataMomentsArray8[i]; + } + } + else + { + uint16_t dataValue = dataMomentsArray16[i]; + if (dataValue < snrThreshold) + { + continue; + } + + for (size_t m = 0; m < vertexCount; m++) + { + dataMoments16[mIndex++] = dataMomentsArray16[i]; + } + } + + // Store vertices + if (gate > 0) + { + const uint16_t baseCoord = gate - 1; + + size_t offset1 = ((startRadial + radial) % common::MAX_RADIALS * + common::MAX_DATA_MOMENT_GATES + + baseCoord) * + 2; + size_t offset2 = offset1 + gateSize * 2; + size_t offset3 = + (((startRadial + radial + 1) % common::MAX_RADIALS) * + common::MAX_DATA_MOMENT_GATES + + baseCoord) * + 2; + size_t offset4 = offset3 + gateSize * 2; + + vertices[vIndex++] = coordinates[offset1]; + vertices[vIndex++] = coordinates[offset1 + 1]; + + vertices[vIndex++] = coordinates[offset2]; + vertices[vIndex++] = coordinates[offset2 + 1]; + + vertices[vIndex++] = coordinates[offset3]; + vertices[vIndex++] = coordinates[offset3 + 1]; + + vertices[vIndex++] = coordinates[offset3]; + vertices[vIndex++] = coordinates[offset3 + 1]; + + vertices[vIndex++] = coordinates[offset4]; + vertices[vIndex++] = coordinates[offset4 + 1]; + + vertices[vIndex++] = coordinates[offset2]; + vertices[vIndex++] = coordinates[offset2 + 1]; + + vertexCount = 6; + } + else + { + const uint16_t baseCoord = gate; + + size_t offset1 = ((startRadial + radial) % common::MAX_RADIALS * + common::MAX_DATA_MOMENT_GATES + + baseCoord) * + 2; + size_t offset2 = + (((startRadial + radial + 1) % common::MAX_RADIALS) * + common::MAX_DATA_MOMENT_GATES + + baseCoord) * + 2; + + // TODO: Radar location + vertices[vIndex++] = 38.6986f; + vertices[vIndex++] = -90.6828f; + + vertices[vIndex++] = coordinates[offset1]; + vertices[vIndex++] = coordinates[offset1 + 1]; + + vertices[vIndex++] = coordinates[offset2]; + vertices[vIndex++] = coordinates[offset2 + 1]; + + vertexCount = 3; + } + } + } + vertices.resize(vIndex); + + if (momentData0->data_word_size() == 8) + { + dataMoments8.resize(mIndex); + } + else + { + dataMoments16.resize(mIndex); + } + + timer.stop(); + BOOST_LOG_TRIVIAL(debug) + << logPrefix_ << "Vertices calculated in " << timer.format(6, "%ws"); + + emit SweepComputed(); +} + +std::shared_ptr Level2ProductView::Create( + const std::string& productName, + std::shared_ptr radarProductManager) +{ + return std::make_shared(productName, radarProductManager); +} + +static std::chrono::system_clock::time_point +TimePoint(uint16_t modifiedJulianDate, uint32_t milliseconds) +{ + using namespace std::chrono; + using sys_days = time_point; + constexpr auto epoch = sys_days {1969y / December / 31d}; + + return epoch + (modifiedJulianDate * 24h) + + std::chrono::milliseconds {milliseconds}; +} + +} // namespace view +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/view/level2_product_view.hpp b/scwx-qt/source/scwx/qt/view/level2_product_view.hpp new file mode 100644 index 00000000..d9d5e6ac --- /dev/null +++ b/scwx-qt/source/scwx/qt/view/level2_product_view.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace scwx +{ +namespace qt +{ +namespace view +{ + +const std::string PRODUCT_L2_REF = "L2REF"; +const std::string PRODUCT_L2_VEL = "L2VEL"; +const std::string PRODUCT_L2_SW = "L2SW"; +const std::string PRODUCT_L2_ZDR = "L2ZDR"; +const std::string PRODUCT_L2_PHI = "L2PHI"; +const std::string PRODUCT_L2_RHO = "L2RHO"; +const std::string PRODUCT_L2_CFP = "L2CFP"; + +class Level2ProductViewImpl; + +class Level2ProductView : public RadarProductView +{ + Q_OBJECT + +public: + explicit Level2ProductView( + const std::string& productName, + std::shared_ptr radarProductManager); + ~Level2ProductView(); + + const std::vector& color_table() const; + std::chrono::system_clock::time_point sweep_time() const; + const std::vector& vertices() const; + + void LoadColorTable(std::shared_ptr colorTable); + + std::tuple GetMomentData() const; + + static std::shared_ptr + Create(const std::string& productName, + std::shared_ptr radarProductManager); + +protected slots: + void ComputeSweep(); + +private: + std::unique_ptr p; +}; + +} // namespace view +} // namespace qt +} // namespace scwx 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 5a6ac559..0bc459bf 100644 --- a/scwx-qt/source/scwx/qt/view/radar_product_view.cpp +++ b/scwx-qt/source/scwx/qt/view/radar_product_view.cpp @@ -14,57 +14,31 @@ namespace view static const std::string logPrefix_ = "[scwx::qt::view::radar_product_view] "; -static constexpr uint32_t VERTICES_PER_BIN = 6; -static constexpr uint32_t VALUES_PER_VERTEX = 2; - -static std::chrono::system_clock::time_point -TimePoint(uint16_t modifiedJulianDate, uint32_t milliseconds); +static const std::vector DEFAULT_COLOR_TABLE = { + boost::gil::rgba8_pixel_t(0, 128, 0, 255), + boost::gil::rgba8_pixel_t(255, 192, 0, 255), + boost::gil::rgba8_pixel_t(255, 0, 0, 255)}; class RadarProductViewImpl { public: - explicit RadarProductViewImpl( - std::shared_ptr radarProductManager) : - radarProductManager_(radarProductManager), - sweepTime_(), - colorTable_ {boost::gil::rgba8_pixel_t(0, 128, 0, 255), - boost::gil::rgba8_pixel_t(255, 192, 0, 255), - boost::gil::rgba8_pixel_t(255, 0, 0, 255)} - { - } - ~RadarProductViewImpl() = default; - - std::shared_ptr radarProductManager_; - - std::vector vertices_; - std::vector dataMoments8_; - std::vector dataMoments16_; - - std::chrono::system_clock::time_point sweepTime_; - - std::vector colorTable_; + explicit RadarProductViewImpl() = default; + ~RadarProductViewImpl() = default; }; -RadarProductView::RadarProductView( - std::shared_ptr radarProductManager) : - p(std::make_unique(radarProductManager)) -{ - connect(radarProductManager.get(), - &manager::RadarProductManager::Level2DataLoaded, - this, - &RadarProductView::ComputeSweep); -} +RadarProductView::RadarProductView() : + p(std::make_unique()) {}; RadarProductView::~RadarProductView() = default; -const std::vector& RadarProductView::vertices() const -{ - return p->vertices_; -} - const std::vector& RadarProductView::color_table() const { - return p->colorTable_; + return DEFAULT_COLOR_TABLE; +} + +std::chrono::system_clock::time_point RadarProductView::sweep_time() const +{ + return {}; } void RadarProductView::Initialize() @@ -72,300 +46,13 @@ void RadarProductView::Initialize() ComputeSweep(); } -std::tuple RadarProductView::GetMomentData() -{ - const void* data; - size_t dataSize; - size_t componentSize; - - if (p->dataMoments8_.size() > 0) - { - data = p->dataMoments8_.data(); - dataSize = p->dataMoments8_.size() * sizeof(uint8_t); - componentSize = 1; - } - else - { - data = p->dataMoments16_.data(); - dataSize = p->dataMoments16_.size() * sizeof(uint16_t); - componentSize = 2; - } - - return std::tie(data, dataSize, componentSize); -} - -void RadarProductView::LoadColorTable( - std::shared_ptr colorTable) -{ - // TODO: Make size, offset and scale dynamic - const float offset = 66.0f; - const float scale = 2.0f; - - std::vector& lut = p->colorTable_; - lut.resize(254); - - auto dataRange = boost::irange(2, 255); - - std::for_each(std::execution::par_unseq, - dataRange.begin(), - dataRange.end(), - [&](uint16_t i) { - float f = (i - offset) / scale; - lut[i - *dataRange.begin()] = colorTable->Color(f); - }); - - emit ColorTableLoaded(); -} - -std::chrono::system_clock::time_point RadarProductView::SweepTime() -{ - return p->sweepTime_; -} - void RadarProductView::ComputeSweep() { BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "ComputeSweep()"; - boost::timer::cpu_timer timer; - - // TODO: Pick this based on radar data - const std::vector& coordinates = - p->radarProductManager_->coordinates(common::RadialSize::_0_5Degree); - - std::shared_ptr level2Data = - p->radarProductManager_->level2_data(); - if (level2Data == nullptr) - { - return; - } - - // TODO: Pick these based on view settings - auto radarData = level2Data->radar_data()[0]; - wsr88d::rda::DataBlockType blockType = wsr88d::rda::DataBlockType::MomentRef; - - p->sweepTime_ = TimePoint(radarData[0]->modified_julian_date(), - radarData[0]->collection_time()); - - // Calculate vertices - timer.start(); - - auto momentData0 = radarData[0]->moment_data_block(blockType); - - // Setup vertex vector - std::vector& vertices = p->vertices_; - const size_t radials = radarData.size(); - const uint32_t gates = momentData0->number_of_data_moment_gates(); - size_t vIndex = 0; - vertices.clear(); - vertices.resize(radials * gates * VERTICES_PER_BIN * VALUES_PER_VERTEX); - - // Setup data moment vector - std::vector& dataMoments8 = p->dataMoments8_; - std::vector& dataMoments16 = p->dataMoments16_; - size_t mIndex = 0; - - if (momentData0->data_word_size() == 8) - { - dataMoments16.resize(0); - dataMoments16.shrink_to_fit(); - - dataMoments8.resize(radials * gates * VERTICES_PER_BIN); - } - else - { - dataMoments8.resize(0); - dataMoments8.shrink_to_fit(); - - dataMoments16.resize(radials * gates * VERTICES_PER_BIN); - } - - // Compute threshold at which to display an individual bin - const float scale = momentData0->scale(); - const float offset = momentData0->offset(); - const uint16_t snrThreshold = - std::lroundf(momentData0->snr_threshold_raw() * scale / 10 + offset); - - // Azimuth resolution spacing: - // 1 = 0.5 degrees - // 2 = 1.0 degrees - const float radialMultiplier = - 2.0f / - std::clamp(radarData[0]->azimuth_resolution_spacing(), 1, 2); - - const float startAngle = radarData[0]->azimuth_angle(); - const uint16_t startRadial = std::lroundf(startAngle * radialMultiplier); - - for (uint16_t radial = 0; radial < radials; ++radial) - { - auto radialData = radarData[radial]; - auto momentData = radarData[radial]->moment_data_block(blockType); - - if (momentData0->data_word_size() != momentData->data_word_size()) - { - BOOST_LOG_TRIVIAL(warning) - << logPrefix_ << "Radial " << radial << " has different word size"; - continue; - } - - // Compute gate interval - const uint16_t dataMomentRange = momentData->data_moment_range_raw(); - const uint16_t dataMomentInterval = - momentData->data_moment_range_sample_interval_raw(); - const uint16_t dataMomentIntervalH = dataMomentInterval / 2; - - // Compute gate size (number of base 250m gates per bin) - const uint16_t gateSize = std::max(1, dataMomentInterval / 250); - - // Compute gate range [startGate, endGate) - const uint16_t startGate = (dataMomentRange - dataMomentIntervalH) / 250; - const uint16_t numberOfDataMomentGates = - std::min(momentData->number_of_data_moment_gates(), - static_cast(gates)); - const uint16_t endGate = - std::min(startGate + numberOfDataMomentGates * gateSize, - common::MAX_DATA_MOMENT_GATES); - - const uint8_t* dataMomentsArray8 = nullptr; - const uint16_t* dataMomentsArray16 = nullptr; - - if (momentData->data_word_size() == 8) - { - dataMomentsArray8 = - reinterpret_cast(momentData->data_moments()); - } - else - { - dataMomentsArray16 = - reinterpret_cast(momentData->data_moments()); - } - - for (uint16_t gate = startGate, i = 0; gate + gateSize <= endGate; - gate += gateSize, ++i) - { - size_t vertexCount = (gate > 0) ? 6 : 3; - - // Store data moment value - if (dataMomentsArray8 != nullptr) - { - uint8_t dataValue = dataMomentsArray8[i]; - if (dataValue < snrThreshold) - { - continue; - } - - for (size_t m = 0; m < vertexCount; m++) - { - dataMoments8[mIndex++] = dataMomentsArray8[i]; - } - } - else - { - uint16_t dataValue = dataMomentsArray16[i]; - if (dataValue < snrThreshold) - { - continue; - } - - for (size_t m = 0; m < vertexCount; m++) - { - dataMoments16[mIndex++] = dataMomentsArray16[i]; - } - } - - // Store vertices - if (gate > 0) - { - const uint16_t baseCoord = gate - 1; - - size_t offset1 = ((startRadial + radial) % common::MAX_RADIALS * - common::MAX_DATA_MOMENT_GATES + - baseCoord) * - 2; - size_t offset2 = offset1 + gateSize * 2; - size_t offset3 = - (((startRadial + radial + 1) % common::MAX_RADIALS) * - common::MAX_DATA_MOMENT_GATES + - baseCoord) * - 2; - size_t offset4 = offset3 + gateSize * 2; - - vertices[vIndex++] = coordinates[offset1]; - vertices[vIndex++] = coordinates[offset1 + 1]; - - vertices[vIndex++] = coordinates[offset2]; - vertices[vIndex++] = coordinates[offset2 + 1]; - - vertices[vIndex++] = coordinates[offset3]; - vertices[vIndex++] = coordinates[offset3 + 1]; - - vertices[vIndex++] = coordinates[offset3]; - vertices[vIndex++] = coordinates[offset3 + 1]; - - vertices[vIndex++] = coordinates[offset4]; - vertices[vIndex++] = coordinates[offset4 + 1]; - - vertices[vIndex++] = coordinates[offset2]; - vertices[vIndex++] = coordinates[offset2 + 1]; - - vertexCount = 6; - } - else - { - const uint16_t baseCoord = gate; - - size_t offset1 = ((startRadial + radial) % common::MAX_RADIALS * - common::MAX_DATA_MOMENT_GATES + - baseCoord) * - 2; - size_t offset2 = - (((startRadial + radial + 1) % common::MAX_RADIALS) * - common::MAX_DATA_MOMENT_GATES + - baseCoord) * - 2; - - // TODO: Radar location - vertices[vIndex++] = 38.6986f; - vertices[vIndex++] = -90.6828f; - - vertices[vIndex++] = coordinates[offset1]; - vertices[vIndex++] = coordinates[offset1 + 1]; - - vertices[vIndex++] = coordinates[offset2]; - vertices[vIndex++] = coordinates[offset2 + 1]; - - vertexCount = 3; - } - } - } - vertices.resize(vIndex); - - if (momentData0->data_word_size() == 8) - { - dataMoments8.resize(mIndex); - } - else - { - dataMoments16.resize(mIndex); - } - - timer.stop(); - BOOST_LOG_TRIVIAL(debug) - << logPrefix_ << "Vertices calculated in " << timer.format(6, "%ws"); - emit SweepComputed(); } -static std::chrono::system_clock::time_point -TimePoint(uint16_t modifiedJulianDate, uint32_t milliseconds) -{ - using namespace std::chrono; - using sys_days = time_point; - constexpr auto epoch = sys_days {1969y / December / 31d}; - - return epoch + (modifiedJulianDate * 24h) + - std::chrono::milliseconds {milliseconds}; -} - } // namespace view } // namespace qt } // namespace scwx 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 dd1e736b..1d2d3af2 100644 --- a/scwx-qt/source/scwx/qt/view/radar_product_view.hpp +++ b/scwx-qt/source/scwx/qt/view/radar_product_view.hpp @@ -1,12 +1,13 @@ #pragma once #include -#include #include #include #include +#include + namespace scwx { namespace qt @@ -21,22 +22,21 @@ class RadarProductView : public QObject Q_OBJECT public: - explicit RadarProductView( - std::shared_ptr radarProductManager); + explicit RadarProductView(); ~RadarProductView(); - const std::vector& vertices() const; - const std::vector& color_table() const; + virtual const std::vector& color_table() const; + virtual std::chrono::system_clock::time_point sweep_time() const; + virtual const std::vector& vertices() const = 0; void Initialize(); + virtual void + LoadColorTable(std::shared_ptr colorTable) = 0; - std::tuple GetMomentData(); - void LoadColorTable(std::shared_ptr colorTable); - - std::chrono::system_clock::time_point SweepTime(); + virtual std::tuple GetMomentData() const = 0; protected slots: - void ComputeSweep(); + virtual void ComputeSweep(); signals: void ColorTableLoaded(); 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 new file mode 100644 index 00000000..ac9826ac --- /dev/null +++ b/scwx-qt/source/scwx/qt/view/radar_product_view_factory.cpp @@ -0,0 +1,51 @@ +#include +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace view +{ + +static const std::string logPrefix_ = + "[scwx::qt::view::radar_product_view_factory] "; + +typedef std::function( + const std::string& productName, + std::shared_ptr radarProductManager)> + CreateRadarProductFunction; + +static const std::unordered_map + create_ {{PRODUCT_L2_REF, Level2ProductView::Create}, + {PRODUCT_L2_VEL, Level2ProductView::Create}, + {PRODUCT_L2_SW, Level2ProductView::Create}, + {PRODUCT_L2_ZDR, Level2ProductView::Create}, + {PRODUCT_L2_PHI, Level2ProductView::Create}, + {PRODUCT_L2_RHO, Level2ProductView::Create}, + {PRODUCT_L2_CFP, Level2ProductView::Create}}; + +std::shared_ptr RadarProductViewFactory::Create( + const std::string& productName, + std::shared_ptr radarProductManager) +{ + std::shared_ptr view = nullptr; + + if (create_.find(productName) == create_.end()) + { + BOOST_LOG_TRIVIAL(warning) + << logPrefix_ << "Unknown radar product: " << productName; + } + else + { + view = create_.at(productName)(productName, radarProductManager); + } + + return view; +} + +} // namespace view +} // namespace qt +} // namespace scwx 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 new file mode 100644 index 00000000..04e6031e --- /dev/null +++ b/scwx-qt/source/scwx/qt/view/radar_product_view_factory.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace view +{ + +class RadarProductViewFactory +{ +private: + explicit RadarProductViewFactory() = delete; + ~RadarProductViewFactory() = delete; + + RadarProductViewFactory(const RadarProductViewFactory&) = delete; + RadarProductViewFactory& operator=(const RadarProductViewFactory&) = delete; + + RadarProductViewFactory(RadarProductViewFactory&&) noexcept = delete; + RadarProductViewFactory& + operator=(RadarProductViewFactory&&) noexcept = delete; + +public: + static std::shared_ptr + Create(const std::string& productName, + std::shared_ptr radarProductManager); +}; + +} // namespace view +} // namespace qt +} // namespace scwx