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