mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-11-01 07:30:04 +00:00
Merge pull request #394 from AdenKoperczak/update_tdwr_products
Update TDWR products
This commit is contained in:
commit
e25f56fcae
8 changed files with 210 additions and 33 deletions
|
|
@ -230,6 +230,7 @@ public:
|
|||
const std::string radarId_;
|
||||
bool initialized_;
|
||||
bool level3ProductsInitialized_;
|
||||
bool level3AvailabilityReady_ {false};
|
||||
|
||||
std::shared_ptr<config::RadarSite> radarSite_;
|
||||
std::size_t cacheLimit_ {6u};
|
||||
|
|
@ -428,9 +429,16 @@ const scwx::util::time_zone* RadarProductManager::default_time_zone() const
|
|||
}
|
||||
}
|
||||
|
||||
bool RadarProductManager::is_tdwr() const
|
||||
{
|
||||
return p->radarSite_->type() == "tdwr";
|
||||
}
|
||||
|
||||
float RadarProductManager::gate_size() const
|
||||
{
|
||||
return (p->radarSite_->type() == "tdwr") ? 150.0f : 250.0f;
|
||||
// tdwr is 150 meter per gate, wsr88d is 250 meter per gate
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
|
||||
return (is_tdwr()) ? 150.0f : 250.0f;
|
||||
}
|
||||
|
||||
std::string RadarProductManager::radar_id() const
|
||||
|
|
@ -454,6 +462,12 @@ void RadarProductManager::Initialize()
|
|||
|
||||
logger_->debug("Initialize()");
|
||||
|
||||
if (is_tdwr())
|
||||
{
|
||||
p->initialized_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
boost::timer::cpu_timer timer;
|
||||
|
||||
// Calculate half degree azimuth coordinates
|
||||
|
|
@ -1572,6 +1586,12 @@ void RadarProductManager::UpdateAvailableProducts()
|
|||
|
||||
if (p->level3ProductsInitialized_)
|
||||
{
|
||||
if (p->level3AvailabilityReady_)
|
||||
{
|
||||
// Multiple maps may use the same manager, so this ensures that all get
|
||||
// notified of the change
|
||||
Q_EMIT Level3ProductsChanged();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1647,6 +1667,7 @@ void RadarProductManagerImpl::UpdateAvailableProductsSync()
|
|||
}
|
||||
}
|
||||
|
||||
level3AvailabilityReady_ = true;
|
||||
Q_EMIT self_->Level3ProductsChanged();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ public:
|
|||
[[nodiscard]] const std::vector<float>&
|
||||
coordinates(common::RadialSize radialSize, bool smoothingEnabled) const;
|
||||
[[nodiscard]] const scwx::util::time_zone* default_time_zone() const;
|
||||
[[nodiscard]] bool is_tdwr() const;
|
||||
[[nodiscard]] float gate_size() const;
|
||||
[[nodiscard]] std::string radar_id() const;
|
||||
[[nodiscard]] std::shared_ptr<config::RadarSite> radar_site() const;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include <scwx/util/logger.hpp>
|
||||
#include <scwx/util/time.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
#include <backends/imgui_impl_opengl3.h>
|
||||
|
|
@ -178,9 +179,11 @@ public:
|
|||
void SelectNearestRadarSite(double latitude,
|
||||
double longitude,
|
||||
std::optional<std::string> type);
|
||||
void SetRadarSite(const std::string& radarSite);
|
||||
void SetRadarSite(const std::string& radarSite,
|
||||
bool checkProductAvailability = false);
|
||||
void UpdateLoadedStyle();
|
||||
bool UpdateStoredMapParameters();
|
||||
void CheckLevel3Availability();
|
||||
|
||||
std::string FindMapSymbologyLayer();
|
||||
|
||||
|
|
@ -268,6 +271,10 @@ public:
|
|||
std::set<types::Hotkey> activeHotkeys_ {};
|
||||
std::chrono::system_clock::time_point prevHotkeyTime_ {};
|
||||
|
||||
bool productAvailabilityCheckNeeded_ {false};
|
||||
bool productAvailabilityUpdated_ {false};
|
||||
bool productAvailabilityProductSelected_ {false};
|
||||
|
||||
public slots:
|
||||
void Update();
|
||||
};
|
||||
|
|
@ -429,6 +436,14 @@ void MapWidgetImpl::ConnectSignals()
|
|||
&manager::HotkeyManager::HotkeyReleased,
|
||||
this,
|
||||
&MapWidgetImpl::HandleHotkeyReleased);
|
||||
connect(widget_,
|
||||
&MapWidget::RadarSiteUpdated,
|
||||
widget_,
|
||||
[this](const std::shared_ptr<config::RadarSite>&)
|
||||
{
|
||||
productAvailabilityProductSelected_ = true;
|
||||
CheckLevel3Availability();
|
||||
});
|
||||
}
|
||||
|
||||
void MapWidgetImpl::HandleHotkeyPressed(types::Hotkey hotkey, bool isAutoRepeat)
|
||||
|
|
@ -913,7 +928,7 @@ void MapWidget::SelectRadarSite(std::shared_ptr<config::RadarSite> radarSite,
|
|||
p->map_->setCoordinate(
|
||||
{radarSite->latitude(), radarSite->longitude()});
|
||||
}
|
||||
p->SetRadarSite(radarSite->id());
|
||||
p->SetRadarSite(radarSite->id(), true);
|
||||
p->Update();
|
||||
|
||||
// Select products from new site
|
||||
|
|
@ -1772,7 +1787,12 @@ void MapWidgetImpl::RadarProductManagerConnect()
|
|||
connect(radarProductManager_.get(),
|
||||
&manager::RadarProductManager::Level3ProductsChanged,
|
||||
this,
|
||||
[this]() { Q_EMIT widget_->Level3ProductsChanged(); });
|
||||
[this]()
|
||||
{
|
||||
productAvailabilityUpdated_ = true;
|
||||
CheckLevel3Availability();
|
||||
Q_EMIT widget_->Level3ProductsChanged();
|
||||
});
|
||||
|
||||
connect(
|
||||
radarProductManager_.get(),
|
||||
|
|
@ -1990,7 +2010,8 @@ void MapWidgetImpl::SelectNearestRadarSite(double latitude,
|
|||
}
|
||||
}
|
||||
|
||||
void MapWidgetImpl::SetRadarSite(const std::string& radarSite)
|
||||
void MapWidgetImpl::SetRadarSite(const std::string& radarSite,
|
||||
bool checkProductAvailability)
|
||||
{
|
||||
// Check if radar site has changed
|
||||
if (radarProductManager_ == nullptr ||
|
||||
|
|
@ -2009,6 +2030,12 @@ void MapWidgetImpl::SetRadarSite(const std::string& radarSite)
|
|||
// Connect signals to new RadarProductManager
|
||||
RadarProductManagerConnect();
|
||||
|
||||
// Once the available products are loaded, check to make sure the current
|
||||
// one is available
|
||||
productAvailabilityCheckNeeded_ = checkProductAvailability;
|
||||
productAvailabilityUpdated_ = false;
|
||||
productAvailabilityProductSelected_ = false;
|
||||
|
||||
radarProductManager_->UpdateAvailableProducts();
|
||||
}
|
||||
}
|
||||
|
|
@ -2053,6 +2080,83 @@ bool MapWidgetImpl::UpdateStoredMapParameters()
|
|||
return changed;
|
||||
}
|
||||
|
||||
void MapWidgetImpl::CheckLevel3Availability()
|
||||
{
|
||||
/*
|
||||
* productAvailabilityCheckNeeded_ Only do this when it is indicated that it
|
||||
* is needed (mostly on radar site change). This is mainly to avoid potential
|
||||
* recursion with SelectRadarProduct calls.
|
||||
*
|
||||
* productAvailabilityUpdated_ Only update once the product availability
|
||||
* has been updated
|
||||
*
|
||||
* productAvailabilityProductSelected_ Only update once the radar site is
|
||||
* fully selected, including the current product
|
||||
*/
|
||||
if (!(productAvailabilityCheckNeeded_ && productAvailabilityUpdated_ &&
|
||||
productAvailabilityProductSelected_))
|
||||
{
|
||||
return;
|
||||
}
|
||||
productAvailabilityCheckNeeded_ = false;
|
||||
|
||||
// Only do this for level3 products
|
||||
if (widget_->GetRadarProductGroup() != common::RadarProductGroup::Level3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const common::Level3ProductCategoryMap& categoryMap =
|
||||
widget_->GetAvailableLevel3Categories();
|
||||
|
||||
const std::string& productTilt = context_->radar_product();
|
||||
const std::string& productName =
|
||||
common::GetLevel3ProductByAwipsId(productTilt);
|
||||
const common::Level3ProductCategory productCategory =
|
||||
common::GetLevel3CategoryByProduct(productName);
|
||||
if (productCategory == common::Level3ProductCategory::Unknown)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& availableProductsIt = categoryMap.find(productCategory);
|
||||
// Has no products in this category, do not change categories
|
||||
if (availableProductsIt == categoryMap.cend())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& availableProducts = availableProductsIt->second;
|
||||
const auto& availableTiltsIt = availableProducts.find(productName);
|
||||
// Does not have the same product, but has others in the same category.
|
||||
// Switch to the default product and tilt in this category.
|
||||
if (availableTiltsIt == availableProducts.cend())
|
||||
{
|
||||
widget_->SelectRadarProduct(
|
||||
common::RadarProductGroup::Level3,
|
||||
common::GetLevel3CategoryDefaultProduct(productCategory, categoryMap),
|
||||
0,
|
||||
widget_->GetSelectedTime());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& availableTilts = availableTiltsIt->second;
|
||||
const auto& tilt = std::ranges::find_if(
|
||||
availableTilts,
|
||||
[productTilt](const std::string& tilt) { return productTilt == tilt; });
|
||||
// Tilt is not available, set it to first tilt
|
||||
if (tilt == availableTilts.cend() && availableTilts.size() > 0)
|
||||
{
|
||||
widget_->SelectRadarProduct(common::RadarProductGroup::Level3,
|
||||
availableTilts[0],
|
||||
0,
|
||||
widget_->GetSelectedTime());
|
||||
return;
|
||||
}
|
||||
|
||||
// Tilt is available, no change needed
|
||||
}
|
||||
|
||||
} // namespace map
|
||||
} // namespace qt
|
||||
} // namespace scwx
|
||||
|
|
|
|||
|
|
@ -60,7 +60,9 @@ public:
|
|||
categoryButtons_ {},
|
||||
productTiltMap_ {},
|
||||
awipsProductMap_ {},
|
||||
awipsProductMutex_ {}
|
||||
awipsProductMutex_ {},
|
||||
categoryMap_ {},
|
||||
categoryMapMutex_ {}
|
||||
{
|
||||
layout_->setContentsMargins(0, 0, 0, 0);
|
||||
layout_->addWidget(productsWidget_);
|
||||
|
|
@ -183,6 +185,9 @@ public:
|
|||
std::unordered_map<QAction*, std::string> awipsProductMap_;
|
||||
std::shared_mutex awipsProductMutex_;
|
||||
|
||||
common::Level3ProductCategoryMap categoryMap_;
|
||||
std::shared_mutex categoryMapMutex_;
|
||||
|
||||
std::string currentAwipsId_ {};
|
||||
QAction* currentProductTiltAction_ {nullptr};
|
||||
|
||||
|
|
@ -322,9 +327,11 @@ void Level3ProductsWidgetImpl::SelectProductCategory(
|
|||
{
|
||||
UpdateCategorySelection(category);
|
||||
|
||||
const std::shared_lock lock {categoryMapMutex_};
|
||||
|
||||
Q_EMIT self_->RadarProductSelected(
|
||||
common::RadarProductGroup::Level3,
|
||||
common::GetLevel3CategoryDefaultProduct(category),
|
||||
common::GetLevel3CategoryDefaultProduct(category, categoryMap_),
|
||||
0);
|
||||
}
|
||||
|
||||
|
|
@ -333,6 +340,12 @@ void Level3ProductsWidget::UpdateAvailableProducts(
|
|||
{
|
||||
logger_->trace("UpdateAvailableProducts()");
|
||||
|
||||
// Save the category map
|
||||
{
|
||||
const std::unique_lock lock {p->categoryMapMutex_};
|
||||
p->categoryMap_ = updatedCategoryMap;
|
||||
}
|
||||
|
||||
// Iterate through each category tool button
|
||||
std::for_each(
|
||||
p->categoryButtons_.cbegin(),
|
||||
|
|
|
|||
|
|
@ -39,13 +39,13 @@ public:
|
|||
vcp_ {},
|
||||
sweepTime_ {}
|
||||
{
|
||||
coordinates_.resize(kMaxCoordinates_);
|
||||
}
|
||||
~Impl() { threadPool_.join(); };
|
||||
|
||||
void ComputeCoordinates(
|
||||
const std::shared_ptr<wsr88d::rpg::GenericRadialDataPacket>& radialData,
|
||||
bool smoothingEnabled);
|
||||
bool smoothingEnabled,
|
||||
float gateSize);
|
||||
|
||||
[[nodiscard]] inline std::uint8_t
|
||||
RemapDataMoment(std::uint8_t dataMoment) const;
|
||||
|
|
@ -269,17 +269,24 @@ void Level3RadialView::ComputeSweep()
|
|||
}
|
||||
|
||||
common::RadialSize radialSize;
|
||||
if (radials == common::MAX_0_5_DEGREE_RADIALS)
|
||||
if (radarProductManager->is_tdwr())
|
||||
{
|
||||
radialSize = common::RadialSize::_0_5Degree;
|
||||
}
|
||||
else if (radials == common::MAX_1_DEGREE_RADIALS)
|
||||
{
|
||||
radialSize = common::RadialSize::_1Degree;
|
||||
radialSize = common::RadialSize::NonStandard;
|
||||
}
|
||||
else
|
||||
{
|
||||
radialSize = common::RadialSize::NonStandard;
|
||||
if (radials == common::MAX_0_5_DEGREE_RADIALS)
|
||||
{
|
||||
radialSize = common::RadialSize::_0_5Degree;
|
||||
}
|
||||
else if (radials == common::MAX_1_DEGREE_RADIALS)
|
||||
{
|
||||
radialSize = common::RadialSize::_1Degree;
|
||||
}
|
||||
else
|
||||
{
|
||||
radialSize = common::RadialSize::NonStandard;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<float>& coordinates =
|
||||
|
|
@ -323,11 +330,21 @@ void Level3RadialView::ComputeSweep()
|
|||
// Compute threshold at which to display an individual bin
|
||||
const uint16_t snrThreshold = descriptionBlock->threshold();
|
||||
|
||||
// Compute gate interval
|
||||
const std::uint16_t dataMomentInterval =
|
||||
descriptionBlock->x_resolution_raw();
|
||||
|
||||
// Get the gate length in meters. Use dataMomentInterval for NonStandard to
|
||||
// avoid generating >1 base gates per bin.
|
||||
const float gateLength = radialSize == common::RadialSize::NonStandard ?
|
||||
static_cast<float>(dataMomentInterval) :
|
||||
radarProductManager->gate_size();
|
||||
|
||||
// Determine which radial to start at
|
||||
std::uint16_t startRadial;
|
||||
if (radialSize == common::RadialSize::NonStandard)
|
||||
{
|
||||
p->ComputeCoordinates(radialData, smoothingEnabled);
|
||||
p->ComputeCoordinates(radialData, smoothingEnabled, gateLength);
|
||||
startRadial = 0;
|
||||
}
|
||||
else
|
||||
|
|
@ -337,15 +354,9 @@ void Level3RadialView::ComputeSweep()
|
|||
startRadial = std::lroundf(startAngle * radialMultiplier);
|
||||
}
|
||||
|
||||
// Compute gate interval
|
||||
const std::uint16_t dataMomentInterval =
|
||||
descriptionBlock->x_resolution_raw();
|
||||
|
||||
// Compute gate size (number of base gates per bin)
|
||||
const std::uint16_t gateSize = std::max<std::uint16_t>(
|
||||
1,
|
||||
dataMomentInterval /
|
||||
static_cast<std::uint16_t>(radarProductManager->gate_size()));
|
||||
1, dataMomentInterval / static_cast<std::uint16_t>(gateLength));
|
||||
|
||||
// Compute gate range [startGate, endGate)
|
||||
std::uint16_t startGate = 0;
|
||||
|
|
@ -526,7 +537,8 @@ Level3RadialView::Impl::RemapDataMoment(std::uint8_t dataMoment) const
|
|||
|
||||
void Level3RadialView::Impl::ComputeCoordinates(
|
||||
const std::shared_ptr<wsr88d::rpg::GenericRadialDataPacket>& radialData,
|
||||
bool smoothingEnabled)
|
||||
bool smoothingEnabled,
|
||||
float gateSize)
|
||||
{
|
||||
logger_->debug("ComputeCoordinates()");
|
||||
|
||||
|
|
@ -537,13 +549,14 @@ void Level3RadialView::Impl::ComputeCoordinates(
|
|||
|
||||
auto radarProductManager = self_->radar_product_manager();
|
||||
auto radarSite = radarProductManager->radar_site();
|
||||
const float gateSize = radarProductManager->gate_size();
|
||||
const double radarLatitude = radarSite->latitude();
|
||||
const double radarLongitude = radarSite->longitude();
|
||||
|
||||
// Calculate azimuth coordinates
|
||||
timer.start();
|
||||
|
||||
coordinates_.resize(kMaxCoordinates_);
|
||||
|
||||
const std::uint16_t numRadials = radialData->number_of_radials();
|
||||
const std::uint16_t numRangeBins = radialData->number_of_range_bins();
|
||||
|
||||
|
|
@ -583,6 +596,10 @@ void Level3RadialView::Impl::ComputeCoordinates(
|
|||
const float range =
|
||||
(static_cast<float>(gate) + gateRangeOffset) * gateSize;
|
||||
const std::size_t offset = static_cast<size_t>(radialGate) * 2;
|
||||
if (offset + 1 >= coordinates_.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double latitude = 0.0;
|
||||
double longitude = 0.0;
|
||||
|
|
|
|||
|
|
@ -73,8 +73,9 @@ Level2Product GetLevel2Product(const std::string& name);
|
|||
|
||||
const std::string& GetLevel3CategoryName(Level3ProductCategory category);
|
||||
const std::string& GetLevel3CategoryDescription(Level3ProductCategory category);
|
||||
const std::string&
|
||||
GetLevel3CategoryDefaultProduct(Level3ProductCategory category);
|
||||
std::string
|
||||
GetLevel3CategoryDefaultProduct(Level3ProductCategory category,
|
||||
const Level3ProductCategoryMap& categoryMap);
|
||||
Level3ProductCategory GetLevel3Category(const std::string& categoryName);
|
||||
Level3ProductCategory
|
||||
GetLevel3CategoryByProduct(const std::string& productName);
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ static const std::unordered_map<int, std::string> level3ProductCodeMap_ {
|
|||
{153, "SDR"}, {154, "SDV"}, {159, "DZD"}, {161, "DCC"}, {163, "DKD"},
|
||||
{165, "DHC"}, {166, "ML"}, {169, "OHA"}, {170, "DAA"}, {172, "DTA"},
|
||||
{173, "DUA"}, {174, "DOD"}, {175, "DSD"}, {177, "HHC"}, {180, "TDR"},
|
||||
{182, "TDV"}};
|
||||
{182, "TDV"}, {186, "TZL"}};
|
||||
|
||||
static const std::unordered_map<std::string, std::string>
|
||||
level3ProductDescription_ {
|
||||
|
|
@ -84,6 +84,7 @@ static const std::unordered_map<std::string, std::string>
|
|||
{"HHC", "Hybrid Hydrometeor Classification"},
|
||||
{"TDR", "Digital Reflectivity"},
|
||||
{"TDV", "Digital Velocity"},
|
||||
{"TZL", "Long Range Reflectivity"},
|
||||
{"?", "Unknown"}};
|
||||
|
||||
static const std::unordered_map<std::string, std::vector<std::string>>
|
||||
|
|
@ -91,6 +92,7 @@ static const std::unordered_map<std::string, std::vector<std::string>>
|
|||
// Reflectivity
|
||||
{"SDR", {"NXB", "NYB", "NZB", "N0B", "NAB", "N1B", "NBB", "N2B", "N3B"}},
|
||||
{"DR", {"NXQ", "NYQ", "NZQ", "N0Q", "NAQ", "N1Q", "NBQ", "N2Q", "N3Q"}},
|
||||
{"TZL", {"TZL"}},
|
||||
{"TDR", {"TZ0", "TZ1", "TZ2"}},
|
||||
{"NCR", {"NCR"}},
|
||||
|
||||
|
|
@ -184,7 +186,7 @@ static const std::unordered_map<Level3ProductCategory, std::string>
|
|||
|
||||
static const std::unordered_map<Level3ProductCategory, std::vector<std::string>>
|
||||
level3CategoryProductList_ {
|
||||
{Level3ProductCategory::Reflectivity, {"SDR", "DR", "TDR", "NCR"}},
|
||||
{Level3ProductCategory::Reflectivity, {"SDR", "DR", "TZL", "TDR", "NCR"}},
|
||||
{Level3ProductCategory::Velocity, {"SDV", "DV", "TDV"}},
|
||||
{Level3ProductCategory::StormRelativeVelocity, {"SRM"}},
|
||||
{Level3ProductCategory::SpectrumWidth, {"SW"}},
|
||||
|
|
@ -296,9 +298,27 @@ const std::string& GetLevel3CategoryDescription(Level3ProductCategory category)
|
|||
return level3CategoryDescription_.at(category);
|
||||
}
|
||||
|
||||
const std::string&
|
||||
GetLevel3CategoryDefaultProduct(Level3ProductCategory category)
|
||||
std::string
|
||||
GetLevel3CategoryDefaultProduct(Level3ProductCategory category,
|
||||
const Level3ProductCategoryMap& categoryMap)
|
||||
{
|
||||
const auto& productsIt = categoryMap.find(category);
|
||||
if (productsIt == categoryMap.cend())
|
||||
{
|
||||
return level3CategoryDefaultAwipsId_.at(category);
|
||||
}
|
||||
|
||||
const auto& productsSiteHas = productsIt->second;
|
||||
const auto& productList = level3CategoryProductList_.at(category);
|
||||
for (auto& product : productList)
|
||||
{
|
||||
const auto& tiltsIt = productsSiteHas.find(product);
|
||||
if (tiltsIt != productsSiteHas.cend() && tiltsIt->second.size() > 0)
|
||||
{
|
||||
return tiltsIt->second[0];
|
||||
}
|
||||
}
|
||||
|
||||
return level3CategoryDefaultAwipsId_.at(category);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ static const std::unordered_map<int, unsigned int> rangeMap_ {
|
|||
{163, 300}, {165, 300}, {166, 230}, {167, 300}, {168, 300}, {169, 230},
|
||||
{170, 230}, {171, 230}, {172, 230}, {173, 230}, {174, 230}, {175, 230},
|
||||
{176, 230}, {177, 230}, {178, 300}, {179, 300}, {180, 89}, {181, 89},
|
||||
{182, 89}, {184, 89}, {186, 412}, {193, 460}, {195, 460}, {196, 50}};
|
||||
{182, 89}, {184, 89}, {186, 417}, {193, 460}, {195, 460}, {196, 50}};
|
||||
|
||||
static const std::unordered_map<int, unsigned int> xResolutionMap_ {
|
||||
{19, 1000}, {20, 2000}, {27, 1000}, {30, 1000}, {31, 2000}, {32, 1000},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue