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 51280987..0da646fb 100644 --- a/scwx-qt/source/scwx/qt/view/level2_product_view.cpp +++ b/scwx-qt/source/scwx/qt/view/level2_product_view.cpp @@ -774,6 +774,14 @@ void Level2ProductViewImpl::ComputeCoordinates( logger_->debug("Coordinates calculated in {}", timer.format(6, "%ws")); } +std::optional +Level2ProductView::GetBinLevel(const common::Coordinate& coordinate) const +{ + // TODO + Q_UNUSED(coordinate); + return std::nullopt; +} + std::shared_ptr Level2ProductView::Create( common::Level2Product product, std::shared_ptr radarProductManager) 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 700cd591..d0c9423d 100644 --- a/scwx-qt/source/scwx/qt/view/level2_product_view.hpp +++ b/scwx-qt/source/scwx/qt/view/level2_product_view.hpp @@ -47,6 +47,8 @@ public: GetMomentData() const override; std::tuple GetCfpMomentData() const override; + std::optional + GetBinLevel(const common::Coordinate& coordinate) const override; static std::shared_ptr Create(common::Level2Product product, diff --git a/scwx-qt/source/scwx/qt/view/level3_product_view.cpp b/scwx-qt/source/scwx/qt/view/level3_product_view.cpp index 34ed0d8e..06f291e0 100644 --- a/scwx-qt/source/scwx/qt/view/level3_product_view.cpp +++ b/scwx-qt/source/scwx/qt/view/level3_product_view.cpp @@ -27,10 +27,10 @@ static const auto logger_ = util::Logger::Create(logPrefix_); static constexpr uint16_t RANGE_FOLDED = 1u; -class Level3ProductViewImpl +class Level3ProductView::Impl { public: - explicit Level3ProductViewImpl(const std::string& product) : + explicit Impl(const std::string& product) : product_ {product}, graphicMessage_ {nullptr}, colorTable_ {}, @@ -42,7 +42,7 @@ public: savedOffset_ {0.0f} { } - ~Level3ProductViewImpl() = default; + ~Impl() = default; std::string product_; @@ -61,8 +61,7 @@ public: Level3ProductView::Level3ProductView( const std::string& product, std::shared_ptr radarProductManager) : - RadarProductView(radarProductManager), - p(std::make_unique(product)) + RadarProductView(radarProductManager), p(std::make_unique(product)) { ConnectRadarProductManager(); } diff --git a/scwx-qt/source/scwx/qt/view/level3_product_view.hpp b/scwx-qt/source/scwx/qt/view/level3_product_view.hpp index 00e42281..f37e44e3 100644 --- a/scwx-qt/source/scwx/qt/view/level3_product_view.hpp +++ b/scwx-qt/source/scwx/qt/view/level3_product_view.hpp @@ -15,8 +15,6 @@ namespace qt namespace view { -class Level3ProductViewImpl; - class Level3ProductView : public RadarProductView { Q_OBJECT @@ -52,7 +50,8 @@ protected: void UpdateColorTable() override; private: - std::unique_ptr p; + class Impl; + std::unique_ptr p; }; } // namespace view diff --git a/scwx-qt/source/scwx/qt/view/level3_radial_view.cpp b/scwx-qt/source/scwx/qt/view/level3_radial_view.cpp index 0e10b5dc..fed11a40 100644 --- a/scwx-qt/source/scwx/qt/view/level3_radial_view.cpp +++ b/scwx-qt/source/scwx/qt/view/level3_radial_view.cpp @@ -28,10 +28,10 @@ static constexpr std::uint16_t RANGE_FOLDED = 1u; static constexpr std::uint32_t VERTICES_PER_BIN = 6u; static constexpr std::uint32_t VALUES_PER_VERTEX = 2u; -class Level3RadialViewImpl +class Level3RadialView::Impl { public: - explicit Level3RadialViewImpl(Level3RadialView* self) : + explicit Impl(Level3RadialView* self) : self_ {self}, latitude_ {}, longitude_ {}, @@ -41,10 +41,10 @@ public: { coordinates_.resize(kMaxCoordinates_); } - ~Level3RadialViewImpl() { threadPool_.join(); }; + ~Impl() { threadPool_.join(); }; void ComputeCoordinates( - std::shared_ptr radialData); + const std::shared_ptr& radialData); Level3RadialView* self_; @@ -54,6 +54,8 @@ public: std::vector vertices_ {}; std::vector dataMoments8_ {}; + std::shared_ptr lastRadialData_ {}; + float latitude_; float longitude_; float range_; @@ -66,7 +68,7 @@ Level3RadialView::Level3RadialView( const std::string& product, std::shared_ptr radarProductManager) : Level3ProductView(product, radarProductManager), - p(std::make_unique(this)) + p(std::make_unique(this)) { } @@ -235,6 +237,8 @@ void Level3RadialView::ComputeSweep() return; } + p->lastRadialData_ = radialData; + // Valid number of radials is 1-720 size_t radials = radialData->number_of_radials(); if (radials < 1 || radials > 720) @@ -424,8 +428,8 @@ void Level3RadialView::ComputeSweep() Q_EMIT SweepComputed(); } -void Level3RadialViewImpl::ComputeCoordinates( - std::shared_ptr radialData) +void Level3RadialView::Impl::ComputeCoordinates( + const std::shared_ptr& radialData) { logger_->debug("ComputeCoordinates()"); @@ -485,6 +489,113 @@ void Level3RadialViewImpl::ComputeCoordinates( logger_->debug("Coordinates calculated in {}", timer.format(6, "%ws")); } +std::optional +Level3RadialView::GetBinLevel(const common::Coordinate& coordinate) const +{ + auto gpm = graphic_product_message(); + if (gpm == nullptr) + { + return std::nullopt; + } + + std::shared_ptr descriptionBlock = + gpm->description_block(); + if (descriptionBlock == nullptr) + { + return std::nullopt; + } + + std::shared_ptr radialData = + p->lastRadialData_; + if (radialData == nullptr) + { + return std::nullopt; + } + + // Determine distance and azimuth of coordinate relative to radar location + double s12; // Distance (meters) + double azi1; // Azimuth (degrees) + double azi2; // Unused + util::GeographicLib::DefaultGeodesic().Inverse( + descriptionBlock->latitude_of_radar(), + descriptionBlock->longitude_of_radar(), + coordinate.latitude_, + coordinate.longitude_, + s12, + azi1, + azi2); + + if (std::isnan(azi1)) + { + // If a problem occurred with the geodesic inverse calculation + return std::nullopt; + } + + // Azimuth is returned as [-180, 180) from the geodesic inverse, we need a + // range of [0, 360) + while (azi1 < 0.0) + { + azi1 += 360.0; + } + + // Compute gate interval + const std::uint16_t gates = radialData->number_of_range_bins(); + const std::uint16_t dataMomentInterval = + descriptionBlock->x_resolution_raw(); + std::uint16_t gate = s12 / dataMomentInterval; + + if (gate >= gates) + { + // Coordinate is beyond radar range + return std::nullopt; + } + + // Find Radial + const std::uint16_t numRadials = radialData->number_of_radials(); + std::uint16_t radial = numRadials; + float nextAngle = radialData->start_angle(0); + for (std::uint16_t i = 0; i < numRadials; ++i) + { + float startAngle = nextAngle; + nextAngle = radialData->start_angle((i + 1) % numRadials); + + if (startAngle < nextAngle) + { + if (startAngle <= azi1 && azi1 < nextAngle) + { + radial = i; + break; + } + } + else + { + // If the bin crosses 0/360 degrees, special handling is needed + if (startAngle <= azi1 || azi1 < nextAngle) + { + radial = i; + break; + } + } + } + + if (radial == numRadials) + { + // No radial was found (not likely to happen without a gap in data) + return std::nullopt; + } + + // Compute threshold at which to display an individual bin + const std::uint16_t snrThreshold = descriptionBlock->threshold(); + const std::uint8_t level = radialData->level(radial).at(gate); + + if (level < snrThreshold && level != RANGE_FOLDED) + { + return std::nullopt; + } + + return level; +} + std::shared_ptr Level3RadialView::Create( const std::string& product, std::shared_ptr radarProductManager) diff --git a/scwx-qt/source/scwx/qt/view/level3_radial_view.hpp b/scwx-qt/source/scwx/qt/view/level3_radial_view.hpp index c6852eb1..7f5f488c 100644 --- a/scwx-qt/source/scwx/qt/view/level3_radial_view.hpp +++ b/scwx-qt/source/scwx/qt/view/level3_radial_view.hpp @@ -13,8 +13,6 @@ namespace qt namespace view { -class Level3RadialViewImpl; - class Level3RadialView : public Level3ProductView { Q_OBJECT @@ -32,6 +30,8 @@ public: std::tuple GetMomentData() const override; + std::optional + GetBinLevel(const common::Coordinate& coordinate) const override; static std::shared_ptr Create(const std::string& product, @@ -44,7 +44,8 @@ protected slots: void ComputeSweep() override; private: - std::unique_ptr p; + class Impl; + std::unique_ptr p; }; } // namespace view diff --git a/scwx-qt/source/scwx/qt/view/level3_raster_view.cpp b/scwx-qt/source/scwx/qt/view/level3_raster_view.cpp index 5dfd9956..9c55f2b8 100644 --- a/scwx-qt/source/scwx/qt/view/level3_raster_view.cpp +++ b/scwx-qt/source/scwx/qt/view/level3_raster_view.cpp @@ -352,6 +352,14 @@ void Level3RasterView::ComputeSweep() Q_EMIT SweepComputed(); } +std::optional +Level3RasterView::GetBinLevel(const common::Coordinate& coordinate) const +{ + // TODO + Q_UNUSED(coordinate); + return std::nullopt; +} + std::shared_ptr Level3RasterView::Create( const std::string& product, std::shared_ptr radarProductManager) diff --git a/scwx-qt/source/scwx/qt/view/level3_raster_view.hpp b/scwx-qt/source/scwx/qt/view/level3_raster_view.hpp index eb60fe80..0f3cd14e 100644 --- a/scwx-qt/source/scwx/qt/view/level3_raster_view.hpp +++ b/scwx-qt/source/scwx/qt/view/level3_raster_view.hpp @@ -32,6 +32,8 @@ public: std::tuple GetMomentData() const override; + std::optional + GetBinLevel(const common::Coordinate& coordinate) const override; static std::shared_ptr Create(const std::string& product, 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 f1c2a8ae..1ac43eb6 100644 --- a/scwx-qt/source/scwx/qt/view/radar_product_view.hpp +++ b/scwx-qt/source/scwx/qt/view/radar_product_view.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -8,6 +9,7 @@ #include #include #include +#include #include #include @@ -63,7 +65,9 @@ public: virtual std::tuple GetMomentData() const = 0; virtual std::tuple - GetCfpMomentData() const; + GetCfpMomentData() const; + virtual std::optional + GetBinLevel(const common::Coordinate& coordinate) const = 0; std::chrono::system_clock::time_point GetSelectedTime() const; virtual std::vector>