mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 22:30:06 +00:00
Merge pull request #322 from dpaulat/feature/radar-smoothing
Radar Smoothing
This commit is contained in:
commit
bef8628bb6
27 changed files with 1184 additions and 347 deletions
|
|
@ -6,6 +6,7 @@ Checks:
|
||||||
- 'misc-*'
|
- 'misc-*'
|
||||||
- 'modernize-*'
|
- 'modernize-*'
|
||||||
- 'performance-*'
|
- 'performance-*'
|
||||||
|
- '-cppcoreguidelines-pro-type-reinterpret-cast'
|
||||||
- '-misc-include-cleaner'
|
- '-misc-include-cleaner'
|
||||||
- '-misc-non-private-member-variables-in-classes'
|
- '-misc-non-private-member-variables-in-classes'
|
||||||
- '-modernize-use-trailing-return-type'
|
- '-modernize-use-trailing-return-type'
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,14 @@ uniform float uDataMomentScale;
|
||||||
|
|
||||||
uniform bool uCFPEnabled;
|
uniform bool uCFPEnabled;
|
||||||
|
|
||||||
flat in uint dataMoment;
|
in float dataMoment;
|
||||||
flat in uint cfpMoment;
|
in float cfpMoment;
|
||||||
|
|
||||||
layout (location = 0) out vec4 fragColor;
|
layout (location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
float texCoord = float(dataMoment - uDataMomentOffset) / uDataMomentScale;
|
float texCoord = (dataMoment - float(uDataMomentOffset)) / uDataMomentScale;
|
||||||
|
|
||||||
if (uCFPEnabled && cfpMoment > 8u)
|
if (uCFPEnabled && cfpMoment > 8u)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ layout (location = 2) in uint aCfpMoment;
|
||||||
uniform mat4 uMVPMatrix;
|
uniform mat4 uMVPMatrix;
|
||||||
uniform vec2 uMapScreenCoord;
|
uniform vec2 uMapScreenCoord;
|
||||||
|
|
||||||
flat out uint dataMoment;
|
out float dataMoment;
|
||||||
flat out uint cfpMoment;
|
out float cfpMoment;
|
||||||
|
|
||||||
vec2 latLngToScreenCoordinate(in vec2 latLng)
|
vec2 latLngToScreenCoordinate(in vec2 latLng)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,8 @@ MainWindow::MainWindow(QWidget* parent) :
|
||||||
p->mapSettingsGroup_ = new ui::CollapsibleGroup(tr("Map Settings"), this);
|
p->mapSettingsGroup_ = new ui::CollapsibleGroup(tr("Map Settings"), this);
|
||||||
p->mapSettingsGroup_->GetContentsLayout()->addWidget(ui->mapStyleLabel);
|
p->mapSettingsGroup_->GetContentsLayout()->addWidget(ui->mapStyleLabel);
|
||||||
p->mapSettingsGroup_->GetContentsLayout()->addWidget(ui->mapStyleComboBox);
|
p->mapSettingsGroup_->GetContentsLayout()->addWidget(ui->mapStyleComboBox);
|
||||||
|
p->mapSettingsGroup_->GetContentsLayout()->addWidget(
|
||||||
|
ui->smoothRadarDataCheckBox);
|
||||||
p->mapSettingsGroup_->GetContentsLayout()->addWidget(
|
p->mapSettingsGroup_->GetContentsLayout()->addWidget(
|
||||||
ui->trackLocationCheckBox);
|
ui->trackLocationCheckBox);
|
||||||
ui->radarToolboxScrollAreaContents->layout()->replaceWidget(
|
ui->radarToolboxScrollAreaContents->layout()->replaceWidget(
|
||||||
|
|
@ -642,6 +644,11 @@ void MainWindow::on_actionDumpRadarProductRecords_triggered()
|
||||||
manager::RadarProductManager::DumpRecords();
|
manager::RadarProductManager::DumpRecords();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_actionRadarWireframe_triggered(bool checked)
|
||||||
|
{
|
||||||
|
p->activeMap_->SetRadarWireframeEnabled(checked);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionUserManual_triggered()
|
void MainWindow::on_actionUserManual_triggered()
|
||||||
{
|
{
|
||||||
QDesktopServices::openUrl(QUrl {"https://supercell-wx.readthedocs.io/"});
|
QDesktopServices::openUrl(QUrl {"https://supercell-wx.readthedocs.io/"});
|
||||||
|
|
@ -1085,6 +1092,25 @@ void MainWindowImpl::ConnectOtherSignals()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
connect(
|
||||||
|
mainWindow_->ui->smoothRadarDataCheckBox,
|
||||||
|
&QCheckBox::checkStateChanged,
|
||||||
|
mainWindow_,
|
||||||
|
[this](Qt::CheckState state)
|
||||||
|
{
|
||||||
|
const bool smoothingEnabled = (state == Qt::CheckState::Checked);
|
||||||
|
|
||||||
|
auto it = std::find(maps_.cbegin(), maps_.cend(), activeMap_);
|
||||||
|
if (it != maps_.cend())
|
||||||
|
{
|
||||||
|
const std::size_t i = std::distance(maps_.cbegin(), it);
|
||||||
|
settings::MapSettings::Instance().smoothing_enabled(i).StageValue(
|
||||||
|
smoothingEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn on smoothing
|
||||||
|
activeMap_->SetSmoothingEnabled(smoothingEnabled);
|
||||||
|
});
|
||||||
connect(mainWindow_->ui->trackLocationCheckBox,
|
connect(mainWindow_->ui->trackLocationCheckBox,
|
||||||
&QCheckBox::checkStateChanged,
|
&QCheckBox::checkStateChanged,
|
||||||
mainWindow_,
|
mainWindow_,
|
||||||
|
|
@ -1471,6 +1497,13 @@ void MainWindowImpl::UpdateRadarProductSettings()
|
||||||
{
|
{
|
||||||
level2SettingsGroup_->setVisible(false);
|
level2SettingsGroup_->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mainWindow_->ui->smoothRadarDataCheckBox->setCheckState(
|
||||||
|
activeMap_->GetSmoothingEnabled() ? Qt::CheckState::Checked :
|
||||||
|
Qt::CheckState::Unchecked);
|
||||||
|
|
||||||
|
mainWindow_->ui->actionRadarWireframe->setChecked(
|
||||||
|
activeMap_->GetRadarWireframeEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindowImpl::UpdateRadarSite()
|
void MainWindowImpl::UpdateRadarSite()
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ public:
|
||||||
void keyPressEvent(QKeyEvent* ev) override final;
|
void keyPressEvent(QKeyEvent* ev) override final;
|
||||||
void keyReleaseEvent(QKeyEvent* ev) override final;
|
void keyReleaseEvent(QKeyEvent* ev) override final;
|
||||||
void showEvent(QShowEvent* event) override;
|
void showEvent(QShowEvent* event) override;
|
||||||
void closeEvent(QCloseEvent *event) override;
|
void closeEvent(QCloseEvent* event) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void ActiveMapMoved(double latitude, double longitude);
|
void ActiveMapMoved(double latitude, double longitude);
|
||||||
|
|
@ -49,6 +49,7 @@ private slots:
|
||||||
void on_actionImGuiDebug_triggered();
|
void on_actionImGuiDebug_triggered();
|
||||||
void on_actionDumpLayerList_triggered();
|
void on_actionDumpLayerList_triggered();
|
||||||
void on_actionDumpRadarProductRecords_triggered();
|
void on_actionDumpRadarProductRecords_triggered();
|
||||||
|
void on_actionRadarWireframe_triggered(bool checked);
|
||||||
void on_actionUserManual_triggered();
|
void on_actionUserManual_triggered();
|
||||||
void on_actionDiscord_triggered();
|
void on_actionDiscord_triggered();
|
||||||
void on_actionGitHubRepository_triggered();
|
void on_actionGitHubRepository_triggered();
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,8 @@
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionDumpLayerList"/>
|
<addaction name="actionDumpLayerList"/>
|
||||||
<addaction name="actionDumpRadarProductRecords"/>
|
<addaction name="actionDumpRadarProductRecords"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionRadarWireframe"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menuTools">
|
<widget class="QMenu" name="menuTools">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
|
|
@ -153,8 +155,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>205</width>
|
<width>190</width>
|
||||||
<height>701</height>
|
<height>680</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||||
|
|
@ -329,6 +331,13 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QComboBox" name="mapStyleComboBox"/>
|
<widget class="QComboBox" name="mapStyleComboBox"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="smoothRadarDataCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Smooth Radar Data</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="trackLocationCheckBox">
|
<widget class="QCheckBox" name="trackLocationCheckBox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
@ -497,6 +506,14 @@
|
||||||
<string>Location &Marker Manager</string>
|
<string>Location &Marker Manager</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionRadarWireframe">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Radar &Wireframe</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../../scwx-qt.qrc"/>
|
<include location="../../../../scwx-qt.qrc"/>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
#include <scwx/util/threads.hpp>
|
#include <scwx/util/threads.hpp>
|
||||||
#include <scwx/wsr88d/nexrad_file_factory.hpp>
|
#include <scwx/wsr88d/nexrad_file_factory.hpp>
|
||||||
|
|
||||||
#include <deque>
|
|
||||||
#include <execution>
|
#include <execution>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
|
|
@ -28,6 +27,7 @@
|
||||||
#include <boost/timer/timer.hpp>
|
#include <boost/timer/timer.hpp>
|
||||||
#include <fmt/chrono.h>
|
#include <fmt/chrono.h>
|
||||||
#include <qmaplibre.hpp>
|
#include <qmaplibre.hpp>
|
||||||
|
#include <units/angle.h>
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
# pragma warning(pop)
|
# pragma warning(pop)
|
||||||
|
|
@ -63,6 +63,8 @@ static constexpr uint32_t NUM_COORIDNATES_1_DEGREE =
|
||||||
|
|
||||||
static const std::string kDefaultLevel3Product_ {"N0B"};
|
static const std::string kDefaultLevel3Product_ {"N0B"};
|
||||||
|
|
||||||
|
static constexpr std::size_t kTimerPlaces_ {6u};
|
||||||
|
|
||||||
static constexpr std::chrono::seconds kFastRetryInterval_ {15};
|
static constexpr std::chrono::seconds kFastRetryInterval_ {15};
|
||||||
static constexpr std::chrono::seconds kSlowRetryInterval_ {120};
|
static constexpr std::chrono::seconds kSlowRetryInterval_ {120};
|
||||||
|
|
||||||
|
|
@ -206,6 +208,13 @@ public:
|
||||||
|
|
||||||
void UpdateAvailableProductsSync();
|
void UpdateAvailableProductsSync();
|
||||||
|
|
||||||
|
void
|
||||||
|
CalculateCoordinates(const boost::integer_range<std::uint32_t>& radialGates,
|
||||||
|
const units::angle::degrees<float> radialAngle,
|
||||||
|
const units::angle::degrees<float> angleOffset,
|
||||||
|
const float gateRangeOffset,
|
||||||
|
std::vector<float>& outputCoordinates);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
PopulateProductTimes(std::shared_ptr<ProviderManager> providerManager,
|
PopulateProductTimes(std::shared_ptr<ProviderManager> providerManager,
|
||||||
RadarProductRecordMap& productRecordMap,
|
RadarProductRecordMap& productRecordMap,
|
||||||
|
|
@ -226,10 +235,12 @@ public:
|
||||||
std::size_t cacheLimit_ {6u};
|
std::size_t cacheLimit_ {6u};
|
||||||
|
|
||||||
std::vector<float> coordinates0_5Degree_ {};
|
std::vector<float> coordinates0_5Degree_ {};
|
||||||
|
std::vector<float> coordinates0_5DegreeSmooth_ {};
|
||||||
std::vector<float> coordinates1Degree_ {};
|
std::vector<float> coordinates1Degree_ {};
|
||||||
|
std::vector<float> coordinates1DegreeSmooth_ {};
|
||||||
|
|
||||||
RadarProductRecordMap level2ProductRecords_ {};
|
RadarProductRecordMap level2ProductRecords_ {};
|
||||||
RadarProductRecordList level2ProductRecentRecords_ {};
|
RadarProductRecordList level2ProductRecentRecords_ {};
|
||||||
std::unordered_map<std::string, RadarProductRecordMap>
|
std::unordered_map<std::string, RadarProductRecordMap>
|
||||||
level3ProductRecordsMap_ {};
|
level3ProductRecordsMap_ {};
|
||||||
std::unordered_map<std::string, RadarProductRecordList>
|
std::unordered_map<std::string, RadarProductRecordList>
|
||||||
|
|
@ -361,14 +372,29 @@ void RadarProductManager::DumpRecords()
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<float>&
|
const std::vector<float>&
|
||||||
RadarProductManager::coordinates(common::RadialSize radialSize) const
|
RadarProductManager::coordinates(common::RadialSize radialSize,
|
||||||
|
bool smoothingEnabled) const
|
||||||
{
|
{
|
||||||
switch (radialSize)
|
switch (radialSize)
|
||||||
{
|
{
|
||||||
case common::RadialSize::_0_5Degree:
|
case common::RadialSize::_0_5Degree:
|
||||||
return p->coordinates0_5Degree_;
|
if (smoothingEnabled)
|
||||||
|
{
|
||||||
|
return p->coordinates0_5DegreeSmooth_;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return p->coordinates0_5Degree_;
|
||||||
|
}
|
||||||
case common::RadialSize::_1Degree:
|
case common::RadialSize::_1Degree:
|
||||||
return p->coordinates1Degree_;
|
if (smoothingEnabled)
|
||||||
|
{
|
||||||
|
return p->coordinates1DegreeSmooth_;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return p->coordinates1Degree_;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw std::invalid_argument("Invalid radial size");
|
throw std::invalid_argument("Invalid radial size");
|
||||||
}
|
}
|
||||||
|
|
@ -430,50 +456,51 @@ void RadarProductManager::Initialize()
|
||||||
|
|
||||||
boost::timer::cpu_timer timer;
|
boost::timer::cpu_timer timer;
|
||||||
|
|
||||||
const GeographicLib::Geodesic& geodesic(
|
|
||||||
util::GeographicLib::DefaultGeodesic());
|
|
||||||
|
|
||||||
const QMapLibre::Coordinate radar(p->radarSite_->latitude(),
|
|
||||||
p->radarSite_->longitude());
|
|
||||||
|
|
||||||
const float gateSize = gate_size();
|
|
||||||
|
|
||||||
// Calculate half degree azimuth coordinates
|
// Calculate half degree azimuth coordinates
|
||||||
timer.start();
|
timer.start();
|
||||||
std::vector<float>& coordinates0_5Degree = p->coordinates0_5Degree_;
|
std::vector<float>& coordinates0_5Degree = p->coordinates0_5Degree_;
|
||||||
|
|
||||||
coordinates0_5Degree.resize(NUM_COORIDNATES_0_5_DEGREE);
|
coordinates0_5Degree.resize(NUM_COORIDNATES_0_5_DEGREE);
|
||||||
|
|
||||||
auto radialGates0_5Degree =
|
const auto radialGates0_5Degree =
|
||||||
boost::irange<uint32_t>(0, NUM_RADIAL_GATES_0_5_DEGREE);
|
boost::irange<uint32_t>(0, NUM_RADIAL_GATES_0_5_DEGREE);
|
||||||
|
|
||||||
std::for_each(
|
// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers): Values are given
|
||||||
std::execution::par_unseq,
|
// descriptions
|
||||||
radialGates0_5Degree.begin(),
|
p->CalculateCoordinates(
|
||||||
radialGates0_5Degree.end(),
|
radialGates0_5Degree,
|
||||||
[&](uint32_t radialGate)
|
units::angle::degrees<float> {0.5f}, // Radial angle
|
||||||
{
|
units::angle::degrees<float> {0.0f}, // Angle offset
|
||||||
const uint16_t gate =
|
// Far end of the first gate is the gate size distance from the radar site
|
||||||
static_cast<uint16_t>(radialGate % common::MAX_DATA_MOMENT_GATES);
|
1.0f,
|
||||||
const uint16_t radial =
|
coordinates0_5Degree);
|
||||||
static_cast<uint16_t>(radialGate / common::MAX_DATA_MOMENT_GATES);
|
// NOLINTEND(cppcoreguidelines-avoid-magic-numbers)
|
||||||
|
|
||||||
const float angle = radial * 0.5f; // 0.5 degree radial
|
|
||||||
const float range = (gate + 1) * gateSize;
|
|
||||||
const size_t offset = radialGate * 2;
|
|
||||||
|
|
||||||
double latitude;
|
|
||||||
double longitude;
|
|
||||||
|
|
||||||
geodesic.Direct(
|
|
||||||
radar.first, radar.second, angle, range, latitude, longitude);
|
|
||||||
|
|
||||||
coordinates0_5Degree[offset] = latitude;
|
|
||||||
coordinates0_5Degree[offset + 1] = longitude;
|
|
||||||
});
|
|
||||||
timer.stop();
|
timer.stop();
|
||||||
logger_->debug("Coordinates (0.5 degree) calculated in {}",
|
logger_->debug("Coordinates (0.5 degree) calculated in {}",
|
||||||
timer.format(6, "%ws"));
|
timer.format(kTimerPlaces_, "%ws"));
|
||||||
|
|
||||||
|
// Calculate half degree smooth azimuth coordinates
|
||||||
|
timer.start();
|
||||||
|
std::vector<float>& coordinates0_5DegreeSmooth =
|
||||||
|
p->coordinates0_5DegreeSmooth_;
|
||||||
|
|
||||||
|
coordinates0_5DegreeSmooth.resize(NUM_COORIDNATES_0_5_DEGREE);
|
||||||
|
|
||||||
|
// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers): Values are given
|
||||||
|
// descriptions
|
||||||
|
p->CalculateCoordinates(radialGates0_5Degree,
|
||||||
|
units::angle::degrees<float> {0.5f}, // Radial angle
|
||||||
|
units::angle::degrees<float> {0.25f}, // Angle offset
|
||||||
|
// Center of the first gate is half the gate size
|
||||||
|
// distance from the radar site
|
||||||
|
0.5f,
|
||||||
|
coordinates0_5DegreeSmooth);
|
||||||
|
// NOLINTEND(cppcoreguidelines-avoid-magic-numbers)
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
logger_->debug("Coordinates (0.5 degree smooth) calculated in {}",
|
||||||
|
timer.format(kTimerPlaces_, "%ws"));
|
||||||
|
|
||||||
// Calculate 1 degree azimuth coordinates
|
// Calculate 1 degree azimuth coordinates
|
||||||
timer.start();
|
timer.start();
|
||||||
|
|
@ -481,38 +508,89 @@ void RadarProductManager::Initialize()
|
||||||
|
|
||||||
coordinates1Degree.resize(NUM_COORIDNATES_1_DEGREE);
|
coordinates1Degree.resize(NUM_COORIDNATES_1_DEGREE);
|
||||||
|
|
||||||
auto radialGates1Degree =
|
const auto radialGates1Degree =
|
||||||
boost::irange<uint32_t>(0, NUM_RADIAL_GATES_1_DEGREE);
|
boost::irange<uint32_t>(0, NUM_RADIAL_GATES_1_DEGREE);
|
||||||
|
|
||||||
|
// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers): Values are given
|
||||||
|
// descriptions
|
||||||
|
p->CalculateCoordinates(
|
||||||
|
radialGates1Degree,
|
||||||
|
units::angle::degrees<float> {1.0f}, // Radial angle
|
||||||
|
units::angle::degrees<float> {0.0f}, // Angle offset
|
||||||
|
// Far end of the first gate is the gate size distance from the radar site
|
||||||
|
1.0f,
|
||||||
|
coordinates1Degree);
|
||||||
|
// NOLINTEND(cppcoreguidelines-avoid-magic-numbers)
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
logger_->debug("Coordinates (1 degree) calculated in {}",
|
||||||
|
timer.format(kTimerPlaces_, "%ws"));
|
||||||
|
|
||||||
|
// Calculate 1 degree smooth azimuth coordinates
|
||||||
|
timer.start();
|
||||||
|
std::vector<float>& coordinates1DegreeSmooth = p->coordinates1DegreeSmooth_;
|
||||||
|
|
||||||
|
coordinates1DegreeSmooth.resize(NUM_COORIDNATES_1_DEGREE);
|
||||||
|
|
||||||
|
// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers): Values are given
|
||||||
|
// descriptions
|
||||||
|
p->CalculateCoordinates(radialGates1Degree,
|
||||||
|
units::angle::degrees<float> {1.0f}, // Radial angle
|
||||||
|
units::angle::degrees<float> {0.5f}, // Angle offset
|
||||||
|
// Center of the first gate is half the gate size
|
||||||
|
// distance from the radar site
|
||||||
|
0.5f,
|
||||||
|
coordinates1DegreeSmooth);
|
||||||
|
// NOLINTEND(cppcoreguidelines-avoid-magic-numbers)
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
logger_->debug("Coordinates (1 degree smooth) calculated in {}",
|
||||||
|
timer.format(kTimerPlaces_, "%ws"));
|
||||||
|
|
||||||
|
p->initialized_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RadarProductManagerImpl::CalculateCoordinates(
|
||||||
|
const boost::integer_range<std::uint32_t>& radialGates,
|
||||||
|
const units::angle::degrees<float> radialAngle,
|
||||||
|
const units::angle::degrees<float> angleOffset,
|
||||||
|
const float gateRangeOffset,
|
||||||
|
std::vector<float>& outputCoordinates)
|
||||||
|
{
|
||||||
|
const GeographicLib::Geodesic& geodesic(
|
||||||
|
util::GeographicLib::DefaultGeodesic());
|
||||||
|
|
||||||
|
const QMapLibre::Coordinate radar(radarSite_->latitude(),
|
||||||
|
radarSite_->longitude());
|
||||||
|
|
||||||
|
const float gateSize = self_->gate_size();
|
||||||
|
|
||||||
std::for_each(
|
std::for_each(
|
||||||
std::execution::par_unseq,
|
std::execution::par_unseq,
|
||||||
radialGates1Degree.begin(),
|
radialGates.begin(),
|
||||||
radialGates1Degree.end(),
|
radialGates.end(),
|
||||||
[&](uint32_t radialGate)
|
[&](uint32_t radialGate)
|
||||||
{
|
{
|
||||||
const uint16_t gate =
|
const auto gate = static_cast<std::uint16_t>(
|
||||||
static_cast<uint16_t>(radialGate % common::MAX_DATA_MOMENT_GATES);
|
radialGate % common::MAX_DATA_MOMENT_GATES);
|
||||||
const uint16_t radial =
|
const auto radial = static_cast<std::uint16_t>(
|
||||||
static_cast<uint16_t>(radialGate / common::MAX_DATA_MOMENT_GATES);
|
radialGate / common::MAX_DATA_MOMENT_GATES);
|
||||||
|
|
||||||
const float angle = radial * 1.0f; // 1 degree radial
|
const float angle = static_cast<float>(radial) * radialAngle.value() +
|
||||||
const float range = (gate + 1) * gateSize;
|
angleOffset.value();
|
||||||
const size_t offset = radialGate * 2;
|
const float range =
|
||||||
|
(static_cast<float>(gate) + gateRangeOffset) * gateSize;
|
||||||
|
const std::size_t offset = static_cast<std::size_t>(radialGate) * 2;
|
||||||
|
|
||||||
double latitude;
|
double latitude = 0.0;
|
||||||
double longitude;
|
double longitude = 0.0;
|
||||||
|
|
||||||
geodesic.Direct(
|
geodesic.Direct(
|
||||||
radar.first, radar.second, angle, range, latitude, longitude);
|
radar.first, radar.second, angle, range, latitude, longitude);
|
||||||
|
|
||||||
coordinates1Degree[offset] = latitude;
|
outputCoordinates[offset] = static_cast<float>(latitude);
|
||||||
coordinates1Degree[offset + 1] = longitude;
|
outputCoordinates[offset + 1] = static_cast<float>(longitude);
|
||||||
});
|
});
|
||||||
timer.stop();
|
|
||||||
logger_->debug("Coordinates (1 degree) calculated in {}",
|
|
||||||
timer.format(6, "%ws"));
|
|
||||||
|
|
||||||
p->initialized_ = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<ProviderManager>
|
std::shared_ptr<ProviderManager>
|
||||||
|
|
|
||||||
|
|
@ -41,11 +41,12 @@ public:
|
||||||
*/
|
*/
|
||||||
static void DumpRecords();
|
static void DumpRecords();
|
||||||
|
|
||||||
const std::vector<float>& coordinates(common::RadialSize radialSize) const;
|
[[nodiscard]] const std::vector<float>&
|
||||||
const scwx::util::time_zone* default_time_zone() const;
|
coordinates(common::RadialSize radialSize, bool smoothingEnabled) const;
|
||||||
float gate_size() const;
|
[[nodiscard]] const scwx::util::time_zone* default_time_zone() const;
|
||||||
std::string radar_id() const;
|
[[nodiscard]] float gate_size() const;
|
||||||
std::shared_ptr<config::RadarSite> radar_site() const;
|
[[nodiscard]] std::string radar_id() const;
|
||||||
|
[[nodiscard]] std::shared_ptr<config::RadarSite> radar_site() const;
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,16 +9,17 @@ namespace map
|
||||||
|
|
||||||
struct MapSettings
|
struct MapSettings
|
||||||
{
|
{
|
||||||
explicit MapSettings() : isActive_ {false} {}
|
explicit MapSettings() = default;
|
||||||
~MapSettings() = default;
|
~MapSettings() = default;
|
||||||
|
|
||||||
MapSettings(const MapSettings&) = delete;
|
MapSettings(const MapSettings&) = delete;
|
||||||
MapSettings& operator=(const MapSettings&) = delete;
|
MapSettings& operator=(const MapSettings&) = delete;
|
||||||
|
|
||||||
MapSettings(MapSettings&&) noexcept = default;
|
MapSettings(MapSettings&&) noexcept = default;
|
||||||
MapSettings& operator=(MapSettings&&) noexcept = default;
|
MapSettings& operator=(MapSettings&&) noexcept = default;
|
||||||
|
|
||||||
bool isActive_;
|
bool isActive_ {false};
|
||||||
|
bool radarWireframeEnabled_ {false};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace map
|
} // namespace map
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
#include <scwx/qt/model/imgui_context_model.hpp>
|
#include <scwx/qt/model/imgui_context_model.hpp>
|
||||||
#include <scwx/qt/model/layer_model.hpp>
|
#include <scwx/qt/model/layer_model.hpp>
|
||||||
#include <scwx/qt/settings/general_settings.hpp>
|
#include <scwx/qt/settings/general_settings.hpp>
|
||||||
|
#include <scwx/qt/settings/map_settings.hpp>
|
||||||
#include <scwx/qt/settings/palette_settings.hpp>
|
#include <scwx/qt/settings/palette_settings.hpp>
|
||||||
#include <scwx/qt/util/file.hpp>
|
#include <scwx/qt/util/file.hpp>
|
||||||
#include <scwx/qt/util/maplibre.hpp>
|
#include <scwx/qt/util/maplibre.hpp>
|
||||||
|
|
@ -105,13 +106,16 @@ public:
|
||||||
map::AlertLayer::InitializeHandler();
|
map::AlertLayer::InitializeHandler();
|
||||||
|
|
||||||
auto& generalSettings = settings::GeneralSettings::Instance();
|
auto& generalSettings = settings::GeneralSettings::Instance();
|
||||||
|
auto& mapSettings = settings::MapSettings::Instance();
|
||||||
|
|
||||||
// Initialize context
|
// Initialize context
|
||||||
context_->set_map_provider(
|
context_->set_map_provider(
|
||||||
GetMapProvider(generalSettings.map_provider().GetValue()));
|
GetMapProvider(generalSettings.map_provider().GetValue()));
|
||||||
context_->set_overlay_product_view(overlayProductView);
|
context_->set_overlay_product_view(overlayProductView);
|
||||||
|
|
||||||
|
// Initialize map data
|
||||||
SetRadarSite(generalSettings.default_radar_site().GetValue());
|
SetRadarSite(generalSettings.default_radar_site().GetValue());
|
||||||
|
smoothingEnabled_ = mapSettings.smoothing_enabled(id).GetValue();
|
||||||
|
|
||||||
// Create ImGui Context
|
// Create ImGui Context
|
||||||
static size_t currentMapId_ {0u};
|
static size_t currentMapId_ {0u};
|
||||||
|
|
@ -225,7 +229,7 @@ public:
|
||||||
std::shared_ptr<OverlayLayer> overlayLayer_;
|
std::shared_ptr<OverlayLayer> overlayLayer_;
|
||||||
std::shared_ptr<OverlayProductLayer> overlayProductLayer_ {nullptr};
|
std::shared_ptr<OverlayProductLayer> overlayProductLayer_ {nullptr};
|
||||||
std::shared_ptr<PlacefileLayer> placefileLayer_;
|
std::shared_ptr<PlacefileLayer> placefileLayer_;
|
||||||
std::shared_ptr<MarkerLayer> markerLayer_;
|
std::shared_ptr<MarkerLayer> markerLayer_;
|
||||||
std::shared_ptr<ColorTableLayer> colorTableLayer_;
|
std::shared_ptr<ColorTableLayer> colorTableLayer_;
|
||||||
std::shared_ptr<RadarSiteLayer> radarSiteLayer_ {nullptr};
|
std::shared_ptr<RadarSiteLayer> radarSiteLayer_ {nullptr};
|
||||||
|
|
||||||
|
|
@ -233,6 +237,7 @@ public:
|
||||||
|
|
||||||
bool autoRefreshEnabled_;
|
bool autoRefreshEnabled_;
|
||||||
bool autoUpdateEnabled_;
|
bool autoUpdateEnabled_;
|
||||||
|
bool smoothingEnabled_ {false};
|
||||||
|
|
||||||
common::Level2Product selectedLevel2Product_;
|
common::Level2Product selectedLevel2Product_;
|
||||||
|
|
||||||
|
|
@ -727,6 +732,35 @@ std::uint16_t MapWidget::GetVcp() const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MapWidget::GetRadarWireframeEnabled() const
|
||||||
|
{
|
||||||
|
return p->context_->settings().radarWireframeEnabled_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapWidget::SetRadarWireframeEnabled(bool wireframeEnabled)
|
||||||
|
{
|
||||||
|
p->context_->settings().radarWireframeEnabled_ = wireframeEnabled;
|
||||||
|
QMetaObject::invokeMethod(
|
||||||
|
this, static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MapWidget::GetSmoothingEnabled() const
|
||||||
|
{
|
||||||
|
return p->smoothingEnabled_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapWidget::SetSmoothingEnabled(bool smoothingEnabled)
|
||||||
|
{
|
||||||
|
p->smoothingEnabled_ = smoothingEnabled;
|
||||||
|
|
||||||
|
auto radarProductView = p->context_->radar_product_view();
|
||||||
|
if (radarProductView != nullptr)
|
||||||
|
{
|
||||||
|
radarProductView->set_smoothing_enabled(smoothingEnabled);
|
||||||
|
radarProductView->Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MapWidget::SelectElevation(float elevation)
|
void MapWidget::SelectElevation(float elevation)
|
||||||
{
|
{
|
||||||
auto radarProductView = p->context_->radar_product_view();
|
auto radarProductView = p->context_->radar_product_view();
|
||||||
|
|
@ -775,6 +809,7 @@ void MapWidget::SelectRadarProduct(common::RadarProductGroup group,
|
||||||
|
|
||||||
radarProductView = view::RadarProductViewFactory::Create(
|
radarProductView = view::RadarProductViewFactory::Create(
|
||||||
group, productName, productCode, p->radarProductManager_);
|
group, productName, productCode, p->radarProductManager_);
|
||||||
|
radarProductView->set_smoothing_enabled(p->smoothingEnabled_);
|
||||||
p->context_->set_radar_product_view(radarProductView);
|
p->context_->set_radar_product_view(radarProductView);
|
||||||
|
|
||||||
p->RadarProductViewConnect();
|
p->RadarProductViewConnect();
|
||||||
|
|
|
||||||
|
|
@ -39,16 +39,19 @@ public:
|
||||||
|
|
||||||
void DumpLayerList() const;
|
void DumpLayerList() const;
|
||||||
|
|
||||||
common::Level3ProductCategoryMap GetAvailableLevel3Categories();
|
[[nodiscard]] common::Level3ProductCategoryMap
|
||||||
float GetElevation() const;
|
GetAvailableLevel3Categories();
|
||||||
std::vector<float> GetElevationCuts() const;
|
[[nodiscard]] float GetElevation() const;
|
||||||
std::vector<std::string> GetLevel3Products();
|
[[nodiscard]] std::vector<float> GetElevationCuts() const;
|
||||||
std::string GetMapStyle() const;
|
[[nodiscard]] std::vector<std::string> GetLevel3Products();
|
||||||
common::RadarProductGroup GetRadarProductGroup() const;
|
[[nodiscard]] std::string GetMapStyle() const;
|
||||||
std::string GetRadarProductName() const;
|
[[nodiscard]] common::RadarProductGroup GetRadarProductGroup() const;
|
||||||
std::shared_ptr<config::RadarSite> GetRadarSite() const;
|
[[nodiscard]] std::string GetRadarProductName() const;
|
||||||
std::chrono::system_clock::time_point GetSelectedTime() const;
|
[[nodiscard]] std::shared_ptr<config::RadarSite> GetRadarSite() const;
|
||||||
std::uint16_t GetVcp() const;
|
[[nodiscard]] bool GetRadarWireframeEnabled() const;
|
||||||
|
[[nodiscard]] std::chrono::system_clock::time_point GetSelectedTime() const;
|
||||||
|
[[nodiscard]] bool GetSmoothingEnabled() const;
|
||||||
|
[[nodiscard]] std::uint16_t GetVcp() const;
|
||||||
|
|
||||||
void SelectElevation(float elevation);
|
void SelectElevation(float elevation);
|
||||||
|
|
||||||
|
|
@ -117,6 +120,8 @@ public:
|
||||||
double pitch);
|
double pitch);
|
||||||
void SetInitialMapStyle(const std::string& styleName);
|
void SetInitialMapStyle(const std::string& styleName);
|
||||||
void SetMapStyle(const std::string& styleName);
|
void SetMapStyle(const std::string& styleName);
|
||||||
|
void SetRadarWireframeEnabled(bool enabled);
|
||||||
|
void SetSmoothingEnabled(bool enabled);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the coordinates associated with mouse movement from another map.
|
* Updates the coordinates associated with mouse movement from another map.
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
#include <scwx/qt/map/radar_product_layer.hpp>
|
#include <scwx/qt/map/radar_product_layer.hpp>
|
||||||
|
#include <scwx/qt/map/map_settings.hpp>
|
||||||
#include <scwx/qt/gl/shader_program.hpp>
|
#include <scwx/qt/gl/shader_program.hpp>
|
||||||
#include <scwx/qt/util/maplibre.hpp>
|
#include <scwx/qt/util/maplibre.hpp>
|
||||||
#include <scwx/qt/util/tooltip.hpp>
|
#include <scwx/qt/util/tooltip.hpp>
|
||||||
#include <scwx/qt/view/radar_product_view.hpp>
|
#include <scwx/qt/view/radar_product_view.hpp>
|
||||||
#include <scwx/util/logger.hpp>
|
#include <scwx/util/logger.hpp>
|
||||||
|
|
||||||
#include <execution>
|
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
# pragma warning(push, 0)
|
# pragma warning(push, 0)
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -267,6 +266,13 @@ void RadarProductLayer::Render(
|
||||||
// Set OpenGL blend mode for transparency
|
// Set OpenGL blend mode for transparency
|
||||||
gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
const bool wireframeEnabled = context()->settings().radarWireframeEnabled_;
|
||||||
|
if (wireframeEnabled)
|
||||||
|
{
|
||||||
|
// Set polygon mode to draw wireframe
|
||||||
|
gl.glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||||
|
}
|
||||||
|
|
||||||
if (p->colorTableNeedsUpdate_)
|
if (p->colorTableNeedsUpdate_)
|
||||||
{
|
{
|
||||||
UpdateColorTable();
|
UpdateColorTable();
|
||||||
|
|
@ -303,6 +309,12 @@ void RadarProductLayer::Render(
|
||||||
gl.glBindVertexArray(p->vao_);
|
gl.glBindVertexArray(p->vao_);
|
||||||
gl.glDrawArrays(GL_TRIANGLES, 0, p->numVertices_);
|
gl.glDrawArrays(GL_TRIANGLES, 0, p->numVertices_);
|
||||||
|
|
||||||
|
if (wireframeEnabled)
|
||||||
|
{
|
||||||
|
// Restore polygon mode to default
|
||||||
|
gl.glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||||
|
}
|
||||||
|
|
||||||
SCWX_GL_CHECK_ERROR();
|
SCWX_GL_CHECK_ERROR();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
#include <scwx/util/logger.hpp>
|
#include <scwx/util/logger.hpp>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <execution>
|
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
|
|
@ -27,12 +26,15 @@ static const std::string kMapStyleName_ {"map_style"};
|
||||||
static const std::string kRadarSiteName_ {"radar_site"};
|
static const std::string kRadarSiteName_ {"radar_site"};
|
||||||
static const std::string kRadarProductGroupName_ {"radar_product_group"};
|
static const std::string kRadarProductGroupName_ {"radar_product_group"};
|
||||||
static const std::string kRadarProductName_ {"radar_product"};
|
static const std::string kRadarProductName_ {"radar_product"};
|
||||||
|
static const std::string kSmoothingEnabledName_ {"smoothing_enabled"};
|
||||||
|
|
||||||
static const std::string kDefaultMapStyle_ {"?"};
|
static const std::string kDefaultMapStyle_ {"?"};
|
||||||
static const std::string kDefaultRadarProductGroupString_ = "L3";
|
static const std::string kDefaultRadarProductGroupString_ = "L3";
|
||||||
static const std::array<std::string, kCount_> kDefaultRadarProduct_ {
|
static const std::array<std::string, kCount_> kDefaultRadarProduct_ {
|
||||||
"N0B", "N0G", "N0C", "N0X"};
|
"N0B", "N0G", "N0C", "N0X"};
|
||||||
|
|
||||||
|
static constexpr bool kDefaultSmoothingEnabled_ {false};
|
||||||
|
|
||||||
class MapSettings::Impl
|
class MapSettings::Impl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -43,26 +45,28 @@ public:
|
||||||
SettingsVariable<std::string> radarProductGroup_ {
|
SettingsVariable<std::string> radarProductGroup_ {
|
||||||
kRadarProductGroupName_};
|
kRadarProductGroupName_};
|
||||||
SettingsVariable<std::string> radarProduct_ {kRadarProductName_};
|
SettingsVariable<std::string> radarProduct_ {kRadarProductName_};
|
||||||
|
SettingsVariable<bool> smoothingEnabled_ {kSmoothingEnabledName_};
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit Impl()
|
explicit Impl()
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < kCount_; i++)
|
for (std::size_t i = 0; i < kCount_; i++)
|
||||||
{
|
{
|
||||||
map_[i].mapStyle_.SetDefault(kDefaultMapStyle_);
|
map_.at(i).mapStyle_.SetDefault(kDefaultMapStyle_);
|
||||||
map_[i].radarSite_.SetDefault(kDefaultRadarSite_);
|
map_.at(i).radarSite_.SetDefault(kDefaultRadarSite_);
|
||||||
map_[i].radarProductGroup_.SetDefault(
|
map_.at(i).radarProductGroup_.SetDefault(
|
||||||
kDefaultRadarProductGroupString_);
|
kDefaultRadarProductGroupString_);
|
||||||
map_[i].radarProduct_.SetDefault(kDefaultRadarProduct_[i]);
|
map_.at(i).radarProduct_.SetDefault(kDefaultRadarProduct_.at(i));
|
||||||
|
map_.at(i).smoothingEnabled_.SetDefault(kDefaultSmoothingEnabled_);
|
||||||
|
|
||||||
map_[i].radarSite_.SetValidator(
|
map_.at(i).radarSite_.SetValidator(
|
||||||
[](const std::string& value)
|
[](const std::string& value)
|
||||||
{
|
{
|
||||||
// Radar site must exist
|
// Radar site must exist
|
||||||
return config::RadarSite::Get(value) != nullptr;
|
return config::RadarSite::Get(value) != nullptr;
|
||||||
});
|
});
|
||||||
|
|
||||||
map_[i].radarProductGroup_.SetValidator(
|
map_.at(i).radarProductGroup_.SetValidator(
|
||||||
[](const std::string& value)
|
[](const std::string& value)
|
||||||
{
|
{
|
||||||
// Radar product group must be valid
|
// Radar product group must be valid
|
||||||
|
|
@ -71,12 +75,12 @@ public:
|
||||||
return radarProductGroup != common::RadarProductGroup::Unknown;
|
return radarProductGroup != common::RadarProductGroup::Unknown;
|
||||||
});
|
});
|
||||||
|
|
||||||
map_[i].radarProduct_.SetValidator(
|
map_.at(i).radarProduct_.SetValidator(
|
||||||
[this, i](const std::string& value)
|
[this, i](const std::string& value)
|
||||||
{
|
{
|
||||||
common::RadarProductGroup radarProductGroup =
|
common::RadarProductGroup radarProductGroup =
|
||||||
common::GetRadarProductGroup(
|
common::GetRadarProductGroup(
|
||||||
map_[i].radarProductGroup_.GetValue());
|
map_.at(i).radarProductGroup_.GetValue());
|
||||||
|
|
||||||
if (radarProductGroup == common::RadarProductGroup::Level2)
|
if (radarProductGroup == common::RadarProductGroup::Level2)
|
||||||
{
|
{
|
||||||
|
|
@ -92,10 +96,11 @@ public:
|
||||||
});
|
});
|
||||||
|
|
||||||
variables_.insert(variables_.cend(),
|
variables_.insert(variables_.cend(),
|
||||||
{&map_[i].mapStyle_,
|
{&map_.at(i).mapStyle_,
|
||||||
&map_[i].radarSite_,
|
&map_.at(i).radarSite_,
|
||||||
&map_[i].radarProductGroup_,
|
&map_.at(i).radarProductGroup_,
|
||||||
&map_[i].radarProduct_});
|
&map_.at(i).radarProduct_,
|
||||||
|
&map_.at(i).smoothingEnabled_});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,10 +108,11 @@ public:
|
||||||
|
|
||||||
void SetDefaults(std::size_t i)
|
void SetDefaults(std::size_t i)
|
||||||
{
|
{
|
||||||
map_[i].mapStyle_.SetValueToDefault();
|
map_.at(i).mapStyle_.SetValueToDefault();
|
||||||
map_[i].radarSite_.SetValueToDefault();
|
map_.at(i).radarSite_.SetValueToDefault();
|
||||||
map_[i].radarProductGroup_.SetValueToDefault();
|
map_.at(i).radarProductGroup_.SetValueToDefault();
|
||||||
map_[i].radarProduct_.SetValueToDefault();
|
map_.at(i).radarProduct_.SetValueToDefault();
|
||||||
|
map_.at(i).smoothingEnabled_.SetValueToDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
friend void tag_invoke(boost::json::value_from_tag,
|
friend void tag_invoke(boost::json::value_from_tag,
|
||||||
|
|
@ -116,7 +122,8 @@ public:
|
||||||
jv = {{kMapStyleName_, data.mapStyle_.GetValue()},
|
jv = {{kMapStyleName_, data.mapStyle_.GetValue()},
|
||||||
{kRadarSiteName_, data.radarSite_.GetValue()},
|
{kRadarSiteName_, data.radarSite_.GetValue()},
|
||||||
{kRadarProductGroupName_, data.radarProductGroup_.GetValue()},
|
{kRadarProductGroupName_, data.radarProductGroup_.GetValue()},
|
||||||
{kRadarProductName_, data.radarProduct_.GetValue()}};
|
{kRadarProductName_, data.radarProduct_.GetValue()},
|
||||||
|
{kSmoothingEnabledName_, data.smoothingEnabled_.GetValue()}};
|
||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator==(const MapData& lhs, const MapData& rhs)
|
friend bool operator==(const MapData& lhs, const MapData& rhs)
|
||||||
|
|
@ -124,7 +131,8 @@ public:
|
||||||
return (lhs.mapStyle_ == rhs.mapStyle_ && //
|
return (lhs.mapStyle_ == rhs.mapStyle_ && //
|
||||||
lhs.radarSite_ == rhs.radarSite_ &&
|
lhs.radarSite_ == rhs.radarSite_ &&
|
||||||
lhs.radarProductGroup_ == rhs.radarProductGroup_ &&
|
lhs.radarProductGroup_ == rhs.radarProductGroup_ &&
|
||||||
lhs.radarProduct_ == rhs.radarProduct_);
|
lhs.radarProduct_ == rhs.radarProduct_ &&
|
||||||
|
lhs.smoothingEnabled_ == rhs.smoothingEnabled_);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<MapData, kCount_> map_ {};
|
std::array<MapData, kCount_> map_ {};
|
||||||
|
|
@ -149,25 +157,29 @@ std::size_t MapSettings::count() const
|
||||||
return kCount_;
|
return kCount_;
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsVariable<std::string>& MapSettings::map_style(std::size_t i) const
|
SettingsVariable<std::string>& MapSettings::map_style(std::size_t i)
|
||||||
{
|
{
|
||||||
return p->map_[i].mapStyle_;
|
return p->map_.at(i).mapStyle_;
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsVariable<std::string>& MapSettings::radar_site(std::size_t i) const
|
SettingsVariable<std::string>& MapSettings::radar_site(std::size_t i)
|
||||||
{
|
{
|
||||||
return p->map_[i].radarSite_;
|
return p->map_.at(i).radarSite_;
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsVariable<std::string>&
|
SettingsVariable<std::string>& MapSettings::radar_product_group(std::size_t i)
|
||||||
MapSettings::radar_product_group(std::size_t i) const
|
|
||||||
{
|
{
|
||||||
return p->map_[i].radarProductGroup_;
|
return p->map_.at(i).radarProductGroup_;
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsVariable<std::string>& MapSettings::radar_product(std::size_t i) const
|
SettingsVariable<std::string>& MapSettings::radar_product(std::size_t i)
|
||||||
{
|
{
|
||||||
return p->map_[i].radarProduct_;
|
return p->map_.at(i).radarProduct_;
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsVariable<bool>& MapSettings::smoothing_enabled(std::size_t i)
|
||||||
|
{
|
||||||
|
return p->map_.at(i).smoothingEnabled_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MapSettings::Shutdown()
|
bool MapSettings::Shutdown()
|
||||||
|
|
@ -177,9 +189,10 @@ bool MapSettings::Shutdown()
|
||||||
// Commit settings that are managed separate from the settings dialog
|
// Commit settings that are managed separate from the settings dialog
|
||||||
for (std::size_t i = 0; i < kCount_; ++i)
|
for (std::size_t i = 0; i < kCount_; ++i)
|
||||||
{
|
{
|
||||||
Impl::MapData& mapRecordSettings = p->map_[i];
|
Impl::MapData& mapRecordSettings = p->map_.at(i);
|
||||||
|
|
||||||
dataChanged |= mapRecordSettings.mapStyle_.Commit();
|
dataChanged |= mapRecordSettings.mapStyle_.Commit();
|
||||||
|
dataChanged |= mapRecordSettings.smoothingEnabled_.Commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
return dataChanged;
|
return dataChanged;
|
||||||
|
|
@ -200,13 +213,15 @@ bool MapSettings::ReadJson(const boost::json::object& json)
|
||||||
if (i < mapArray.size() && mapArray.at(i).is_object())
|
if (i < mapArray.size() && mapArray.at(i).is_object())
|
||||||
{
|
{
|
||||||
const boost::json::object& mapRecord = mapArray.at(i).as_object();
|
const boost::json::object& mapRecord = mapArray.at(i).as_object();
|
||||||
Impl::MapData& mapRecordSettings = p->map_[i];
|
Impl::MapData& mapRecordSettings = p->map_.at(i);
|
||||||
|
|
||||||
// Load JSON Elements
|
// Load JSON Elements
|
||||||
validated &= mapRecordSettings.mapStyle_.ReadValue(mapRecord);
|
validated &= mapRecordSettings.mapStyle_.ReadValue(mapRecord);
|
||||||
validated &= mapRecordSettings.radarSite_.ReadValue(mapRecord);
|
validated &= mapRecordSettings.radarSite_.ReadValue(mapRecord);
|
||||||
validated &=
|
validated &=
|
||||||
mapRecordSettings.radarProductGroup_.ReadValue(mapRecord);
|
mapRecordSettings.radarProductGroup_.ReadValue(mapRecord);
|
||||||
|
validated &=
|
||||||
|
mapRecordSettings.smoothingEnabled_.ReadValue(mapRecord);
|
||||||
|
|
||||||
bool productValidated =
|
bool productValidated =
|
||||||
mapRecordSettings.radarProduct_.ReadValue(mapRecord);
|
mapRecordSettings.radarProduct_.ReadValue(mapRecord);
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,11 @@ public:
|
||||||
MapSettings& operator=(MapSettings&&) noexcept;
|
MapSettings& operator=(MapSettings&&) noexcept;
|
||||||
|
|
||||||
std::size_t count() const;
|
std::size_t count() const;
|
||||||
SettingsVariable<std::string>& map_style(std::size_t i) const;
|
SettingsVariable<std::string>& map_style(std::size_t i);
|
||||||
SettingsVariable<std::string>& radar_site(std::size_t i) const;
|
SettingsVariable<std::string>& radar_site(std::size_t i);
|
||||||
SettingsVariable<std::string>& radar_product_group(std::size_t i) const;
|
SettingsVariable<std::string>& radar_product_group(std::size_t i);
|
||||||
SettingsVariable<std::string>& radar_product(std::size_t i) const;
|
SettingsVariable<std::string>& radar_product(std::size_t i);
|
||||||
|
SettingsVariable<bool>& smoothing_enabled(std::size_t i);
|
||||||
|
|
||||||
bool Shutdown();
|
bool Shutdown();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,15 @@ class ProductSettings::Impl
|
||||||
public:
|
public:
|
||||||
explicit Impl()
|
explicit Impl()
|
||||||
{
|
{
|
||||||
|
showSmoothedRangeFolding_.SetDefault(false);
|
||||||
stiForecastEnabled_.SetDefault(true);
|
stiForecastEnabled_.SetDefault(true);
|
||||||
stiPastEnabled_.SetDefault(true);
|
stiPastEnabled_.SetDefault(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
~Impl() {}
|
~Impl() {}
|
||||||
|
|
||||||
|
SettingsVariable<bool> showSmoothedRangeFolding_ {
|
||||||
|
"show_smoothed_range_folding"};
|
||||||
SettingsVariable<bool> stiForecastEnabled_ {"sti_forecast_enabled"};
|
SettingsVariable<bool> stiForecastEnabled_ {"sti_forecast_enabled"};
|
||||||
SettingsVariable<bool> stiPastEnabled_ {"sti_past_enabled"};
|
SettingsVariable<bool> stiPastEnabled_ {"sti_past_enabled"};
|
||||||
};
|
};
|
||||||
|
|
@ -28,7 +31,9 @@ public:
|
||||||
ProductSettings::ProductSettings() :
|
ProductSettings::ProductSettings() :
|
||||||
SettingsCategory("product"), p(std::make_unique<Impl>())
|
SettingsCategory("product"), p(std::make_unique<Impl>())
|
||||||
{
|
{
|
||||||
RegisterVariables({&p->stiForecastEnabled_, &p->stiPastEnabled_});
|
RegisterVariables({&p->showSmoothedRangeFolding_,
|
||||||
|
&p->stiForecastEnabled_,
|
||||||
|
&p->stiPastEnabled_});
|
||||||
SetDefaults();
|
SetDefaults();
|
||||||
}
|
}
|
||||||
ProductSettings::~ProductSettings() = default;
|
ProductSettings::~ProductSettings() = default;
|
||||||
|
|
@ -37,12 +42,17 @@ ProductSettings::ProductSettings(ProductSettings&&) noexcept = default;
|
||||||
ProductSettings&
|
ProductSettings&
|
||||||
ProductSettings::operator=(ProductSettings&&) noexcept = default;
|
ProductSettings::operator=(ProductSettings&&) noexcept = default;
|
||||||
|
|
||||||
SettingsVariable<bool>& ProductSettings::sti_forecast_enabled() const
|
SettingsVariable<bool>& ProductSettings::show_smoothed_range_folding()
|
||||||
|
{
|
||||||
|
return p->showSmoothedRangeFolding_;
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsVariable<bool>& ProductSettings::sti_forecast_enabled()
|
||||||
{
|
{
|
||||||
return p->stiForecastEnabled_;
|
return p->stiForecastEnabled_;
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsVariable<bool>& ProductSettings::sti_past_enabled() const
|
SettingsVariable<bool>& ProductSettings::sti_past_enabled()
|
||||||
{
|
{
|
||||||
return p->stiPastEnabled_;
|
return p->stiPastEnabled_;
|
||||||
}
|
}
|
||||||
|
|
@ -66,7 +76,9 @@ ProductSettings& ProductSettings::Instance()
|
||||||
|
|
||||||
bool operator==(const ProductSettings& lhs, const ProductSettings& rhs)
|
bool operator==(const ProductSettings& lhs, const ProductSettings& rhs)
|
||||||
{
|
{
|
||||||
return (lhs.p->stiForecastEnabled_ == rhs.p->stiForecastEnabled_ &&
|
return (lhs.p->showSmoothedRangeFolding_ ==
|
||||||
|
rhs.p->showSmoothedRangeFolding_ &&
|
||||||
|
lhs.p->stiForecastEnabled_ == rhs.p->stiForecastEnabled_ &&
|
||||||
lhs.p->stiPastEnabled_ == rhs.p->stiPastEnabled_);
|
lhs.p->stiPastEnabled_ == rhs.p->stiPastEnabled_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,9 @@ public:
|
||||||
ProductSettings(ProductSettings&&) noexcept;
|
ProductSettings(ProductSettings&&) noexcept;
|
||||||
ProductSettings& operator=(ProductSettings&&) noexcept;
|
ProductSettings& operator=(ProductSettings&&) noexcept;
|
||||||
|
|
||||||
SettingsVariable<bool>& sti_forecast_enabled() const;
|
SettingsVariable<bool>& show_smoothed_range_folding();
|
||||||
SettingsVariable<bool>& sti_past_enabled() const;
|
SettingsVariable<bool>& sti_forecast_enabled();
|
||||||
|
SettingsVariable<bool>& sti_past_enabled();
|
||||||
|
|
||||||
static ProductSettings& Instance();
|
static ProductSettings& Instance();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include <scwx/qt/settings/audio_settings.hpp>
|
#include <scwx/qt/settings/audio_settings.hpp>
|
||||||
#include <scwx/qt/settings/general_settings.hpp>
|
#include <scwx/qt/settings/general_settings.hpp>
|
||||||
#include <scwx/qt/settings/palette_settings.hpp>
|
#include <scwx/qt/settings/palette_settings.hpp>
|
||||||
|
#include <scwx/qt/settings/product_settings.hpp>
|
||||||
#include <scwx/qt/settings/settings_interface.hpp>
|
#include <scwx/qt/settings/settings_interface.hpp>
|
||||||
#include <scwx/qt/settings/text_settings.hpp>
|
#include <scwx/qt/settings/text_settings.hpp>
|
||||||
#include <scwx/qt/settings/unit_settings.hpp>
|
#include <scwx/qt/settings/unit_settings.hpp>
|
||||||
|
|
@ -136,6 +137,7 @@ public:
|
||||||
&showMapAttribution_,
|
&showMapAttribution_,
|
||||||
&showMapCenter_,
|
&showMapCenter_,
|
||||||
&showMapLogo_,
|
&showMapLogo_,
|
||||||
|
&showSmoothedRangeFolding_,
|
||||||
&updateNotificationsEnabled_,
|
&updateNotificationsEnabled_,
|
||||||
&cursorIconAlwaysOn_,
|
&cursorIconAlwaysOn_,
|
||||||
&debugEnabled_,
|
&debugEnabled_,
|
||||||
|
|
@ -251,6 +253,7 @@ public:
|
||||||
settings::SettingsInterface<bool> showMapAttribution_ {};
|
settings::SettingsInterface<bool> showMapAttribution_ {};
|
||||||
settings::SettingsInterface<bool> showMapCenter_ {};
|
settings::SettingsInterface<bool> showMapCenter_ {};
|
||||||
settings::SettingsInterface<bool> showMapLogo_ {};
|
settings::SettingsInterface<bool> showMapLogo_ {};
|
||||||
|
settings::SettingsInterface<bool> showSmoothedRangeFolding_ {};
|
||||||
settings::SettingsInterface<bool> updateNotificationsEnabled_ {};
|
settings::SettingsInterface<bool> updateNotificationsEnabled_ {};
|
||||||
settings::SettingsInterface<bool> cursorIconAlwaysOn_ {};
|
settings::SettingsInterface<bool> cursorIconAlwaysOn_ {};
|
||||||
settings::SettingsInterface<bool> debugEnabled_ {};
|
settings::SettingsInterface<bool> debugEnabled_ {};
|
||||||
|
|
@ -527,21 +530,22 @@ void SettingsDialogImpl::SetupGeneralTab()
|
||||||
{
|
{
|
||||||
settings::GeneralSettings& generalSettings =
|
settings::GeneralSettings& generalSettings =
|
||||||
settings::GeneralSettings::Instance();
|
settings::GeneralSettings::Instance();
|
||||||
|
settings::ProductSettings& productSettings =
|
||||||
|
settings::ProductSettings::Instance();
|
||||||
|
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
self_->ui->themeComboBox,
|
self_->ui->themeComboBox,
|
||||||
&QComboBox::currentTextChanged,
|
&QComboBox::currentTextChanged,
|
||||||
self_,
|
self_,
|
||||||
[this](const QString& text)
|
[this](const QString& text)
|
||||||
{
|
{
|
||||||
types::UiStyle style = types::GetUiStyle(text.toStdString());
|
const types::UiStyle style = types::GetUiStyle(text.toStdString());
|
||||||
bool themeFileEnabled = style == types::UiStyle::FusionCustom;
|
const bool themeFileEnabled = style == types::UiStyle::FusionCustom;
|
||||||
|
|
||||||
self_->ui->themeFileLineEdit->setEnabled(themeFileEnabled);
|
self_->ui->themeFileLineEdit->setEnabled(themeFileEnabled);
|
||||||
self_->ui->themeFileSelectButton->setEnabled(themeFileEnabled);
|
self_->ui->themeFileSelectButton->setEnabled(themeFileEnabled);
|
||||||
self_->ui->resetThemeFileButton->setEnabled(themeFileEnabled);
|
self_->ui->resetThemeFileButton->setEnabled(themeFileEnabled);
|
||||||
});
|
});
|
||||||
|
|
||||||
theme_.SetSettingsVariable(generalSettings.theme());
|
theme_.SetSettingsVariable(generalSettings.theme());
|
||||||
SCWX_SETTINGS_COMBO_BOX(theme_,
|
SCWX_SETTINGS_COMBO_BOX(theme_,
|
||||||
|
|
@ -759,6 +763,11 @@ void SettingsDialogImpl::SetupGeneralTab()
|
||||||
showMapLogo_.SetSettingsVariable(generalSettings.show_map_logo());
|
showMapLogo_.SetSettingsVariable(generalSettings.show_map_logo());
|
||||||
showMapLogo_.SetEditWidget(self_->ui->showMapLogoCheckBox);
|
showMapLogo_.SetEditWidget(self_->ui->showMapLogoCheckBox);
|
||||||
|
|
||||||
|
showSmoothedRangeFolding_.SetSettingsVariable(
|
||||||
|
productSettings.show_smoothed_range_folding());
|
||||||
|
showSmoothedRangeFolding_.SetEditWidget(
|
||||||
|
self_->ui->showSmoothedRangeFoldingCheckBox);
|
||||||
|
|
||||||
updateNotificationsEnabled_.SetSettingsVariable(
|
updateNotificationsEnabled_.SetSettingsVariable(
|
||||||
generalSettings.update_notifications_enabled());
|
generalSettings.update_notifications_enabled());
|
||||||
updateNotificationsEnabled_.SetEditWidget(
|
updateNotificationsEnabled_.SetEditWidget(
|
||||||
|
|
|
||||||
|
|
@ -135,9 +135,9 @@
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>-246</y>
|
<y>-272</y>
|
||||||
<width>511</width>
|
<width>513</width>
|
||||||
<height>703</height>
|
<height>702</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
|
@ -562,6 +562,19 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="cursorIconAlwaysOnCheckBox">
|
||||||
|
<property name="acceptDrops">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Multi-Pane Cursor Marker Always On</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="showMapAttributionCheckBox">
|
<widget class="QCheckBox" name="showMapAttributionCheckBox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
@ -584,22 +597,16 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="enableUpdateNotificationsCheckBox">
|
<widget class="QCheckBox" name="showSmoothedRangeFoldingCheckBox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Update Notifications Enabled</string>
|
<string>Show Range Folding when Smoothing Radar Data</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="cursorIconAlwaysOnCheckBox">
|
<widget class="QCheckBox" name="enableUpdateNotificationsCheckBox">
|
||||||
<property name="acceptDrops">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Multi-Pane Cursor Marker Always On</string>
|
<string>Update Notifications Enabled</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,11 @@ static constexpr std::uint32_t kMaxRadialGates_ =
|
||||||
common::MAX_0_5_DEGREE_RADIALS * common::MAX_DATA_MOMENT_GATES;
|
common::MAX_0_5_DEGREE_RADIALS * common::MAX_DATA_MOMENT_GATES;
|
||||||
static constexpr std::uint32_t kMaxCoordinates_ = kMaxRadialGates_ * 2u;
|
static constexpr std::uint32_t kMaxCoordinates_ = kMaxRadialGates_ * 2u;
|
||||||
|
|
||||||
|
static constexpr std::uint8_t kDataWordSize8_ = 8u;
|
||||||
|
|
||||||
|
static constexpr std::size_t kVerticesPerGate_ = 6u;
|
||||||
|
static constexpr std::size_t kVerticesPerOriginGate_ = 3u;
|
||||||
|
|
||||||
static constexpr uint16_t RANGE_FOLDED = 1u;
|
static constexpr uint16_t RANGE_FOLDED = 1u;
|
||||||
static constexpr uint32_t VERTICES_PER_BIN = 6u;
|
static constexpr uint32_t VERTICES_PER_BIN = 6u;
|
||||||
static constexpr uint32_t VALUES_PER_VERTEX = 2u;
|
static constexpr uint32_t VALUES_PER_VERTEX = 2u;
|
||||||
|
|
@ -53,11 +58,10 @@ static const std::unordered_map<common::Level2Product, std::string>
|
||||||
{common::Level2Product::CorrelationCoefficient, "%"},
|
{common::Level2Product::CorrelationCoefficient, "%"},
|
||||||
{common::Level2Product::ClutterFilterPowerRemoved, "dB"}};
|
{common::Level2Product::ClutterFilterPowerRemoved, "dB"}};
|
||||||
|
|
||||||
class Level2ProductViewImpl
|
class Level2ProductView::Impl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Level2ProductViewImpl(Level2ProductView* self,
|
explicit Impl(Level2ProductView* self, common::Level2Product product) :
|
||||||
common::Level2Product product) :
|
|
||||||
self_ {self},
|
self_ {self},
|
||||||
product_ {product},
|
product_ {product},
|
||||||
selectedElevation_ {0.0f},
|
selectedElevation_ {0.0f},
|
||||||
|
|
@ -94,7 +98,7 @@ public:
|
||||||
UpdateOtherUnits(unitSettings.other_units().GetValue());
|
UpdateOtherUnits(unitSettings.other_units().GetValue());
|
||||||
UpdateSpeedUnits(unitSettings.speed_units().GetValue());
|
UpdateSpeedUnits(unitSettings.speed_units().GetValue());
|
||||||
}
|
}
|
||||||
~Level2ProductViewImpl()
|
~Impl()
|
||||||
{
|
{
|
||||||
auto& unitSettings = settings::UnitSettings::Instance();
|
auto& unitSettings = settings::UnitSettings::Instance();
|
||||||
|
|
||||||
|
|
@ -106,23 +110,36 @@ public:
|
||||||
threadPool_.join();
|
threadPool_.join();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Impl(const Impl&) = delete;
|
||||||
|
Impl& operator=(const Impl&) = delete;
|
||||||
|
|
||||||
|
Impl(Impl&&) noexcept = delete;
|
||||||
|
Impl& operator=(Impl&&) noexcept = delete;
|
||||||
|
|
||||||
void ComputeCoordinates(
|
void ComputeCoordinates(
|
||||||
const std::shared_ptr<wsr88d::rda::ElevationScan>& radarData);
|
const std::shared_ptr<wsr88d::rda::ElevationScan>& radarData,
|
||||||
|
bool smoothingEnabled);
|
||||||
|
|
||||||
void SetProduct(const std::string& productName);
|
void SetProduct(const std::string& productName);
|
||||||
void SetProduct(common::Level2Product product);
|
void SetProduct(common::Level2Product product);
|
||||||
void UpdateOtherUnits(const std::string& name);
|
void UpdateOtherUnits(const std::string& name);
|
||||||
void UpdateSpeedUnits(const std::string& name);
|
void UpdateSpeedUnits(const std::string& name);
|
||||||
|
|
||||||
|
void ComputeEdgeValue();
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]] inline T RemapDataMoment(T dataMoment) const;
|
||||||
|
|
||||||
static bool IsRadarDataIncomplete(
|
static bool IsRadarDataIncomplete(
|
||||||
const std::shared_ptr<const wsr88d::rda::ElevationScan>& radarData);
|
const std::shared_ptr<const wsr88d::rda::ElevationScan>& radarData);
|
||||||
|
static units::degrees<float> NormalizeAngle(units::degrees<float> angle);
|
||||||
|
|
||||||
Level2ProductView* self_;
|
Level2ProductView* self_;
|
||||||
|
|
||||||
boost::asio::thread_pool threadPool_ {1u};
|
boost::asio::thread_pool threadPool_ {1u};
|
||||||
|
|
||||||
common::Level2Product product_;
|
common::Level2Product product_;
|
||||||
wsr88d::rda::DataBlockType dataBlockType_;
|
wsr88d::rda::DataBlockType dataBlockType_ {
|
||||||
|
wsr88d::rda::DataBlockType::Unknown};
|
||||||
|
|
||||||
float selectedElevation_;
|
float selectedElevation_;
|
||||||
|
|
||||||
|
|
@ -130,11 +147,17 @@ public:
|
||||||
std::shared_ptr<wsr88d::rda::GenericRadarData::MomentDataBlock>
|
std::shared_ptr<wsr88d::rda::GenericRadarData::MomentDataBlock>
|
||||||
momentDataBlock0_;
|
momentDataBlock0_;
|
||||||
|
|
||||||
|
bool lastShowSmoothedRangeFolding_ {false};
|
||||||
|
bool lastSmoothingEnabled_ {false};
|
||||||
|
|
||||||
std::vector<float> coordinates_ {};
|
std::vector<float> coordinates_ {};
|
||||||
std::vector<float> vertices_ {};
|
std::vector<float> vertices_ {};
|
||||||
std::vector<uint8_t> dataMoments8_ {};
|
std::vector<uint8_t> dataMoments8_ {};
|
||||||
std::vector<uint16_t> dataMoments16_ {};
|
std::vector<uint16_t> dataMoments16_ {};
|
||||||
std::vector<uint8_t> cfpMoments_ {};
|
std::vector<uint8_t> cfpMoments_ {};
|
||||||
|
std::uint16_t edgeValue_ {};
|
||||||
|
|
||||||
|
bool showSmoothedRangeFolding_ {false};
|
||||||
|
|
||||||
float latitude_;
|
float latitude_;
|
||||||
float longitude_;
|
float longitude_;
|
||||||
|
|
@ -164,7 +187,7 @@ Level2ProductView::Level2ProductView(
|
||||||
common::Level2Product product,
|
common::Level2Product product,
|
||||||
std::shared_ptr<manager::RadarProductManager> radarProductManager) :
|
std::shared_ptr<manager::RadarProductManager> radarProductManager) :
|
||||||
RadarProductView(radarProductManager),
|
RadarProductView(radarProductManager),
|
||||||
p(std::make_unique<Level2ProductViewImpl>(this, product))
|
p(std::make_unique<Impl>(this, product))
|
||||||
{
|
{
|
||||||
ConnectRadarProductManager();
|
ConnectRadarProductManager();
|
||||||
}
|
}
|
||||||
|
|
@ -379,12 +402,12 @@ void Level2ProductView::SelectProduct(const std::string& productName)
|
||||||
p->SetProduct(productName);
|
p->SetProduct(productName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Level2ProductViewImpl::SetProduct(const std::string& productName)
|
void Level2ProductView::Impl::SetProduct(const std::string& productName)
|
||||||
{
|
{
|
||||||
SetProduct(common::GetLevel2Product(productName));
|
SetProduct(common::GetLevel2Product(productName));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Level2ProductViewImpl::SetProduct(common::Level2Product product)
|
void Level2ProductView::Impl::SetProduct(common::Level2Product product)
|
||||||
{
|
{
|
||||||
product_ = product;
|
product_ = product;
|
||||||
|
|
||||||
|
|
@ -401,12 +424,12 @@ void Level2ProductViewImpl::SetProduct(common::Level2Product product)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Level2ProductViewImpl::UpdateOtherUnits(const std::string& name)
|
void Level2ProductView::Impl::UpdateOtherUnits(const std::string& name)
|
||||||
{
|
{
|
||||||
otherUnits_ = types::GetOtherUnitsFromName(name);
|
otherUnits_ = types::GetOtherUnitsFromName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Level2ProductViewImpl::UpdateSpeedUnits(const std::string& name)
|
void Level2ProductView::Impl::UpdateSpeedUnits(const std::string& name)
|
||||||
{
|
{
|
||||||
speedUnits_ = types::GetSpeedUnitsFromName(name);
|
speedUnits_ = types::GetSpeedUnitsFromName(name);
|
||||||
}
|
}
|
||||||
|
|
@ -511,6 +534,9 @@ void Level2ProductView::ComputeSweep()
|
||||||
|
|
||||||
std::shared_ptr<manager::RadarProductManager> radarProductManager =
|
std::shared_ptr<manager::RadarProductManager> radarProductManager =
|
||||||
radar_product_manager();
|
radar_product_manager();
|
||||||
|
const bool smoothingEnabled = smoothing_enabled();
|
||||||
|
p->showSmoothedRangeFolding_ = show_smoothed_range_folding();
|
||||||
|
const bool& showSmoothedRangeFolding = p->showSmoothedRangeFolding_;
|
||||||
|
|
||||||
std::shared_ptr<wsr88d::rda::ElevationScan> radarData;
|
std::shared_ptr<wsr88d::rda::ElevationScan> radarData;
|
||||||
std::chrono::system_clock::time_point requestedTime {selected_time()};
|
std::chrono::system_clock::time_point requestedTime {selected_time()};
|
||||||
|
|
@ -523,12 +549,18 @@ void Level2ProductView::ComputeSweep()
|
||||||
Q_EMIT SweepNotComputed(types::NoUpdateReason::NotLoaded);
|
Q_EMIT SweepNotComputed(types::NoUpdateReason::NotLoaded);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (radarData == p->elevationScan_)
|
if (radarData == p->elevationScan_ &&
|
||||||
|
smoothingEnabled == p->lastSmoothingEnabled_ &&
|
||||||
|
(showSmoothedRangeFolding == p->lastShowSmoothedRangeFolding_ ||
|
||||||
|
!smoothingEnabled))
|
||||||
{
|
{
|
||||||
Q_EMIT SweepNotComputed(types::NoUpdateReason::NoChange);
|
Q_EMIT SweepNotComputed(types::NoUpdateReason::NoChange);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p->lastShowSmoothedRangeFolding_ = showSmoothedRangeFolding;
|
||||||
|
p->lastSmoothingEnabled_ = smoothingEnabled;
|
||||||
|
|
||||||
logger_->debug("Computing Sweep");
|
logger_->debug("Computing Sweep");
|
||||||
|
|
||||||
std::size_t radials = radarData->crbegin()->first + 1;
|
std::size_t radials = radarData->crbegin()->first + 1;
|
||||||
|
|
@ -536,8 +568,7 @@ void Level2ProductView::ComputeSweep()
|
||||||
|
|
||||||
// When there is missing data, insert another empty vertex radial at the end
|
// When there is missing data, insert another empty vertex radial at the end
|
||||||
// to avoid stretching
|
// to avoid stretching
|
||||||
const bool isRadarDataIncomplete =
|
const bool isRadarDataIncomplete = Impl::IsRadarDataIncomplete(radarData);
|
||||||
Level2ProductViewImpl::IsRadarDataIncomplete(radarData);
|
|
||||||
if (isRadarDataIncomplete)
|
if (isRadarDataIncomplete)
|
||||||
{
|
{
|
||||||
++vertexRadials;
|
++vertexRadials;
|
||||||
|
|
@ -548,7 +579,7 @@ void Level2ProductView::ComputeSweep()
|
||||||
vertexRadials =
|
vertexRadials =
|
||||||
std::min<std::size_t>(vertexRadials, common::MAX_0_5_DEGREE_RADIALS);
|
std::min<std::size_t>(vertexRadials, common::MAX_0_5_DEGREE_RADIALS);
|
||||||
|
|
||||||
p->ComputeCoordinates(radarData);
|
p->ComputeCoordinates(radarData, smoothingEnabled);
|
||||||
|
|
||||||
const std::vector<float>& coordinates = p->coordinates_;
|
const std::vector<float>& coordinates = p->coordinates_;
|
||||||
|
|
||||||
|
|
@ -627,11 +658,20 @@ void Level2ProductView::ComputeSweep()
|
||||||
// Start radial is always 0, as coordinates are calculated for each sweep
|
// Start radial is always 0, as coordinates are calculated for each sweep
|
||||||
constexpr std::uint16_t startRadial = 0u;
|
constexpr std::uint16_t startRadial = 0u;
|
||||||
|
|
||||||
for (auto& radialPair : *radarData)
|
// For most products other than reflectivity, the edge should not go to the
|
||||||
|
// bottom of the color table
|
||||||
|
if (smoothingEnabled)
|
||||||
{
|
{
|
||||||
|
p->ComputeEdgeValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = radarData->cbegin(); it != radarData->cend(); ++it)
|
||||||
|
{
|
||||||
|
const auto& radialPair = *it;
|
||||||
std::uint16_t radial = radialPair.first;
|
std::uint16_t radial = radialPair.first;
|
||||||
auto& radialData = radialPair.second;
|
const auto& radialData = radialPair.second;
|
||||||
auto momentData = radialData->moment_data_block(p->dataBlockType_);
|
const std::shared_ptr<wsr88d::rda::GenericRadarData::MomentDataBlock>
|
||||||
|
momentData = radialData->moment_data_block(p->dataBlockType_);
|
||||||
|
|
||||||
if (momentData0->data_word_size() != momentData->data_word_size())
|
if (momentData0->data_word_size() != momentData->data_word_size())
|
||||||
{
|
{
|
||||||
|
|
@ -653,7 +693,7 @@ void Level2ProductView::ComputeSweep()
|
||||||
std::max<std::int32_t>(1, dataMomentInterval / gateSizeMeters);
|
std::max<std::int32_t>(1, dataMomentInterval / gateSizeMeters);
|
||||||
|
|
||||||
// Compute gate range [startGate, endGate)
|
// Compute gate range [startGate, endGate)
|
||||||
const std::int32_t startGate =
|
std::int32_t startGate =
|
||||||
(dataMomentRange - dataMomentIntervalH) / gateSizeMeters;
|
(dataMomentRange - dataMomentIntervalH) / gateSizeMeters;
|
||||||
const std::int32_t numberOfDataMomentGates =
|
const std::int32_t numberOfDataMomentGates =
|
||||||
std::min<std::int32_t>(momentData->number_of_data_moment_gates(),
|
std::min<std::int32_t>(momentData->number_of_data_moment_gates(),
|
||||||
|
|
@ -662,9 +702,19 @@ void Level2ProductView::ComputeSweep()
|
||||||
startGate + numberOfDataMomentGates * gateSize,
|
startGate + numberOfDataMomentGates * gateSize,
|
||||||
static_cast<std::int32_t>(common::MAX_DATA_MOMENT_GATES));
|
static_cast<std::int32_t>(common::MAX_DATA_MOMENT_GATES));
|
||||||
|
|
||||||
const std::uint8_t* dataMomentsArray8 = nullptr;
|
if (smoothingEnabled)
|
||||||
const std::uint16_t* dataMomentsArray16 = nullptr;
|
{
|
||||||
const std::uint8_t* cfpMomentsArray = nullptr;
|
// If smoothing is enabled, the start gate is incremented by one, as we
|
||||||
|
// are skipping the radar site origin. The end gate is unaffected, as
|
||||||
|
// we need to draw one less data point.
|
||||||
|
++startGate;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint8_t* dataMomentsArray8 = nullptr;
|
||||||
|
const std::uint16_t* dataMomentsArray16 = nullptr;
|
||||||
|
const std::uint8_t* nextDataMomentsArray8 = nullptr;
|
||||||
|
const std::uint16_t* nextDataMomentsArray16 = nullptr;
|
||||||
|
const std::uint8_t* cfpMomentsArray = nullptr;
|
||||||
|
|
||||||
if (momentData->data_word_size() == 8)
|
if (momentData->data_word_size() == 8)
|
||||||
{
|
{
|
||||||
|
|
@ -684,6 +734,45 @@ void Level2ProductView::ComputeSweep()
|
||||||
->data_moments());
|
->data_moments());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<wsr88d::rda::GenericRadarData::MomentDataBlock>
|
||||||
|
nextMomentData = nullptr;
|
||||||
|
std::int32_t numberOfNextDataMomentGates = 0;
|
||||||
|
if (smoothingEnabled)
|
||||||
|
{
|
||||||
|
// Smoothing requires the next radial pair as well
|
||||||
|
auto nextIt = std::next(it);
|
||||||
|
if (nextIt == radarData->cend())
|
||||||
|
{
|
||||||
|
nextIt = radarData->cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& nextRadialPair = *(nextIt);
|
||||||
|
const auto& nextRadialData = nextRadialPair.second;
|
||||||
|
nextMomentData = nextRadialData->moment_data_block(p->dataBlockType_);
|
||||||
|
|
||||||
|
if (momentData->data_word_size() != nextMomentData->data_word_size())
|
||||||
|
{
|
||||||
|
// Data should be consistent between radials
|
||||||
|
logger_->warn("Invalid data moment size");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextMomentData->data_word_size() == kDataWordSize8_)
|
||||||
|
{
|
||||||
|
nextDataMomentsArray8 = reinterpret_cast<const std::uint8_t*>(
|
||||||
|
nextMomentData->data_moments());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextDataMomentsArray16 = reinterpret_cast<const std::uint16_t*>(
|
||||||
|
nextMomentData->data_moments());
|
||||||
|
}
|
||||||
|
|
||||||
|
numberOfNextDataMomentGates = std::min<std::int32_t>(
|
||||||
|
nextMomentData->number_of_data_moment_gates(),
|
||||||
|
static_cast<std::int32_t>(gates));
|
||||||
|
}
|
||||||
|
|
||||||
for (std::int32_t gate = startGate, i = 0; gate + gateSize <= endGate;
|
for (std::int32_t gate = startGate, i = 0; gate + gateSize <= endGate;
|
||||||
gate += gateSize, ++i)
|
gate += gateSize, ++i)
|
||||||
{
|
{
|
||||||
|
|
@ -692,57 +781,172 @@ void Level2ProductView::ComputeSweep()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t vertexCount = (gate > 0) ? 6 : 3;
|
const std::size_t vertexCount =
|
||||||
|
(gate > 0) ? kVerticesPerGate_ : kVerticesPerOriginGate_;
|
||||||
|
|
||||||
|
// Allow pointer arithmetic here, as bounds have already been checked
|
||||||
|
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||||
|
|
||||||
// Store data moment value
|
// Store data moment value
|
||||||
if (dataMomentsArray8 != nullptr)
|
if (dataMomentsArray8 != nullptr)
|
||||||
{
|
{
|
||||||
std::uint8_t dataValue = dataMomentsArray8[i];
|
if (!smoothingEnabled)
|
||||||
if (dataValue < snrThreshold && dataValue != RANGE_FOLDED)
|
|
||||||
{
|
{
|
||||||
continue;
|
const std::uint8_t& dataValue = dataMomentsArray8[i];
|
||||||
}
|
if (dataValue < snrThreshold && dataValue != RANGE_FOLDED)
|
||||||
|
|
||||||
for (std::size_t m = 0; m < vertexCount; m++)
|
|
||||||
{
|
|
||||||
dataMoments8[mIndex++] = dataMomentsArray8[i];
|
|
||||||
|
|
||||||
if (cfpMomentsArray != nullptr)
|
|
||||||
{
|
{
|
||||||
cfpMoments[mIndex - 1] = cfpMomentsArray[i];
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (std::size_t m = 0; m < vertexCount; m++)
|
||||||
|
{
|
||||||
|
dataMoments8[mIndex++] = dataValue;
|
||||||
|
|
||||||
|
if (cfpMomentsArray != nullptr)
|
||||||
|
{
|
||||||
|
cfpMoments[mIndex - 1] = cfpMomentsArray[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (gate > 0)
|
||||||
|
{
|
||||||
|
// Validate indices are all in range
|
||||||
|
if (i + 1 >= numberOfDataMomentGates ||
|
||||||
|
i + 1 >= numberOfNextDataMomentGates)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint8_t& dm1 = dataMomentsArray8[i];
|
||||||
|
const std::uint8_t& dm2 = dataMomentsArray8[i + 1];
|
||||||
|
const std::uint8_t& dm3 = nextDataMomentsArray8[i];
|
||||||
|
const std::uint8_t& dm4 = nextDataMomentsArray8[i + 1];
|
||||||
|
|
||||||
|
if ((!showSmoothedRangeFolding && //
|
||||||
|
(dm1 < snrThreshold || dm1 == RANGE_FOLDED) &&
|
||||||
|
(dm2 < snrThreshold || dm2 == RANGE_FOLDED) &&
|
||||||
|
(dm3 < snrThreshold || dm3 == RANGE_FOLDED) &&
|
||||||
|
(dm4 < snrThreshold || dm4 == RANGE_FOLDED)) ||
|
||||||
|
(showSmoothedRangeFolding && //
|
||||||
|
dm1 < snrThreshold && dm1 != RANGE_FOLDED &&
|
||||||
|
dm2 < snrThreshold && dm2 != RANGE_FOLDED &&
|
||||||
|
dm3 < snrThreshold && dm3 != RANGE_FOLDED &&
|
||||||
|
dm4 < snrThreshold && dm4 != RANGE_FOLDED))
|
||||||
|
{
|
||||||
|
// Skip only if all data moments are hidden
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The order must match the store vertices section below
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm1);
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm2);
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm4);
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm1);
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm3);
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm4);
|
||||||
|
|
||||||
|
// cfpMoments is unused, so not populated here
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If smoothing is enabled, gate should never start at zero
|
||||||
|
// (radar site origin)
|
||||||
|
logger_->error(
|
||||||
|
"Smoothing enabled, gate should not start at zero");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::uint16_t dataValue = dataMomentsArray16[i];
|
if (!smoothingEnabled)
|
||||||
if (dataValue < snrThreshold && dataValue != RANGE_FOLDED)
|
|
||||||
{
|
{
|
||||||
|
const std::uint16_t& dataValue = dataMomentsArray16[i];
|
||||||
|
if (dataValue < snrThreshold && dataValue != RANGE_FOLDED)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t m = 0; m < vertexCount; m++)
|
||||||
|
{
|
||||||
|
dataMoments16[mIndex++] = dataValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (gate > 0)
|
||||||
|
{
|
||||||
|
// Validate indices are all in range
|
||||||
|
if (i + 1 >= numberOfDataMomentGates ||
|
||||||
|
i + 1 >= numberOfNextDataMomentGates)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint16_t& dm1 = dataMomentsArray16[i];
|
||||||
|
const std::uint16_t& dm2 = dataMomentsArray16[i + 1];
|
||||||
|
const std::uint16_t& dm3 = nextDataMomentsArray16[i];
|
||||||
|
const std::uint16_t& dm4 = nextDataMomentsArray16[i + 1];
|
||||||
|
|
||||||
|
if ((!showSmoothedRangeFolding && //
|
||||||
|
(dm1 < snrThreshold || dm1 == RANGE_FOLDED) &&
|
||||||
|
(dm2 < snrThreshold || dm2 == RANGE_FOLDED) &&
|
||||||
|
(dm3 < snrThreshold || dm3 == RANGE_FOLDED) &&
|
||||||
|
(dm4 < snrThreshold || dm4 == RANGE_FOLDED)) ||
|
||||||
|
(showSmoothedRangeFolding && //
|
||||||
|
dm1 < snrThreshold && dm1 != RANGE_FOLDED &&
|
||||||
|
dm2 < snrThreshold && dm2 != RANGE_FOLDED &&
|
||||||
|
dm3 < snrThreshold && dm3 != RANGE_FOLDED &&
|
||||||
|
dm4 < snrThreshold && dm4 != RANGE_FOLDED))
|
||||||
|
{
|
||||||
|
// Skip only if all data moments are hidden
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The order must match the store vertices section below
|
||||||
|
dataMoments16[mIndex++] = p->RemapDataMoment(dm1);
|
||||||
|
dataMoments16[mIndex++] = p->RemapDataMoment(dm2);
|
||||||
|
dataMoments16[mIndex++] = p->RemapDataMoment(dm4);
|
||||||
|
dataMoments16[mIndex++] = p->RemapDataMoment(dm1);
|
||||||
|
dataMoments16[mIndex++] = p->RemapDataMoment(dm3);
|
||||||
|
dataMoments16[mIndex++] = p->RemapDataMoment(dm4);
|
||||||
|
|
||||||
|
// cfpMoments is unused, so not populated here
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If smoothing is enabled, gate should never start at zero
|
||||||
|
// (radar site origin)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::size_t m = 0; m < vertexCount; m++)
|
|
||||||
{
|
|
||||||
dataMoments16[mIndex++] = dataMomentsArray16[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||||
|
|
||||||
// Store vertices
|
// Store vertices
|
||||||
if (gate > 0)
|
if (gate > 0)
|
||||||
{
|
{
|
||||||
|
// Draw two triangles per gate
|
||||||
|
//
|
||||||
|
// 2 +---+ 4
|
||||||
|
// | /|
|
||||||
|
// | / |
|
||||||
|
// |/ |
|
||||||
|
// 1 +---+ 3
|
||||||
|
|
||||||
const std::uint16_t baseCoord = gate - 1;
|
const std::uint16_t baseCoord = gate - 1;
|
||||||
|
|
||||||
std::size_t offset1 = ((startRadial + radial) % vertexRadials *
|
const std::size_t offset1 =
|
||||||
common::MAX_DATA_MOMENT_GATES +
|
((startRadial + radial) % vertexRadials *
|
||||||
baseCoord) *
|
common::MAX_DATA_MOMENT_GATES +
|
||||||
2;
|
baseCoord) *
|
||||||
std::size_t offset2 = offset1 + gateSize * 2;
|
2;
|
||||||
std::size_t offset3 =
|
const std::size_t offset2 =
|
||||||
|
offset1 + static_cast<std::size_t>(gateSize) * 2;
|
||||||
|
const std::size_t offset3 =
|
||||||
(((startRadial + radial + 1) % vertexRadials) *
|
(((startRadial + radial + 1) % vertexRadials) *
|
||||||
common::MAX_DATA_MOMENT_GATES +
|
common::MAX_DATA_MOMENT_GATES +
|
||||||
baseCoord) *
|
baseCoord) *
|
||||||
2;
|
2;
|
||||||
std::size_t offset4 = offset3 + gateSize * 2;
|
const std::size_t offset4 =
|
||||||
|
offset3 + static_cast<std::size_t>(gateSize) * 2;
|
||||||
|
|
||||||
vertices[vIndex++] = coordinates[offset1];
|
vertices[vIndex++] = coordinates[offset1];
|
||||||
vertices[vIndex++] = coordinates[offset1 + 1];
|
vertices[vIndex++] = coordinates[offset1 + 1];
|
||||||
|
|
@ -750,19 +954,17 @@ void Level2ProductView::ComputeSweep()
|
||||||
vertices[vIndex++] = coordinates[offset2];
|
vertices[vIndex++] = coordinates[offset2];
|
||||||
vertices[vIndex++] = coordinates[offset2 + 1];
|
vertices[vIndex++] = coordinates[offset2 + 1];
|
||||||
|
|
||||||
vertices[vIndex++] = coordinates[offset3];
|
vertices[vIndex++] = coordinates[offset4];
|
||||||
vertices[vIndex++] = coordinates[offset3 + 1];
|
vertices[vIndex++] = coordinates[offset4 + 1];
|
||||||
|
|
||||||
|
vertices[vIndex++] = coordinates[offset1];
|
||||||
|
vertices[vIndex++] = coordinates[offset1 + 1];
|
||||||
|
|
||||||
vertices[vIndex++] = coordinates[offset3];
|
vertices[vIndex++] = coordinates[offset3];
|
||||||
vertices[vIndex++] = coordinates[offset3 + 1];
|
vertices[vIndex++] = coordinates[offset3 + 1];
|
||||||
|
|
||||||
vertices[vIndex++] = coordinates[offset4];
|
vertices[vIndex++] = coordinates[offset4];
|
||||||
vertices[vIndex++] = coordinates[offset4 + 1];
|
vertices[vIndex++] = coordinates[offset4 + 1];
|
||||||
|
|
||||||
vertices[vIndex++] = coordinates[offset2];
|
|
||||||
vertices[vIndex++] = coordinates[offset2 + 1];
|
|
||||||
|
|
||||||
vertexCount = 6;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -786,8 +988,6 @@ void Level2ProductView::ComputeSweep()
|
||||||
|
|
||||||
vertices[vIndex++] = coordinates[offset2];
|
vertices[vIndex++] = coordinates[offset2];
|
||||||
vertices[vIndex++] = coordinates[offset2 + 1];
|
vertices[vIndex++] = coordinates[offset2 + 1];
|
||||||
|
|
||||||
vertexCount = 3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -819,8 +1019,50 @@ void Level2ProductView::ComputeSweep()
|
||||||
Q_EMIT SweepComputed();
|
Q_EMIT SweepComputed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Level2ProductViewImpl::ComputeCoordinates(
|
void Level2ProductView::Impl::ComputeEdgeValue()
|
||||||
const std::shared_ptr<wsr88d::rda::ElevationScan>& radarData)
|
{
|
||||||
|
const float offset = momentDataBlock0_->offset();
|
||||||
|
|
||||||
|
switch (dataBlockType_)
|
||||||
|
{
|
||||||
|
case wsr88d::rda::DataBlockType::MomentVel:
|
||||||
|
case wsr88d::rda::DataBlockType::MomentZdr:
|
||||||
|
edgeValue_ = static_cast<std::uint16_t>(offset);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case wsr88d::rda::DataBlockType::MomentSw:
|
||||||
|
case wsr88d::rda::DataBlockType::MomentPhi:
|
||||||
|
edgeValue_ = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case wsr88d::rda::DataBlockType::MomentRho:
|
||||||
|
edgeValue_ = std::numeric_limits<std::uint8_t>::max();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case wsr88d::rda::DataBlockType::MomentRef:
|
||||||
|
default:
|
||||||
|
edgeValue_ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T Level2ProductView::Impl::RemapDataMoment(T dataMoment) const
|
||||||
|
{
|
||||||
|
if (dataMoment != 0 &&
|
||||||
|
(dataMoment != RANGE_FOLDED || showSmoothedRangeFolding_))
|
||||||
|
{
|
||||||
|
return dataMoment;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return edgeValue_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Level2ProductView::Impl::ComputeCoordinates(
|
||||||
|
const std::shared_ptr<wsr88d::rda::ElevationScan>& radarData,
|
||||||
|
bool smoothingEnabled)
|
||||||
{
|
{
|
||||||
logger_->debug("ComputeCoordinates()");
|
logger_->debug("ComputeCoordinates()");
|
||||||
|
|
||||||
|
|
@ -860,6 +1102,14 @@ void Level2ProductViewImpl::ComputeCoordinates(
|
||||||
auto radials = boost::irange<std::uint32_t>(0u, numRadials);
|
auto radials = boost::irange<std::uint32_t>(0u, numRadials);
|
||||||
auto gates = boost::irange<std::uint32_t>(0u, numRangeBins);
|
auto gates = boost::irange<std::uint32_t>(0u, numRangeBins);
|
||||||
|
|
||||||
|
const float gateRangeOffset = (smoothingEnabled) ?
|
||||||
|
// Center of the first gate is half the gate
|
||||||
|
// size distance from the radar site
|
||||||
|
0.5f :
|
||||||
|
// Far end of the first gate is the gate
|
||||||
|
// size distance from the radar site
|
||||||
|
1.0f;
|
||||||
|
|
||||||
std::for_each(
|
std::for_each(
|
||||||
std::execution::par_unseq,
|
std::execution::par_unseq,
|
||||||
radials.begin(),
|
radials.begin(),
|
||||||
|
|
@ -869,7 +1119,7 @@ void Level2ProductViewImpl::ComputeCoordinates(
|
||||||
units::degrees<float> angle {};
|
units::degrees<float> angle {};
|
||||||
|
|
||||||
auto radialData = radarData->find(radial);
|
auto radialData = radarData->find(radial);
|
||||||
if (radialData != radarData->cend())
|
if (radialData != radarData->cend() && !smoothingEnabled)
|
||||||
{
|
{
|
||||||
angle = radialData->second->azimuth_angle();
|
angle = radialData->second->azimuth_angle();
|
||||||
}
|
}
|
||||||
|
|
@ -880,19 +1130,60 @@ void Level2ProductViewImpl::ComputeCoordinates(
|
||||||
auto prevRadial2 = radarData->find(
|
auto prevRadial2 = radarData->find(
|
||||||
(radial >= 2) ? radial - 2 : numRadials - (2 - radial));
|
(radial >= 2) ? radial - 2 : numRadials - (2 - radial));
|
||||||
|
|
||||||
if (prevRadial1 != radarData->cend() &&
|
if (radialData != radarData->cend() &&
|
||||||
prevRadial2 != radarData->cend())
|
prevRadial1 != radarData->cend() && smoothingEnabled)
|
||||||
|
{
|
||||||
|
const units::degrees<float> currentAngle =
|
||||||
|
radialData->second->azimuth_angle();
|
||||||
|
const units::degrees<float> prevAngle =
|
||||||
|
prevRadial1->second->azimuth_angle();
|
||||||
|
|
||||||
|
// Calculate delta angle
|
||||||
|
const units::degrees<float> deltaAngle =
|
||||||
|
NormalizeAngle(currentAngle - prevAngle);
|
||||||
|
|
||||||
|
// Delta scale is half the delta angle to reach the center of the
|
||||||
|
// bin, because smoothing is enabled
|
||||||
|
constexpr float deltaScale = 0.5f;
|
||||||
|
|
||||||
|
angle = currentAngle + deltaAngle * deltaScale;
|
||||||
|
}
|
||||||
|
else if (radialData != radarData->cend() && smoothingEnabled)
|
||||||
|
{
|
||||||
|
const units::degrees<float> currentAngle =
|
||||||
|
radialData->second->azimuth_angle();
|
||||||
|
|
||||||
|
// Assume a half degree delta if there aren't enough angles
|
||||||
|
// to determine a delta angle
|
||||||
|
constexpr units::degrees<float> deltaAngle {0.5f};
|
||||||
|
|
||||||
|
// Delta scale is half the delta angle to reach the center of the
|
||||||
|
// bin, because smoothing is enabled
|
||||||
|
constexpr float deltaScale = 0.5f;
|
||||||
|
|
||||||
|
angle = currentAngle + deltaAngle * deltaScale;
|
||||||
|
}
|
||||||
|
else if (prevRadial1 != radarData->cend() &&
|
||||||
|
prevRadial2 != radarData->cend())
|
||||||
{
|
{
|
||||||
const units::degrees<float> prevAngle1 =
|
const units::degrees<float> prevAngle1 =
|
||||||
prevRadial1->second->azimuth_angle();
|
prevRadial1->second->azimuth_angle();
|
||||||
const units::degrees<float> prevAngle2 =
|
const units::degrees<float> prevAngle2 =
|
||||||
prevRadial2->second->azimuth_angle();
|
prevRadial2->second->azimuth_angle();
|
||||||
|
|
||||||
// No wrapping required since angle is only used for geodesic
|
// Calculate delta angle
|
||||||
// calculation
|
const units::degrees<float> deltaAngle =
|
||||||
const units::degrees<float> deltaAngle = prevAngle1 - prevAngle2;
|
NormalizeAngle(prevAngle1 - prevAngle2);
|
||||||
|
|
||||||
angle = prevAngle1 + deltaAngle;
|
const float deltaScale =
|
||||||
|
(smoothingEnabled) ?
|
||||||
|
// Delta scale is 1.5x the delta angle to reach the center
|
||||||
|
// of the next bin, because smoothing is enabled
|
||||||
|
1.5f :
|
||||||
|
// Delta scale is 1.0x the delta angle
|
||||||
|
1.0f;
|
||||||
|
|
||||||
|
angle = prevAngle1 + deltaAngle * deltaScale;
|
||||||
}
|
}
|
||||||
else if (prevRadial1 != radarData->cend())
|
else if (prevRadial1 != radarData->cend())
|
||||||
{
|
{
|
||||||
|
|
@ -903,7 +1194,15 @@ void Level2ProductViewImpl::ComputeCoordinates(
|
||||||
// to determine a delta angle
|
// to determine a delta angle
|
||||||
constexpr units::degrees<float> deltaAngle {0.5f};
|
constexpr units::degrees<float> deltaAngle {0.5f};
|
||||||
|
|
||||||
angle = prevAngle1 + deltaAngle;
|
const float deltaScale =
|
||||||
|
(smoothingEnabled) ?
|
||||||
|
// Delta scale is 1.5x the delta angle to reach the center
|
||||||
|
// of the next bin, because smoothing is enabled
|
||||||
|
1.5f :
|
||||||
|
// Delta scale is 1.0x the delta angle
|
||||||
|
1.0f;
|
||||||
|
|
||||||
|
angle = prevAngle1 + deltaAngle * deltaScale;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -912,35 +1211,38 @@ void Level2ProductViewImpl::ComputeCoordinates(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::for_each(std::execution::par_unseq,
|
std::for_each(
|
||||||
gates.begin(),
|
std::execution::par_unseq,
|
||||||
gates.end(),
|
gates.begin(),
|
||||||
[&](std::uint32_t gate)
|
gates.end(),
|
||||||
{
|
[&](std::uint32_t gate)
|
||||||
const std::uint32_t radialGate =
|
{
|
||||||
radial * common::MAX_DATA_MOMENT_GATES + gate;
|
const std::uint32_t radialGate =
|
||||||
const float range = (gate + 1) * gateSize;
|
radial * common::MAX_DATA_MOMENT_GATES + gate;
|
||||||
const std::size_t offset = radialGate * 2;
|
const float range =
|
||||||
|
(static_cast<float>(gate) + gateRangeOffset) * gateSize;
|
||||||
|
const std::size_t offset =
|
||||||
|
static_cast<std::size_t>(radialGate) * 2;
|
||||||
|
|
||||||
double latitude;
|
double latitude = 0.0;
|
||||||
double longitude;
|
double longitude = 0.0;
|
||||||
|
|
||||||
geodesic.Direct(radarLatitude,
|
geodesic.Direct(radarLatitude,
|
||||||
radarLongitude,
|
radarLongitude,
|
||||||
angle.value(),
|
angle.value(),
|
||||||
range,
|
range,
|
||||||
latitude,
|
latitude,
|
||||||
longitude);
|
longitude);
|
||||||
|
|
||||||
coordinates_[offset] = latitude;
|
coordinates_[offset] = static_cast<float>(latitude);
|
||||||
coordinates_[offset + 1] = longitude;
|
coordinates_[offset + 1] = static_cast<float>(longitude);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
timer.stop();
|
timer.stop();
|
||||||
logger_->debug("Coordinates calculated in {}", timer.format(6, "%ws"));
|
logger_->debug("Coordinates calculated in {}", timer.format(6, "%ws"));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Level2ProductViewImpl::IsRadarDataIncomplete(
|
bool Level2ProductView::Impl::IsRadarDataIncomplete(
|
||||||
const std::shared_ptr<const wsr88d::rda::ElevationScan>& radarData)
|
const std::shared_ptr<const wsr88d::rda::ElevationScan>& radarData)
|
||||||
{
|
{
|
||||||
// Assume the data is incomplete when the delta between the first and last
|
// Assume the data is incomplete when the delta between the first and last
|
||||||
|
|
@ -957,6 +1259,25 @@ bool Level2ProductViewImpl::IsRadarDataIncomplete(
|
||||||
return angleDelta > kIncompleteDataAngleThreshold_;
|
return angleDelta > kIncompleteDataAngleThreshold_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
units::degrees<float>
|
||||||
|
Level2ProductView::Impl::NormalizeAngle(units::degrees<float> angle)
|
||||||
|
{
|
||||||
|
constexpr auto angleLimit = units::degrees<float> {180.0f};
|
||||||
|
constexpr auto fullAngle = units::degrees<float> {360.0f};
|
||||||
|
|
||||||
|
// Normalize angle to [-180, 180)
|
||||||
|
while (angle < -angleLimit)
|
||||||
|
{
|
||||||
|
angle += fullAngle;
|
||||||
|
}
|
||||||
|
while (angle >= angleLimit)
|
||||||
|
{
|
||||||
|
angle -= fullAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::uint16_t>
|
std::optional<std::uint16_t>
|
||||||
Level2ProductView::GetBinLevel(const common::Coordinate& coordinate) const
|
Level2ProductView::GetBinLevel(const common::Coordinate& coordinate) const
|
||||||
{
|
{
|
||||||
|
|
@ -1003,7 +1324,7 @@ Level2ProductView::GetBinLevel(const common::Coordinate& coordinate) const
|
||||||
static_cast<std::uint16_t>(radarData->crbegin()->first + 1);
|
static_cast<std::uint16_t>(radarData->crbegin()->first + 1);
|
||||||
|
|
||||||
// Add an extra radial when incomplete data exists
|
// Add an extra radial when incomplete data exists
|
||||||
if (Level2ProductViewImpl::IsRadarDataIncomplete(radarData))
|
if (Impl::IsRadarDataIncomplete(radarData))
|
||||||
{
|
{
|
||||||
++numRadials;
|
++numRadials;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ namespace qt
|
||||||
namespace view
|
namespace view
|
||||||
{
|
{
|
||||||
|
|
||||||
class Level2ProductViewImpl;
|
|
||||||
|
|
||||||
class Level2ProductView : public RadarProductView
|
class Level2ProductView : public RadarProductView
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
@ -73,7 +71,8 @@ protected slots:
|
||||||
void ComputeSweep() override;
|
void ComputeSweep() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Level2ProductViewImpl> p;
|
class Impl;
|
||||||
|
std::unique_ptr<Impl> p;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace view
|
} // namespace view
|
||||||
|
|
|
||||||
|
|
@ -485,6 +485,52 @@ void Level3ProductView::UpdateColorTableLut()
|
||||||
Q_EMIT ColorTableLutUpdated();
|
Q_EMIT ColorTableLutUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::uint8_t Level3ProductView::ComputeEdgeValue() const
|
||||||
|
{
|
||||||
|
std::uint8_t edgeValue = 0;
|
||||||
|
|
||||||
|
const std::shared_ptr<wsr88d::rpg::ProductDescriptionBlock>
|
||||||
|
descriptionBlock = p->graphicMessage_->description_block();
|
||||||
|
|
||||||
|
const float offset = descriptionBlock->offset();
|
||||||
|
const float scale = descriptionBlock->scale();
|
||||||
|
|
||||||
|
switch (p->category_)
|
||||||
|
{
|
||||||
|
case common::Level3ProductCategory::Velocity:
|
||||||
|
edgeValue = static_cast<std::uint8_t>((scale > 0.0f) ? (-offset / scale) :
|
||||||
|
-offset);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case common::Level3ProductCategory::DifferentialReflectivity:
|
||||||
|
edgeValue = static_cast<std::uint8_t>(-offset);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case common::Level3ProductCategory::SpectrumWidth:
|
||||||
|
case common::Level3ProductCategory::SpecificDifferentialPhase:
|
||||||
|
edgeValue = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case common::Level3ProductCategory::CorrelationCoefficient:
|
||||||
|
edgeValue = static_cast<std::uint8_t>(
|
||||||
|
std::max<std::uint16_t>(std::numeric_limits<std::uint8_t>::max(),
|
||||||
|
descriptionBlock->number_of_levels()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case common::Level3ProductCategory::Reflectivity:
|
||||||
|
case common::Level3ProductCategory::StormRelativeVelocity:
|
||||||
|
case common::Level3ProductCategory::VerticallyIntegratedLiquid:
|
||||||
|
case common::Level3ProductCategory::EchoTops:
|
||||||
|
case common::Level3ProductCategory::HydrometeorClassification:
|
||||||
|
case common::Level3ProductCategory::PrecipitationAccumulation:
|
||||||
|
default:
|
||||||
|
edgeValue = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return edgeValue;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<wsr88d::DataLevelCode>
|
std::optional<wsr88d::DataLevelCode>
|
||||||
Level3ProductView::GetDataLevelCode(std::uint16_t level) const
|
Level3ProductView::GetDataLevelCode(std::uint16_t level) const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,8 @@ protected:
|
||||||
void DisconnectRadarProductManager() override;
|
void DisconnectRadarProductManager() override;
|
||||||
void UpdateColorTableLut() override;
|
void UpdateColorTableLut() override;
|
||||||
|
|
||||||
|
[[nodiscard]] std::uint8_t ComputeEdgeValue() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Impl;
|
class Impl;
|
||||||
std::unique_ptr<Impl> p;
|
std::unique_ptr<Impl> p;
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,11 @@ public:
|
||||||
~Impl() { threadPool_.join(); };
|
~Impl() { threadPool_.join(); };
|
||||||
|
|
||||||
void ComputeCoordinates(
|
void ComputeCoordinates(
|
||||||
const std::shared_ptr<wsr88d::rpg::GenericRadialDataPacket>& radialData);
|
const std::shared_ptr<wsr88d::rpg::GenericRadialDataPacket>& radialData,
|
||||||
|
bool smoothingEnabled);
|
||||||
|
|
||||||
|
[[nodiscard]] inline std::uint8_t
|
||||||
|
RemapDataMoment(std::uint8_t dataMoment) const;
|
||||||
|
|
||||||
Level3RadialView* self_;
|
Level3RadialView* self_;
|
||||||
|
|
||||||
|
|
@ -53,8 +57,13 @@ public:
|
||||||
std::vector<float> coordinates_ {};
|
std::vector<float> coordinates_ {};
|
||||||
std::vector<float> vertices_ {};
|
std::vector<float> vertices_ {};
|
||||||
std::vector<std::uint8_t> dataMoments8_ {};
|
std::vector<std::uint8_t> dataMoments8_ {};
|
||||||
|
std::uint8_t edgeValue_ {};
|
||||||
|
|
||||||
|
bool showSmoothedRangeFolding_ {false};
|
||||||
|
|
||||||
std::shared_ptr<wsr88d::rpg::GenericRadialDataPacket> lastRadialData_ {};
|
std::shared_ptr<wsr88d::rpg::GenericRadialDataPacket> lastRadialData_ {};
|
||||||
|
bool lastShowSmoothedRangeFolding_ {false};
|
||||||
|
bool lastSmoothingEnabled_ {false};
|
||||||
|
|
||||||
float latitude_;
|
float latitude_;
|
||||||
float longitude_;
|
float longitude_;
|
||||||
|
|
@ -125,6 +134,9 @@ void Level3RadialView::ComputeSweep()
|
||||||
|
|
||||||
std::shared_ptr<manager::RadarProductManager> radarProductManager =
|
std::shared_ptr<manager::RadarProductManager> radarProductManager =
|
||||||
radar_product_manager();
|
radar_product_manager();
|
||||||
|
const bool smoothingEnabled = smoothing_enabled();
|
||||||
|
p->showSmoothedRangeFolding_ = show_smoothed_range_folding();
|
||||||
|
const bool& showSmoothedRangeFolding = p->showSmoothedRangeFolding_;
|
||||||
|
|
||||||
// Retrieve message from Radar Product Manager
|
// Retrieve message from Radar Product Manager
|
||||||
std::shared_ptr<wsr88d::rpg::Level3Message> message;
|
std::shared_ptr<wsr88d::rpg::Level3Message> message;
|
||||||
|
|
@ -155,7 +167,10 @@ void Level3RadialView::ComputeSweep()
|
||||||
Q_EMIT SweepNotComputed(types::NoUpdateReason::InvalidData);
|
Q_EMIT SweepNotComputed(types::NoUpdateReason::InvalidData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (gpm == graphic_product_message())
|
else if (gpm == graphic_product_message() &&
|
||||||
|
smoothingEnabled == p->lastSmoothingEnabled_ &&
|
||||||
|
(showSmoothedRangeFolding == p->lastShowSmoothedRangeFolding_ ||
|
||||||
|
!smoothingEnabled))
|
||||||
{
|
{
|
||||||
// Skip if this is the message we previously processed
|
// Skip if this is the message we previously processed
|
||||||
Q_EMIT SweepNotComputed(types::NoUpdateReason::NoChange);
|
Q_EMIT SweepNotComputed(types::NoUpdateReason::NoChange);
|
||||||
|
|
@ -163,6 +178,9 @@ void Level3RadialView::ComputeSweep()
|
||||||
}
|
}
|
||||||
set_graphic_product_message(gpm);
|
set_graphic_product_message(gpm);
|
||||||
|
|
||||||
|
p->lastShowSmoothedRangeFolding_ = showSmoothedRangeFolding;
|
||||||
|
p->lastSmoothingEnabled_ = smoothingEnabled;
|
||||||
|
|
||||||
// A message with radial data should have a Product Description Block and
|
// A message with radial data should have a Product Description Block and
|
||||||
// Product Symbology Block
|
// Product Symbology Block
|
||||||
std::shared_ptr<wsr88d::rpg::ProductDescriptionBlock> descriptionBlock =
|
std::shared_ptr<wsr88d::rpg::ProductDescriptionBlock> descriptionBlock =
|
||||||
|
|
@ -267,11 +285,11 @@ void Level3RadialView::ComputeSweep()
|
||||||
const std::vector<float>& coordinates =
|
const std::vector<float>& coordinates =
|
||||||
(radialSize == common::RadialSize::NonStandard) ?
|
(radialSize == common::RadialSize::NonStandard) ?
|
||||||
p->coordinates_ :
|
p->coordinates_ :
|
||||||
radarProductManager->coordinates(radialSize);
|
radarProductManager->coordinates(radialSize, smoothingEnabled);
|
||||||
|
|
||||||
// There should be a positive number of range bins in radial data
|
// There should be a positive number of range bins in radial data
|
||||||
const uint16_t gates = radialData->number_of_range_bins();
|
const uint16_t numberOfDataMomentGates = radialData->number_of_range_bins();
|
||||||
if (gates < 1)
|
if (numberOfDataMomentGates < 1)
|
||||||
{
|
{
|
||||||
logger_->warn("No range bins in radial data");
|
logger_->warn("No range bins in radial data");
|
||||||
Q_EMIT SweepNotComputed(types::NoUpdateReason::InvalidData);
|
Q_EMIT SweepNotComputed(types::NoUpdateReason::InvalidData);
|
||||||
|
|
@ -293,13 +311,14 @@ void Level3RadialView::ComputeSweep()
|
||||||
std::vector<float>& vertices = p->vertices_;
|
std::vector<float>& vertices = p->vertices_;
|
||||||
size_t vIndex = 0;
|
size_t vIndex = 0;
|
||||||
vertices.clear();
|
vertices.clear();
|
||||||
vertices.resize(radials * gates * VERTICES_PER_BIN * VALUES_PER_VERTEX);
|
vertices.resize(radials * numberOfDataMomentGates * VERTICES_PER_BIN *
|
||||||
|
VALUES_PER_VERTEX);
|
||||||
|
|
||||||
// Setup data moment vector
|
// Setup data moment vector
|
||||||
std::vector<uint8_t>& dataMoments8 = p->dataMoments8_;
|
std::vector<uint8_t>& dataMoments8 = p->dataMoments8_;
|
||||||
size_t mIndex = 0;
|
size_t mIndex = 0;
|
||||||
|
|
||||||
dataMoments8.resize(radials * gates * VERTICES_PER_BIN);
|
dataMoments8.resize(radials * numberOfDataMomentGates * VERTICES_PER_BIN);
|
||||||
|
|
||||||
// Compute threshold at which to display an individual bin
|
// Compute threshold at which to display an individual bin
|
||||||
const uint16_t snrThreshold = descriptionBlock->threshold();
|
const uint16_t snrThreshold = descriptionBlock->threshold();
|
||||||
|
|
@ -308,7 +327,7 @@ void Level3RadialView::ComputeSweep()
|
||||||
std::uint16_t startRadial;
|
std::uint16_t startRadial;
|
||||||
if (radialSize == common::RadialSize::NonStandard)
|
if (radialSize == common::RadialSize::NonStandard)
|
||||||
{
|
{
|
||||||
p->ComputeCoordinates(radialData);
|
p->ComputeCoordinates(radialData, smoothingEnabled);
|
||||||
startRadial = 0;
|
startRadial = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -318,40 +337,105 @@ void Level3RadialView::ComputeSweep()
|
||||||
startRadial = std::lroundf(startAngle * radialMultiplier);
|
startRadial = std::lroundf(startAngle * radialMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint16_t radial = 0; radial < radialData->number_of_radials(); radial++)
|
// 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()));
|
||||||
|
|
||||||
|
// Compute gate range [startGate, endGate)
|
||||||
|
std::uint16_t startGate = 0;
|
||||||
|
const std::uint16_t endGate =
|
||||||
|
std::min<std::uint16_t>(startGate + numberOfDataMomentGates * gateSize,
|
||||||
|
common::MAX_DATA_MOMENT_GATES);
|
||||||
|
|
||||||
|
if (smoothingEnabled)
|
||||||
{
|
{
|
||||||
const auto dataMomentsArray8 = radialData->level(radial);
|
// If smoothing is enabled, the start gate is incremented by one, as we
|
||||||
|
// are skipping the radar site origin. The end gate is unaffected, as
|
||||||
|
// we need to draw one less data point.
|
||||||
|
++startGate;
|
||||||
|
|
||||||
// Compute gate interval
|
// For most products other than reflectivity, the edge should not go to
|
||||||
const uint16_t dataMomentInterval = descriptionBlock->x_resolution_raw();
|
// the bottom of the color table
|
||||||
|
p->edgeValue_ = ComputeEdgeValue();
|
||||||
|
}
|
||||||
|
|
||||||
// Compute gate size (number of base gates per bin)
|
for (std::uint16_t radial = 0; radial < radialData->number_of_radials();
|
||||||
const uint16_t gateSize = std::max<uint16_t>(
|
++radial)
|
||||||
1,
|
{
|
||||||
dataMomentInterval /
|
const auto& dataMomentsArray8 = radialData->level(radial);
|
||||||
static_cast<uint16_t>(radarProductManager->gate_size()));
|
|
||||||
|
|
||||||
// Compute gate range [startGate, endGate)
|
const std::uint16_t nextRadial =
|
||||||
const uint16_t startGate = 0;
|
(radial == radialData->number_of_radials() - 1) ? 0 : radial + 1;
|
||||||
const uint16_t endGate = std::min<uint16_t>(
|
const auto& nextDataMomentsArray8 = radialData->level(nextRadial);
|
||||||
startGate + gates * gateSize, common::MAX_DATA_MOMENT_GATES);
|
|
||||||
|
|
||||||
for (uint16_t gate = startGate, i = 0; gate + gateSize <= endGate;
|
for (std::uint16_t gate = startGate, i = 0; gate + gateSize <= endGate;
|
||||||
gate += gateSize, ++i)
|
gate += gateSize, ++i)
|
||||||
{
|
{
|
||||||
size_t vertexCount = (gate > 0) ? 6 : 3;
|
size_t vertexCount = (gate > 0) ? 6 : 3;
|
||||||
|
|
||||||
// Store data moment value
|
if (!smoothingEnabled)
|
||||||
uint8_t dataValue =
|
|
||||||
(i < dataMomentsArray8.size()) ? dataMomentsArray8[i] : 0;
|
|
||||||
if (dataValue < snrThreshold && dataValue != RANGE_FOLDED)
|
|
||||||
{
|
{
|
||||||
continue;
|
// Store data moment value
|
||||||
}
|
const uint8_t dataValue =
|
||||||
|
(i < dataMomentsArray8.size()) ? dataMomentsArray8[i] : 0;
|
||||||
|
if (dataValue < snrThreshold && dataValue != RANGE_FOLDED)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t m = 0; m < vertexCount; m++)
|
for (size_t m = 0; m < vertexCount; m++)
|
||||||
|
{
|
||||||
|
dataMoments8[mIndex++] = dataValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (gate > 0)
|
||||||
{
|
{
|
||||||
dataMoments8[mIndex++] = dataValue;
|
// Validate indices are all in range
|
||||||
|
if (i + 1 >= numberOfDataMomentGates)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint8_t& dm1 = dataMomentsArray8[i];
|
||||||
|
const std::uint8_t& dm2 = dataMomentsArray8[i + 1];
|
||||||
|
const std::uint8_t& dm3 = nextDataMomentsArray8[i];
|
||||||
|
const std::uint8_t& dm4 = nextDataMomentsArray8[i + 1];
|
||||||
|
|
||||||
|
if ((!showSmoothedRangeFolding && //
|
||||||
|
(dm1 < snrThreshold || dm1 == RANGE_FOLDED) &&
|
||||||
|
(dm2 < snrThreshold || dm2 == RANGE_FOLDED) &&
|
||||||
|
(dm3 < snrThreshold || dm3 == RANGE_FOLDED) &&
|
||||||
|
(dm4 < snrThreshold || dm4 == RANGE_FOLDED)) ||
|
||||||
|
(showSmoothedRangeFolding && //
|
||||||
|
dm1 < snrThreshold && dm1 != RANGE_FOLDED &&
|
||||||
|
dm2 < snrThreshold && dm2 != RANGE_FOLDED &&
|
||||||
|
dm3 < snrThreshold && dm3 != RANGE_FOLDED &&
|
||||||
|
dm4 < snrThreshold && dm4 != RANGE_FOLDED))
|
||||||
|
{
|
||||||
|
// Skip only if all data moments are hidden
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The order must match the store vertices section below
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm1);
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm2);
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm4);
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm1);
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm3);
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If smoothing is enabled, gate should never start at zero
|
||||||
|
// (radar site origin)
|
||||||
|
logger_->error("Smoothing enabled, gate should not start at zero");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store vertices
|
// Store vertices
|
||||||
|
|
@ -376,19 +460,17 @@ void Level3RadialView::ComputeSweep()
|
||||||
vertices[vIndex++] = coordinates[offset2];
|
vertices[vIndex++] = coordinates[offset2];
|
||||||
vertices[vIndex++] = coordinates[offset2 + 1];
|
vertices[vIndex++] = coordinates[offset2 + 1];
|
||||||
|
|
||||||
vertices[vIndex++] = coordinates[offset3];
|
vertices[vIndex++] = coordinates[offset4];
|
||||||
vertices[vIndex++] = coordinates[offset3 + 1];
|
vertices[vIndex++] = coordinates[offset4 + 1];
|
||||||
|
|
||||||
|
vertices[vIndex++] = coordinates[offset1];
|
||||||
|
vertices[vIndex++] = coordinates[offset1 + 1];
|
||||||
|
|
||||||
vertices[vIndex++] = coordinates[offset3];
|
vertices[vIndex++] = coordinates[offset3];
|
||||||
vertices[vIndex++] = coordinates[offset3 + 1];
|
vertices[vIndex++] = coordinates[offset3 + 1];
|
||||||
|
|
||||||
vertices[vIndex++] = coordinates[offset4];
|
vertices[vIndex++] = coordinates[offset4];
|
||||||
vertices[vIndex++] = coordinates[offset4 + 1];
|
vertices[vIndex++] = coordinates[offset4 + 1];
|
||||||
|
|
||||||
vertices[vIndex++] = coordinates[offset2];
|
|
||||||
vertices[vIndex++] = coordinates[offset2 + 1];
|
|
||||||
|
|
||||||
vertexCount = 6;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -411,8 +493,6 @@ void Level3RadialView::ComputeSweep()
|
||||||
|
|
||||||
vertices[vIndex++] = coordinates[offset2];
|
vertices[vIndex++] = coordinates[offset2];
|
||||||
vertices[vIndex++] = coordinates[offset2 + 1];
|
vertices[vIndex++] = coordinates[offset2 + 1];
|
||||||
|
|
||||||
vertexCount = 3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -430,8 +510,23 @@ void Level3RadialView::ComputeSweep()
|
||||||
Q_EMIT SweepComputed();
|
Q_EMIT SweepComputed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::uint8_t
|
||||||
|
Level3RadialView::Impl::RemapDataMoment(std::uint8_t dataMoment) const
|
||||||
|
{
|
||||||
|
if (dataMoment != 0 &&
|
||||||
|
(dataMoment != RANGE_FOLDED || showSmoothedRangeFolding_))
|
||||||
|
{
|
||||||
|
return dataMoment;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return edgeValue_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Level3RadialView::Impl::ComputeCoordinates(
|
void Level3RadialView::Impl::ComputeCoordinates(
|
||||||
const std::shared_ptr<wsr88d::rpg::GenericRadialDataPacket>& radialData)
|
const std::shared_ptr<wsr88d::rpg::GenericRadialDataPacket>& radialData,
|
||||||
|
bool smoothingEnabled)
|
||||||
{
|
{
|
||||||
logger_->debug("ComputeCoordinates()");
|
logger_->debug("ComputeCoordinates()");
|
||||||
|
|
||||||
|
|
@ -455,38 +550,54 @@ void Level3RadialView::Impl::ComputeCoordinates(
|
||||||
auto radials = boost::irange<std::uint32_t>(0u, numRadials);
|
auto radials = boost::irange<std::uint32_t>(0u, numRadials);
|
||||||
auto gates = boost::irange<std::uint32_t>(0u, numRangeBins);
|
auto gates = boost::irange<std::uint32_t>(0u, numRangeBins);
|
||||||
|
|
||||||
std::for_each(std::execution::par_unseq,
|
const float gateRangeOffset = (smoothingEnabled) ?
|
||||||
radials.begin(),
|
// Center of the first gate is half the gate
|
||||||
radials.end(),
|
// size distance from the radar site
|
||||||
[&](std::uint32_t radial)
|
0.5f :
|
||||||
{
|
// Far end of the first gate is the gate
|
||||||
const float angle = radialData->start_angle(radial);
|
// size distance from the radar site
|
||||||
|
1.0f;
|
||||||
|
|
||||||
std::for_each(std::execution::par_unseq,
|
std::for_each(
|
||||||
gates.begin(),
|
std::execution::par_unseq,
|
||||||
gates.end(),
|
radials.begin(),
|
||||||
[&](std::uint32_t gate)
|
radials.end(),
|
||||||
{
|
[&](std::uint32_t radial)
|
||||||
const std::uint32_t radialGate =
|
{
|
||||||
radial * common::MAX_DATA_MOMENT_GATES +
|
float angle = radialData->start_angle(radial);
|
||||||
gate;
|
|
||||||
const float range = (gate + 1) * gateSize;
|
|
||||||
const std::size_t offset = radialGate * 2;
|
|
||||||
|
|
||||||
double latitude;
|
if (smoothingEnabled)
|
||||||
double longitude;
|
{
|
||||||
|
static constexpr float kDeltaAngleFactor = 0.5f;
|
||||||
|
angle += radialData->delta_angle(radial) * kDeltaAngleFactor;
|
||||||
|
}
|
||||||
|
|
||||||
geodesic.Direct(radarLatitude,
|
std::for_each(
|
||||||
radarLongitude,
|
std::execution::par_unseq,
|
||||||
angle,
|
gates.begin(),
|
||||||
range,
|
gates.end(),
|
||||||
latitude,
|
[&](std::uint32_t gate)
|
||||||
longitude);
|
{
|
||||||
|
const std::uint32_t radialGate =
|
||||||
|
radial * common::MAX_DATA_MOMENT_GATES + gate;
|
||||||
|
const float range =
|
||||||
|
(static_cast<float>(gate) + gateRangeOffset) * gateSize;
|
||||||
|
const std::size_t offset = static_cast<size_t>(radialGate) * 2;
|
||||||
|
|
||||||
coordinates_[offset] = latitude;
|
double latitude = 0.0;
|
||||||
coordinates_[offset + 1] = longitude;
|
double longitude = 0.0;
|
||||||
});
|
|
||||||
});
|
geodesic.Direct(radarLatitude,
|
||||||
|
radarLongitude,
|
||||||
|
angle,
|
||||||
|
range,
|
||||||
|
latitude,
|
||||||
|
longitude);
|
||||||
|
|
||||||
|
coordinates_[offset] = static_cast<float>(latitude);
|
||||||
|
coordinates_[offset + 1] = static_cast<float>(longitude);
|
||||||
|
});
|
||||||
|
});
|
||||||
timer.stop();
|
timer.stop();
|
||||||
logger_->debug("Coordinates calculated in {}", timer.format(6, "%ws"));
|
logger_->debug("Coordinates calculated in {}", timer.format(6, "%ws"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,20 @@ public:
|
||||||
}
|
}
|
||||||
~Level3RasterViewImpl() { threadPool_.join(); };
|
~Level3RasterViewImpl() { threadPool_.join(); };
|
||||||
|
|
||||||
|
[[nodiscard]] inline std::uint8_t
|
||||||
|
RemapDataMoment(std::uint8_t dataMoment) const;
|
||||||
|
|
||||||
boost::asio::thread_pool threadPool_ {1u};
|
boost::asio::thread_pool threadPool_ {1u};
|
||||||
|
|
||||||
std::vector<float> vertices_;
|
std::vector<float> vertices_ {};
|
||||||
std::vector<uint8_t> dataMoments8_;
|
std::vector<std::uint8_t> dataMoments8_ {};
|
||||||
|
std::uint8_t edgeValue_ {};
|
||||||
|
|
||||||
|
bool showSmoothedRangeFolding_ {false};
|
||||||
|
|
||||||
std::shared_ptr<wsr88d::rpg::RasterDataPacket> lastRasterData_ {};
|
std::shared_ptr<wsr88d::rpg::RasterDataPacket> lastRasterData_ {};
|
||||||
|
bool lastShowSmoothedRangeFolding_ {false};
|
||||||
|
bool lastSmoothingEnabled_ {false};
|
||||||
|
|
||||||
float latitude_;
|
float latitude_;
|
||||||
float longitude_;
|
float longitude_;
|
||||||
|
|
@ -109,6 +117,9 @@ void Level3RasterView::ComputeSweep()
|
||||||
|
|
||||||
std::shared_ptr<manager::RadarProductManager> radarProductManager =
|
std::shared_ptr<manager::RadarProductManager> radarProductManager =
|
||||||
radar_product_manager();
|
radar_product_manager();
|
||||||
|
const bool smoothingEnabled = smoothing_enabled();
|
||||||
|
p->showSmoothedRangeFolding_ = show_smoothed_range_folding();
|
||||||
|
const bool& showSmoothedRangeFolding = p->showSmoothedRangeFolding_;
|
||||||
|
|
||||||
// Retrieve message from Radar Product Manager
|
// Retrieve message from Radar Product Manager
|
||||||
std::shared_ptr<wsr88d::rpg::Level3Message> message;
|
std::shared_ptr<wsr88d::rpg::Level3Message> message;
|
||||||
|
|
@ -139,7 +150,10 @@ void Level3RasterView::ComputeSweep()
|
||||||
Q_EMIT SweepNotComputed(types::NoUpdateReason::InvalidData);
|
Q_EMIT SweepNotComputed(types::NoUpdateReason::InvalidData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (gpm == graphic_product_message())
|
else if (gpm == graphic_product_message() &&
|
||||||
|
smoothingEnabled == p->lastSmoothingEnabled_ &&
|
||||||
|
(showSmoothedRangeFolding == p->lastShowSmoothedRangeFolding_ ||
|
||||||
|
!smoothingEnabled))
|
||||||
{
|
{
|
||||||
// Skip if this is the message we previously processed
|
// Skip if this is the message we previously processed
|
||||||
Q_EMIT SweepNotComputed(types::NoUpdateReason::NoChange);
|
Q_EMIT SweepNotComputed(types::NoUpdateReason::NoChange);
|
||||||
|
|
@ -147,6 +161,9 @@ void Level3RasterView::ComputeSweep()
|
||||||
}
|
}
|
||||||
set_graphic_product_message(gpm);
|
set_graphic_product_message(gpm);
|
||||||
|
|
||||||
|
p->lastShowSmoothedRangeFolding_ = showSmoothedRangeFolding;
|
||||||
|
p->lastSmoothingEnabled_ = smoothingEnabled;
|
||||||
|
|
||||||
// A message with radial data should have a Product Description Block and
|
// A message with radial data should have a Product Description Block and
|
||||||
// Product Symbology Block
|
// Product Symbology Block
|
||||||
std::shared_ptr<wsr88d::rpg::ProductDescriptionBlock> descriptionBlock =
|
std::shared_ptr<wsr88d::rpg::ProductDescriptionBlock> descriptionBlock =
|
||||||
|
|
@ -231,16 +248,18 @@ void Level3RasterView::ComputeSweep()
|
||||||
const GeographicLib::Geodesic& geodesic =
|
const GeographicLib::Geodesic& geodesic =
|
||||||
util::GeographicLib::DefaultGeodesic();
|
util::GeographicLib::DefaultGeodesic();
|
||||||
|
|
||||||
const uint16_t xResolution = descriptionBlock->x_resolution_raw();
|
const std::uint16_t xResolution = descriptionBlock->x_resolution_raw();
|
||||||
const uint16_t yResolution = descriptionBlock->y_resolution_raw();
|
const std::uint16_t yResolution = descriptionBlock->y_resolution_raw();
|
||||||
double iCoordinate =
|
const double iCoordinate =
|
||||||
(-rasterData->i_coordinate_start() - 1.0 - p->range_) * 1000.0;
|
(-rasterData->i_coordinate_start() - 1.0 - p->range_) * 1000.0;
|
||||||
double jCoordinate =
|
const double jCoordinate =
|
||||||
(rasterData->j_coordinate_start() + 1.0 + p->range_) * 1000.0;
|
(rasterData->j_coordinate_start() + 1.0 + p->range_) * 1000.0;
|
||||||
|
const double xOffset = (smoothingEnabled) ? xResolution * 0.5 : 0.0;
|
||||||
|
const double yOffset = (smoothingEnabled) ? yResolution * 0.5 : 0.0;
|
||||||
|
|
||||||
size_t numCoordinates =
|
const std::size_t numCoordinates =
|
||||||
static_cast<size_t>(rows + 1) * static_cast<size_t>(maxColumns + 1);
|
static_cast<size_t>(rows + 1) * static_cast<size_t>(maxColumns + 1);
|
||||||
auto coordinateRange =
|
const auto coordinateRange =
|
||||||
boost::irange<uint32_t>(0, static_cast<uint32_t>(numCoordinates));
|
boost::irange<uint32_t>(0, static_cast<uint32_t>(numCoordinates));
|
||||||
|
|
||||||
std::vector<float> coordinates;
|
std::vector<float> coordinates;
|
||||||
|
|
@ -260,8 +279,8 @@ void Level3RasterView::ComputeSweep()
|
||||||
const uint32_t col = index % (rows + 1);
|
const uint32_t col = index % (rows + 1);
|
||||||
const uint32_t row = index / (rows + 1);
|
const uint32_t row = index / (rows + 1);
|
||||||
|
|
||||||
const double i = iCoordinate + xResolution * col;
|
const double i = iCoordinate + xResolution * col + xOffset;
|
||||||
const double j = jCoordinate - yResolution * row;
|
const double j = jCoordinate - yResolution * row - yOffset;
|
||||||
|
|
||||||
// Calculate polar coordinates based on i and j
|
// Calculate polar coordinates based on i and j
|
||||||
const double angle = std::atan2(i, j) * 180.0 / M_PI;
|
const double angle = std::atan2(i, j) * 180.0 / M_PI;
|
||||||
|
|
@ -299,25 +318,83 @@ void Level3RasterView::ComputeSweep()
|
||||||
// Compute threshold at which to display an individual bin
|
// Compute threshold at which to display an individual bin
|
||||||
const uint16_t snrThreshold = descriptionBlock->threshold();
|
const uint16_t snrThreshold = descriptionBlock->threshold();
|
||||||
|
|
||||||
for (size_t row = 0; row < rasterData->number_of_rows(); ++row)
|
const std::size_t rowCount = (smoothingEnabled) ?
|
||||||
|
rasterData->number_of_rows() - 1 :
|
||||||
|
rasterData->number_of_rows();
|
||||||
|
|
||||||
|
if (smoothingEnabled)
|
||||||
{
|
{
|
||||||
const auto dataMomentsArray8 =
|
// For most products other than reflectivity, the edge should not go to
|
||||||
|
// the bottom of the color table
|
||||||
|
p->edgeValue_ = ComputeEdgeValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t row = 0; row < rowCount; ++row)
|
||||||
|
{
|
||||||
|
const std::size_t nextRow =
|
||||||
|
(row == static_cast<std::size_t>(rasterData->number_of_rows() - 1)) ?
|
||||||
|
0 :
|
||||||
|
row + 1;
|
||||||
|
|
||||||
|
const auto& dataMomentsArray8 =
|
||||||
rasterData->level(static_cast<uint16_t>(row));
|
rasterData->level(static_cast<uint16_t>(row));
|
||||||
|
const auto& nextDataMomentsArray8 =
|
||||||
|
rasterData->level(static_cast<uint16_t>(nextRow));
|
||||||
|
|
||||||
for (size_t bin = 0; bin < dataMomentsArray8.size(); ++bin)
|
for (size_t bin = 0; bin < dataMomentsArray8.size(); ++bin)
|
||||||
{
|
{
|
||||||
constexpr size_t vertexCount = 6;
|
if (!smoothingEnabled)
|
||||||
|
|
||||||
// Store data moment value
|
|
||||||
uint8_t dataValue = dataMomentsArray8[bin];
|
|
||||||
if (dataValue < snrThreshold && dataValue != RANGE_FOLDED)
|
|
||||||
{
|
{
|
||||||
continue;
|
static constexpr std::size_t vertexCount = 6;
|
||||||
|
|
||||||
|
// Store data moment value
|
||||||
|
const std::uint8_t& dataValue = dataMomentsArray8[bin];
|
||||||
|
if (dataValue < snrThreshold && dataValue != RANGE_FOLDED)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t m = 0; m < vertexCount; m++)
|
||||||
|
{
|
||||||
|
dataMoments8[mIndex++] = dataValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
for (size_t m = 0; m < vertexCount; m++)
|
|
||||||
{
|
{
|
||||||
dataMoments8[mIndex++] = dataValue;
|
// Validate indices are all in range
|
||||||
|
if (bin + 1 >= dataMomentsArray8.size() ||
|
||||||
|
bin + 1 >= nextDataMomentsArray8.size())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint8_t& dm1 = dataMomentsArray8[bin];
|
||||||
|
const std::uint8_t& dm2 = dataMomentsArray8[bin + 1];
|
||||||
|
const std::uint8_t& dm3 = nextDataMomentsArray8[bin];
|
||||||
|
const std::uint8_t& dm4 = nextDataMomentsArray8[bin + 1];
|
||||||
|
|
||||||
|
if ((!showSmoothedRangeFolding && //
|
||||||
|
(dm1 < snrThreshold || dm1 == RANGE_FOLDED) &&
|
||||||
|
(dm2 < snrThreshold || dm2 == RANGE_FOLDED) &&
|
||||||
|
(dm3 < snrThreshold || dm3 == RANGE_FOLDED) &&
|
||||||
|
(dm4 < snrThreshold || dm4 == RANGE_FOLDED)) ||
|
||||||
|
(showSmoothedRangeFolding && //
|
||||||
|
dm1 < snrThreshold && dm1 != RANGE_FOLDED &&
|
||||||
|
dm2 < snrThreshold && dm2 != RANGE_FOLDED &&
|
||||||
|
dm3 < snrThreshold && dm3 != RANGE_FOLDED &&
|
||||||
|
dm4 < snrThreshold && dm4 != RANGE_FOLDED))
|
||||||
|
{
|
||||||
|
// Skip only if all data moments are hidden
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The order must match the store vertices section below
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm1);
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm2);
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm4);
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm1);
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm3);
|
||||||
|
dataMoments8[mIndex++] = p->RemapDataMoment(dm4);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store vertices
|
// Store vertices
|
||||||
|
|
@ -332,17 +409,17 @@ void Level3RasterView::ComputeSweep()
|
||||||
vertices[vIndex++] = coordinates[offset2];
|
vertices[vIndex++] = coordinates[offset2];
|
||||||
vertices[vIndex++] = coordinates[offset2 + 1];
|
vertices[vIndex++] = coordinates[offset2 + 1];
|
||||||
|
|
||||||
vertices[vIndex++] = coordinates[offset3];
|
vertices[vIndex++] = coordinates[offset4];
|
||||||
vertices[vIndex++] = coordinates[offset3 + 1];
|
vertices[vIndex++] = coordinates[offset4 + 1];
|
||||||
|
|
||||||
|
vertices[vIndex++] = coordinates[offset1];
|
||||||
|
vertices[vIndex++] = coordinates[offset1 + 1];
|
||||||
|
|
||||||
vertices[vIndex++] = coordinates[offset3];
|
vertices[vIndex++] = coordinates[offset3];
|
||||||
vertices[vIndex++] = coordinates[offset3 + 1];
|
vertices[vIndex++] = coordinates[offset3 + 1];
|
||||||
|
|
||||||
vertices[vIndex++] = coordinates[offset4];
|
vertices[vIndex++] = coordinates[offset4];
|
||||||
vertices[vIndex++] = coordinates[offset4 + 1];
|
vertices[vIndex++] = coordinates[offset4 + 1];
|
||||||
|
|
||||||
vertices[vIndex++] = coordinates[offset2];
|
|
||||||
vertices[vIndex++] = coordinates[offset2 + 1];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vertices.resize(vIndex);
|
vertices.resize(vIndex);
|
||||||
|
|
@ -359,6 +436,20 @@ void Level3RasterView::ComputeSweep()
|
||||||
Q_EMIT SweepComputed();
|
Q_EMIT SweepComputed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::uint8_t
|
||||||
|
Level3RasterViewImpl::RemapDataMoment(std::uint8_t dataMoment) const
|
||||||
|
{
|
||||||
|
if (dataMoment != 0 &&
|
||||||
|
(dataMoment != RANGE_FOLDED || showSmoothedRangeFolding_))
|
||||||
|
{
|
||||||
|
return dataMoment;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return edgeValue_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::uint16_t>
|
std::optional<std::uint16_t>
|
||||||
Level3RasterView::GetBinLevel(const common::Coordinate& coordinate) const
|
Level3RasterView::GetBinLevel(const common::Coordinate& coordinate) const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include <scwx/qt/view/radar_product_view.hpp>
|
#include <scwx/qt/view/radar_product_view.hpp>
|
||||||
|
#include <scwx/qt/settings/product_settings.hpp>
|
||||||
#include <scwx/common/constants.hpp>
|
#include <scwx/common/constants.hpp>
|
||||||
#include <scwx/util/logger.hpp>
|
#include <scwx/util/logger.hpp>
|
||||||
|
|
||||||
|
|
@ -28,26 +29,44 @@ class RadarProductViewImpl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit RadarProductViewImpl(
|
explicit RadarProductViewImpl(
|
||||||
|
RadarProductView* self,
|
||||||
std::shared_ptr<manager::RadarProductManager> radarProductManager) :
|
std::shared_ptr<manager::RadarProductManager> radarProductManager) :
|
||||||
|
self_ {self},
|
||||||
initialized_ {false},
|
initialized_ {false},
|
||||||
sweepMutex_ {},
|
sweepMutex_ {},
|
||||||
selectedTime_ {},
|
selectedTime_ {},
|
||||||
radarProductManager_ {radarProductManager}
|
radarProductManager_ {radarProductManager}
|
||||||
{
|
{
|
||||||
|
auto& productSettings = settings::ProductSettings::Instance();
|
||||||
|
connection_ = productSettings.changed_signal().connect(
|
||||||
|
[this]()
|
||||||
|
{
|
||||||
|
showSmoothedRangeFolding_ = settings::ProductSettings::Instance()
|
||||||
|
.show_smoothed_range_folding()
|
||||||
|
.GetValue();
|
||||||
|
self_->Update();
|
||||||
|
});
|
||||||
|
;
|
||||||
}
|
}
|
||||||
~RadarProductViewImpl() {}
|
~RadarProductViewImpl() {}
|
||||||
|
|
||||||
|
RadarProductView* self_;
|
||||||
|
|
||||||
bool initialized_;
|
bool initialized_;
|
||||||
std::mutex sweepMutex_;
|
std::mutex sweepMutex_;
|
||||||
|
|
||||||
std::chrono::system_clock::time_point selectedTime_;
|
std::chrono::system_clock::time_point selectedTime_;
|
||||||
|
bool showSmoothedRangeFolding_ {false};
|
||||||
|
bool smoothingEnabled_ {false};
|
||||||
|
|
||||||
std::shared_ptr<manager::RadarProductManager> radarProductManager_;
|
std::shared_ptr<manager::RadarProductManager> radarProductManager_;
|
||||||
|
|
||||||
|
boost::signals2::scoped_connection connection_;
|
||||||
};
|
};
|
||||||
|
|
||||||
RadarProductView::RadarProductView(
|
RadarProductView::RadarProductView(
|
||||||
std::shared_ptr<manager::RadarProductManager> radarProductManager) :
|
std::shared_ptr<manager::RadarProductManager> radarProductManager) :
|
||||||
p(std::make_unique<RadarProductViewImpl>(radarProductManager)) {};
|
p(std::make_unique<RadarProductViewImpl>(this, radarProductManager)) {};
|
||||||
RadarProductView::~RadarProductView() = default;
|
RadarProductView::~RadarProductView() = default;
|
||||||
|
|
||||||
const std::vector<boost::gil::rgba8_pixel_t>&
|
const std::vector<boost::gil::rgba8_pixel_t>&
|
||||||
|
|
@ -87,6 +106,16 @@ std::chrono::system_clock::time_point RadarProductView::selected_time() const
|
||||||
return p->selectedTime_;
|
return p->selectedTime_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RadarProductView::show_smoothed_range_folding() const
|
||||||
|
{
|
||||||
|
return p->showSmoothedRangeFolding_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RadarProductView::smoothing_enabled() const
|
||||||
|
{
|
||||||
|
return p->smoothingEnabled_;
|
||||||
|
}
|
||||||
|
|
||||||
std::chrono::system_clock::time_point RadarProductView::sweep_time() const
|
std::chrono::system_clock::time_point RadarProductView::sweep_time() const
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
|
|
@ -105,6 +134,11 @@ void RadarProductView::set_radar_product_manager(
|
||||||
ConnectRadarProductManager();
|
ConnectRadarProductManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RadarProductView::set_smoothing_enabled(bool smoothingEnabled)
|
||||||
|
{
|
||||||
|
p->smoothingEnabled_ = smoothingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
void RadarProductView::Initialize()
|
void RadarProductView::Initialize()
|
||||||
{
|
{
|
||||||
ComputeSweep();
|
ComputeSweep();
|
||||||
|
|
|
||||||
|
|
@ -47,12 +47,16 @@ public:
|
||||||
virtual std::uint16_t vcp() const = 0;
|
virtual std::uint16_t vcp() const = 0;
|
||||||
virtual const std::vector<float>& vertices() const = 0;
|
virtual const std::vector<float>& vertices() const = 0;
|
||||||
|
|
||||||
std::shared_ptr<manager::RadarProductManager> radar_product_manager() const;
|
[[nodiscard]] std::shared_ptr<manager::RadarProductManager>
|
||||||
std::chrono::system_clock::time_point selected_time() const;
|
radar_product_manager() const;
|
||||||
std::mutex& sweep_mutex();
|
[[nodiscard]] std::chrono::system_clock::time_point selected_time() const;
|
||||||
|
[[nodiscard]] bool show_smoothed_range_folding() const;
|
||||||
|
[[nodiscard]] bool smoothing_enabled() const;
|
||||||
|
[[nodiscard]] std::mutex& sweep_mutex();
|
||||||
|
|
||||||
void set_radar_product_manager(
|
void set_radar_product_manager(
|
||||||
std::shared_ptr<manager::RadarProductManager> radarProductManager);
|
std::shared_ptr<manager::RadarProductManager> radarProductManager);
|
||||||
|
void set_smoothing_enabled(bool smoothingEnabled);
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
virtual void
|
virtual void
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit eaf8f185ce2b3a3248da1a4d6c8e2e9265638f15
|
Subproject commit 0eb475909f9e64ce81e7b8b39420d980b81b3baa
|
||||||
Loading…
Add table
Add a link
Reference in a new issue