mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 20:30:05 +00:00
Compare commits
43 commits
de43670ec2
...
d218d05184
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d218d05184 | ||
|
|
2cc5e48102 | ||
|
|
4502b59830 | ||
|
|
eed2736dde | ||
|
|
d687d04d76 | ||
|
|
54fd86de5c | ||
|
|
b8e9cb659e | ||
|
|
94dc5b547d | ||
|
|
2dc69544ec | ||
|
|
f8ebd265e0 | ||
|
|
a43c2df13f | ||
|
|
a952d890e6 | ||
|
|
9416ff1546 | ||
|
|
cb749e7b9e | ||
|
|
4fdf52b9b4 | ||
|
|
76a74922c5 | ||
|
|
f34d11a7ea | ||
|
|
3fe7dd9eed | ||
|
|
985473a0a4 | ||
|
|
7fbd9e45a9 | ||
|
|
889b6e81be | ||
|
|
acfb515e10 | ||
|
|
22a6ed33c1 | ||
|
|
95b9a03437 | ||
|
|
77ae293e87 | ||
|
|
d6834127db | ||
|
|
1f8cd8ee39 | ||
|
|
b0c7554f47 | ||
|
|
403a556b30 | ||
|
|
2beac20ee3 | ||
|
|
07adbb382d | ||
|
|
41bf7f731f | ||
|
|
931dd2d0a7 | ||
|
|
341096af1d | ||
|
|
f679f37fc1 | ||
|
|
a306fb4363 | ||
|
|
449d8cb796 | ||
|
|
f4226b487d | ||
|
|
68f66c0c2f | ||
|
|
df0d698837 | ||
|
|
b6797eee7e | ||
|
|
3c5c9ea27e | ||
|
|
0e21884322 |
32 changed files with 868 additions and 355 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
|
@ -127,7 +127,7 @@ jobs:
|
||||||
env:
|
env:
|
||||||
CC: ${{ matrix.env_cc }}
|
CC: ${{ matrix.env_cc }}
|
||||||
CXX: ${{ matrix.env_cxx }}
|
CXX: ${{ matrix.env_cxx }}
|
||||||
SCWX_VERSION: v0.5.1
|
SCWX_VERSION: v0.5.2
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0)
|
||||||
scwx_python_setup()
|
scwx_python_setup()
|
||||||
|
|
||||||
project(${PROJECT_NAME}
|
project(${PROJECT_NAME}
|
||||||
VERSION 0.5.1
|
VERSION 0.5.2
|
||||||
DESCRIPTION "Supercell Wx is a free, open source advanced weather radar viewer."
|
DESCRIPTION "Supercell Wx is a free, open source advanced weather radar viewer."
|
||||||
HOMEPAGE_URL "https://github.com/dpaulat/supercell-wx"
|
HOMEPAGE_URL "https://github.com/dpaulat/supercell-wx"
|
||||||
LANGUAGES C CXX)
|
LANGUAGES C CXX)
|
||||||
|
|
@ -32,7 +32,7 @@ set_property(DIRECTORY
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBOOST_ALL_NO_LIB")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBOOST_ALL_NO_LIB")
|
||||||
|
|
||||||
set(SCWX_DIR ${PROJECT_SOURCE_DIR})
|
set(SCWX_DIR ${PROJECT_SOURCE_DIR})
|
||||||
set(SCWX_VERSION "0.5.1")
|
set(SCWX_VERSION "0.5.2")
|
||||||
|
|
||||||
option(SCWX_ADDRESS_SANITIZER "Build with Address Sanitizer" OFF)
|
option(SCWX_ADDRESS_SANITIZER "Build with Address Sanitizer" OFF)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,7 @@ set(HDR_TYPES source/scwx/qt/types/alert_types.hpp
|
||||||
source/scwx/qt/types/media_types.hpp
|
source/scwx/qt/types/media_types.hpp
|
||||||
source/scwx/qt/types/qt_types.hpp
|
source/scwx/qt/types/qt_types.hpp
|
||||||
source/scwx/qt/types/radar_product_record.hpp
|
source/scwx/qt/types/radar_product_record.hpp
|
||||||
|
source/scwx/qt/types/radar_product_types.hpp
|
||||||
source/scwx/qt/types/text_event_key.hpp
|
source/scwx/qt/types/text_event_key.hpp
|
||||||
source/scwx/qt/types/text_types.hpp
|
source/scwx/qt/types/text_types.hpp
|
||||||
source/scwx/qt/types/texture_types.hpp
|
source/scwx/qt/types/texture_types.hpp
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QLibraryInfo>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QStyleHints>
|
#include <QStyleHints>
|
||||||
#include <QSurfaceFormat>
|
#include <QSurfaceFormat>
|
||||||
|
|
@ -71,6 +72,8 @@ int main(int argc, char* argv[])
|
||||||
scwx::qt::main::kVersionString_,
|
scwx::qt::main::kVersionString_,
|
||||||
scwx::qt::main::kBuildNumber_,
|
scwx::qt::main::kBuildNumber_,
|
||||||
scwx::qt::main::kCommitString_);
|
scwx::qt::main::kCommitString_);
|
||||||
|
logger_->info("Qt version {}",
|
||||||
|
QLibraryInfo::version().toString().toStdString());
|
||||||
|
|
||||||
InitializeOpenGL();
|
InitializeOpenGL();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -463,18 +463,28 @@ void MainWindow::keyReleaseEvent(QKeyEvent* ev)
|
||||||
void MainWindow::showEvent(QShowEvent* event)
|
void MainWindow::showEvent(QShowEvent* event)
|
||||||
{
|
{
|
||||||
QMainWindow::showEvent(event);
|
QMainWindow::showEvent(event);
|
||||||
auto& uiSettings = settings::UiSettings::Instance();
|
|
||||||
|
|
||||||
// restore the geometry state
|
static bool firstShowEvent = true;
|
||||||
std::string uiGeometry = uiSettings.main_ui_geometry().GetValue();
|
bool restored = false;
|
||||||
restoreGeometry(
|
|
||||||
QByteArray::fromBase64(QByteArray::fromStdString(uiGeometry)));
|
|
||||||
|
|
||||||
// restore the UI state
|
if (firstShowEvent)
|
||||||
std::string uiState = uiSettings.main_ui_state().GetValue();
|
{
|
||||||
|
auto& uiSettings = settings::UiSettings::Instance();
|
||||||
|
|
||||||
|
// restore the geometry state
|
||||||
|
const std::string uiGeometry = uiSettings.main_ui_geometry().GetValue();
|
||||||
|
restoreGeometry(
|
||||||
|
QByteArray::fromBase64(QByteArray::fromStdString(uiGeometry)));
|
||||||
|
|
||||||
|
// restore the UI state
|
||||||
|
const std::string uiState = uiSettings.main_ui_state().GetValue();
|
||||||
|
|
||||||
|
restored = restoreState(
|
||||||
|
QByteArray::fromBase64(QByteArray::fromStdString(uiState)));
|
||||||
|
|
||||||
|
firstShowEvent = false;
|
||||||
|
}
|
||||||
|
|
||||||
bool restored =
|
|
||||||
restoreState(QByteArray::fromBase64(QByteArray::fromStdString(uiState)));
|
|
||||||
if (!restored)
|
if (!restored)
|
||||||
{
|
{
|
||||||
resizeDocks({ui->radarToolboxDock}, {194}, Qt::Horizontal);
|
resizeDocks({ui->radarToolboxDock}, {194}, Qt::Horizontal);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include <execution>
|
#include <execution>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
|
@ -36,11 +37,7 @@
|
||||||
# pragma warning(pop)
|
# pragma warning(pop)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace scwx
|
namespace scwx::qt::manager
|
||||||
{
|
|
||||||
namespace qt
|
|
||||||
{
|
|
||||||
namespace manager
|
|
||||||
{
|
{
|
||||||
|
|
||||||
static const std::string logPrefix_ =
|
static const std::string logPrefix_ =
|
||||||
|
|
@ -109,21 +106,27 @@ public:
|
||||||
group, product, isChunks_, latestTime);
|
group, product, isChunks_, latestTime);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
~ProviderManager() { threadPool_.join(); };
|
~ProviderManager() override
|
||||||
|
{
|
||||||
|
providerThreadPool_.stop();
|
||||||
|
providerThreadPool_.join();
|
||||||
|
};
|
||||||
|
|
||||||
std::string name() const;
|
std::string name() const;
|
||||||
|
|
||||||
void Disable();
|
void Disable();
|
||||||
|
void RefreshData();
|
||||||
|
void RefreshDataSync();
|
||||||
|
|
||||||
boost::asio::thread_pool threadPool_ {1u};
|
boost::asio::thread_pool providerThreadPool_ {2u};
|
||||||
|
|
||||||
const std::string radarId_;
|
const std::string radarId_;
|
||||||
const common::RadarProductGroup group_;
|
const common::RadarProductGroup group_;
|
||||||
const std::string product_;
|
const std::string product_;
|
||||||
const bool isChunks_;
|
const bool isChunks_;
|
||||||
bool refreshEnabled_ {false};
|
bool refreshEnabled_ {false};
|
||||||
boost::asio::steady_timer refreshTimer_ {threadPool_};
|
boost::asio::steady_timer refreshTimer_ {providerThreadPool_};
|
||||||
std::mutex refreshTimerMutex_ {};
|
std::mutex refreshTimerMutex_ {};
|
||||||
std::shared_ptr<provider::NexradDataProvider> provider_ {nullptr};
|
std::shared_ptr<provider::NexradDataProvider> provider_ {nullptr};
|
||||||
size_t refreshCount_ {0};
|
size_t refreshCount_ {0};
|
||||||
|
|
||||||
|
|
@ -184,11 +187,9 @@ public:
|
||||||
auto& [key, providerManager] = p;
|
auto& [key, providerManager] = p;
|
||||||
providerManager->Disable();
|
providerManager->Disable();
|
||||||
});
|
});
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
// Lock other mutexes before destroying, ensure loading is complete
|
threadPool_.stop();
|
||||||
std::unique_lock loadLevel2DataLock {loadLevel2DataMutex_};
|
|
||||||
std::unique_lock loadLevel3DataLock {loadLevel3DataMutex_};
|
|
||||||
|
|
||||||
threadPool_.join();
|
threadPool_.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -203,14 +204,14 @@ public:
|
||||||
boost::uuids::uuid uuid,
|
boost::uuids::uuid uuid,
|
||||||
const std::set<std::shared_ptr<ProviderManager>>& providerManagers,
|
const std::set<std::shared_ptr<ProviderManager>>& providerManagers,
|
||||||
bool enabled);
|
bool enabled);
|
||||||
void RefreshData(std::shared_ptr<ProviderManager> providerManager);
|
|
||||||
void RefreshDataSync(std::shared_ptr<ProviderManager> providerManager);
|
|
||||||
|
|
||||||
std::map<std::chrono::system_clock::time_point,
|
std::tuple<std::map<std::chrono::system_clock::time_point,
|
||||||
std::shared_ptr<types::RadarProductRecord>>
|
std::shared_ptr<types::RadarProductRecord>>,
|
||||||
|
types::RadarProductLoadStatus>
|
||||||
GetLevel2ProductRecords(std::chrono::system_clock::time_point time);
|
GetLevel2ProductRecords(std::chrono::system_clock::time_point time);
|
||||||
std::tuple<std::shared_ptr<types::RadarProductRecord>,
|
std::tuple<std::shared_ptr<types::RadarProductRecord>,
|
||||||
std::chrono::system_clock::time_point>
|
std::chrono::system_clock::time_point,
|
||||||
|
types::RadarProductLoadStatus>
|
||||||
GetLevel3ProductRecord(const std::string& product,
|
GetLevel3ProductRecord(const std::string& product,
|
||||||
std::chrono::system_clock::time_point time);
|
std::chrono::system_clock::time_point time);
|
||||||
std::shared_ptr<types::RadarProductRecord>
|
std::shared_ptr<types::RadarProductRecord>
|
||||||
|
|
@ -224,15 +225,24 @@ public:
|
||||||
std::mutex& mutex,
|
std::mutex& mutex,
|
||||||
std::chrono::system_clock::time_point time);
|
std::chrono::system_clock::time_point time);
|
||||||
void
|
void
|
||||||
LoadProviderData(std::chrono::system_clock::time_point time,
|
LoadProviderData(std::chrono::system_clock::time_point time,
|
||||||
std::shared_ptr<ProviderManager> providerManager,
|
std::shared_ptr<ProviderManager> providerManager,
|
||||||
RadarProductRecordMap& recordMap,
|
RadarProductRecordMap& recordMap,
|
||||||
std::shared_mutex& recordMutex,
|
std::shared_mutex& recordMutex,
|
||||||
std::mutex& loadDataMutex,
|
std::mutex& loadDataMutex,
|
||||||
const std::shared_ptr<request::NexradFileRequest>& request);
|
const std::shared_ptr<request::NexradFileRequest>& request);
|
||||||
void PopulateLevel2ProductTimes(std::chrono::system_clock::time_point time);
|
|
||||||
|
bool AreLevel2ProductTimesPopulated(
|
||||||
|
std::chrono::system_clock::time_point time) const;
|
||||||
|
bool
|
||||||
|
AreLevel3ProductTimesPopulated(const std::string& product,
|
||||||
|
std::chrono::system_clock::time_point time);
|
||||||
|
|
||||||
|
void PopulateLevel2ProductTimes(std::chrono::system_clock::time_point time,
|
||||||
|
bool update = true);
|
||||||
void PopulateLevel3ProductTimes(const std::string& product,
|
void PopulateLevel3ProductTimes(const std::string& product,
|
||||||
std::chrono::system_clock::time_point time);
|
std::chrono::system_clock::time_point time,
|
||||||
|
bool update = true);
|
||||||
|
|
||||||
void UpdateAvailableProductsSync();
|
void UpdateAvailableProductsSync();
|
||||||
|
|
||||||
|
|
@ -243,11 +253,16 @@ public:
|
||||||
const float gateRangeOffset,
|
const float gateRangeOffset,
|
||||||
std::vector<float>& outputCoordinates);
|
std::vector<float>& outputCoordinates);
|
||||||
|
|
||||||
|
static bool AreProductTimesPopulated(
|
||||||
|
const std::shared_ptr<ProviderManager>& providerManager,
|
||||||
|
std::chrono::system_clock::time_point time);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
PopulateProductTimes(std::shared_ptr<ProviderManager> providerManager,
|
PopulateProductTimes(std::shared_ptr<ProviderManager> providerManager,
|
||||||
RadarProductRecordMap& productRecordMap,
|
RadarProductRecordMap& productRecordMap,
|
||||||
std::shared_mutex& productRecordMutex,
|
std::shared_mutex& productRecordMutex,
|
||||||
std::chrono::system_clock::time_point time);
|
std::chrono::system_clock::time_point time,
|
||||||
|
bool update);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
LoadNexradFile(CreateNexradFileFunction load,
|
LoadNexradFile(CreateNexradFileFunction load,
|
||||||
|
|
@ -771,28 +786,27 @@ void RadarProductManagerImpl::EnableRefresh(
|
||||||
if (providerManager->refreshEnabled_ != enabled)
|
if (providerManager->refreshEnabled_ != enabled)
|
||||||
{
|
{
|
||||||
providerManager->refreshEnabled_ = enabled;
|
providerManager->refreshEnabled_ = enabled;
|
||||||
RefreshData(providerManager);
|
providerManager->RefreshData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadarProductManagerImpl::RefreshData(
|
void ProviderManager::RefreshData()
|
||||||
std::shared_ptr<ProviderManager> providerManager)
|
|
||||||
{
|
{
|
||||||
logger_->trace("RefreshData: {}", providerManager->name());
|
logger_->trace("RefreshData: {}", name());
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock lock(providerManager->refreshTimerMutex_);
|
const std::unique_lock lock(refreshTimerMutex_);
|
||||||
providerManager->refreshTimer_.cancel();
|
refreshTimer_.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::asio::post(threadPool_,
|
boost::asio::post(providerThreadPool_,
|
||||||
[=, this]()
|
[this]()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RefreshDataSync(providerManager);
|
RefreshDataSync();
|
||||||
}
|
}
|
||||||
catch (const std::exception& ex)
|
catch (const std::exception& ex)
|
||||||
{
|
{
|
||||||
|
|
@ -801,27 +815,24 @@ void RadarProductManagerImpl::RefreshData(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadarProductManagerImpl::RefreshDataSync(
|
void ProviderManager::RefreshDataSync()
|
||||||
std::shared_ptr<ProviderManager> providerManager)
|
|
||||||
{
|
{
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
auto [newObjects, totalObjects] = providerManager->provider_->Refresh();
|
auto [newObjects, totalObjects] = provider_->Refresh();
|
||||||
|
|
||||||
// Level2 chunked data is updated quickly and uses a faster interval
|
// Level2 chunked data is updated quickly and uses a faster interval
|
||||||
const std::chrono::milliseconds fastRetryInterval =
|
const std::chrono::milliseconds fastRetryInterval =
|
||||||
providerManager->isChunks_ ? kFastRetryIntervalChunks_ :
|
isChunks_ ? kFastRetryIntervalChunks_ : kFastRetryInterval_;
|
||||||
kFastRetryInterval_;
|
|
||||||
const std::chrono::milliseconds slowRetryInterval =
|
const std::chrono::milliseconds slowRetryInterval =
|
||||||
providerManager->isChunks_ ? kSlowRetryIntervalChunks_ :
|
isChunks_ ? kSlowRetryIntervalChunks_ : kSlowRetryInterval_;
|
||||||
kSlowRetryInterval_;
|
|
||||||
std::chrono::milliseconds interval = fastRetryInterval;
|
std::chrono::milliseconds interval = fastRetryInterval;
|
||||||
|
|
||||||
if (totalObjects > 0)
|
if (totalObjects > 0)
|
||||||
{
|
{
|
||||||
auto latestTime = providerManager->provider_->FindLatestTime();
|
auto latestTime = provider_->FindLatestTime();
|
||||||
auto updatePeriod = providerManager->provider_->update_period();
|
auto updatePeriod = provider_->update_period();
|
||||||
auto lastModified = providerManager->provider_->last_modified();
|
auto lastModified = provider_->last_modified();
|
||||||
auto sinceLastModified = scwx::util::time::now() - lastModified;
|
auto sinceLastModified = scwx::util::time::now() - lastModified;
|
||||||
|
|
||||||
// For the default interval, assume products are updated at a
|
// For the default interval, assume products are updated at a
|
||||||
|
|
@ -830,7 +841,11 @@ void RadarProductManagerImpl::RefreshDataSync(
|
||||||
interval = std::chrono::duration_cast<std::chrono::milliseconds>(
|
interval = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
updatePeriod - sinceLastModified);
|
updatePeriod - sinceLastModified);
|
||||||
|
|
||||||
if (updatePeriod > 0s && sinceLastModified > updatePeriod * 5)
|
// Allow 5 update periods before considering the data stale
|
||||||
|
constexpr std::size_t kUpdatePeriodStaleCount = 5;
|
||||||
|
|
||||||
|
if (updatePeriod > 0s &&
|
||||||
|
sinceLastModified > updatePeriod * kUpdatePeriodStaleCount)
|
||||||
{
|
{
|
||||||
// If it has been at least 5 update periods since the file has
|
// If it has been at least 5 update periods since the file has
|
||||||
// been last modified, slow the retry period
|
// been last modified, slow the retry period
|
||||||
|
|
@ -844,46 +859,43 @@ void RadarProductManagerImpl::RefreshDataSync(
|
||||||
|
|
||||||
if (newObjects > 0)
|
if (newObjects > 0)
|
||||||
{
|
{
|
||||||
Q_EMIT providerManager->NewDataAvailable(
|
Q_EMIT NewDataAvailable(group_, product_, latestTime);
|
||||||
providerManager->group_, providerManager->product_, latestTime);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (providerManager->refreshEnabled_)
|
else if (refreshEnabled_)
|
||||||
{
|
{
|
||||||
logger_->info("[{}] No data found", providerManager->name());
|
logger_->info("[{}] No data found", name());
|
||||||
|
|
||||||
// If no data is found, retry at the slow retry interval
|
// If no data is found, retry at the slow retry interval
|
||||||
interval = slowRetryInterval;
|
interval = slowRetryInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_lock const lock(providerManager->refreshTimerMutex_);
|
std::unique_lock const lock(refreshTimerMutex_);
|
||||||
|
|
||||||
if (providerManager->refreshEnabled_)
|
if (refreshEnabled_)
|
||||||
{
|
{
|
||||||
logger_->trace(
|
logger_->trace(
|
||||||
"[{}] Scheduled refresh in {:%M:%S}",
|
"[{}] Scheduled refresh in {:%M:%S}",
|
||||||
providerManager->name(),
|
name(),
|
||||||
std::chrono::duration_cast<std::chrono::seconds>(interval));
|
std::chrono::duration_cast<std::chrono::seconds>(interval));
|
||||||
|
|
||||||
{
|
{
|
||||||
providerManager->refreshTimer_.expires_after(interval);
|
refreshTimer_.expires_after(interval);
|
||||||
providerManager->refreshTimer_.async_wait(
|
refreshTimer_.async_wait(
|
||||||
[=, this](const boost::system::error_code& e)
|
[this](const boost::system::error_code& e)
|
||||||
{
|
{
|
||||||
if (e == boost::system::errc::success)
|
if (e == boost::system::errc::success)
|
||||||
{
|
{
|
||||||
RefreshData(providerManager);
|
RefreshData();
|
||||||
}
|
}
|
||||||
else if (e == boost::asio::error::operation_aborted)
|
else if (e == boost::asio::error::operation_aborted)
|
||||||
{
|
{
|
||||||
logger_->debug("[{}] Data refresh timer cancelled",
|
logger_->debug("[{}] Data refresh timer cancelled", name());
|
||||||
providerManager->name());
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger_->warn("[{}] Data refresh timer error: {}",
|
logger_->warn(
|
||||||
providerManager->name(),
|
"[{}] Data refresh timer error: {}", name(), e.message());
|
||||||
e.message());
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -934,32 +946,32 @@ RadarProductManager::GetActiveVolumeTimes(
|
||||||
[&](const std::shared_ptr<provider::NexradDataProvider>& provider)
|
[&](const std::shared_ptr<provider::NexradDataProvider>& provider)
|
||||||
{
|
{
|
||||||
// For yesterday, today and tomorrow (in parallel)
|
// For yesterday, today and tomorrow (in parallel)
|
||||||
std::for_each(std::execution::par,
|
std::for_each(
|
||||||
dates.begin(),
|
std::execution::par,
|
||||||
dates.end(),
|
dates.begin(),
|
||||||
[&](const auto& date)
|
dates.end(),
|
||||||
{
|
[&](const auto& date)
|
||||||
// Don't query for a time point in the future
|
{
|
||||||
if (date > scwx::util::time::now())
|
// Don't query for a time point in the future
|
||||||
{
|
if (date > scwx::util::time::now())
|
||||||
return;
|
{
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Query the provider for volume time points
|
// Query the provider for volume time points
|
||||||
auto timePoints = provider->GetTimePointsByDate(date);
|
auto timePoints = provider->GetTimePointsByDate(date, true);
|
||||||
|
|
||||||
// TODO: Note, this will miss volume times present in
|
// TODO: Note, this will miss volume times present in Level 2
|
||||||
// Level 2 products with a second scan
|
// products with a second scan
|
||||||
|
|
||||||
// Lock the merged volume time list
|
// Lock the merged volume time list
|
||||||
std::unique_lock volumeTimesLock {volumeTimesMutex};
|
const std::unique_lock volumeTimesLock {volumeTimesMutex};
|
||||||
|
|
||||||
// Copy time points to the merged list
|
// Copy time points to the merged list
|
||||||
std::copy(
|
std::copy(timePoints.begin(),
|
||||||
timePoints.begin(),
|
timePoints.end(),
|
||||||
timePoints.end(),
|
std::inserter(volumeTimes, volumeTimes.end()));
|
||||||
std::inserter(volumeTimes, volumeTimes.end()));
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Return merged volume times list
|
// Return merged volume times list
|
||||||
|
|
@ -1202,21 +1214,75 @@ void RadarProductManagerImpl::LoadNexradFile(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RadarProductManagerImpl::AreLevel2ProductTimesPopulated(
|
||||||
|
std::chrono::system_clock::time_point time) const
|
||||||
|
{
|
||||||
|
return AreProductTimesPopulated(level2ProviderManager_, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RadarProductManagerImpl::AreLevel3ProductTimesPopulated(
|
||||||
|
const std::string& product, std::chrono::system_clock::time_point time)
|
||||||
|
{
|
||||||
|
// Get provider manager
|
||||||
|
const auto level3ProviderManager = GetLevel3ProviderManager(product);
|
||||||
|
|
||||||
|
return AreProductTimesPopulated(level3ProviderManager, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RadarProductManagerImpl::AreProductTimesPopulated(
|
||||||
|
const std::shared_ptr<ProviderManager>& providerManager,
|
||||||
|
std::chrono::system_clock::time_point time)
|
||||||
|
{
|
||||||
|
auto today = std::chrono::floor<std::chrono::days>(time);
|
||||||
|
|
||||||
|
bool productTimesPopulated = true;
|
||||||
|
|
||||||
|
// Assume a query for the epoch is a query for now
|
||||||
|
if (today == std::chrono::system_clock::time_point {})
|
||||||
|
{
|
||||||
|
today = std::chrono::floor<std::chrono::days>(scwx::util::time::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto yesterday = today - std::chrono::days {1};
|
||||||
|
const auto tomorrow = today + std::chrono::days {1};
|
||||||
|
const auto dates = {yesterday, today, tomorrow};
|
||||||
|
|
||||||
|
for (auto& date : dates)
|
||||||
|
{
|
||||||
|
// Don't query for a time point in the future
|
||||||
|
if (date > scwx::util::time::now())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!providerManager->provider_->IsDateCached(date))
|
||||||
|
{
|
||||||
|
productTimesPopulated = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return productTimesPopulated;
|
||||||
|
}
|
||||||
|
|
||||||
void RadarProductManagerImpl::PopulateLevel2ProductTimes(
|
void RadarProductManagerImpl::PopulateLevel2ProductTimes(
|
||||||
std::chrono::system_clock::time_point time)
|
std::chrono::system_clock::time_point time, bool update)
|
||||||
{
|
{
|
||||||
PopulateProductTimes(level2ProviderManager_,
|
PopulateProductTimes(level2ProviderManager_,
|
||||||
level2ProductRecords_,
|
level2ProductRecords_,
|
||||||
level2ProductRecordMutex_,
|
level2ProductRecordMutex_,
|
||||||
time);
|
time,
|
||||||
|
update);
|
||||||
PopulateProductTimes(level2ChunksProviderManager_,
|
PopulateProductTimes(level2ChunksProviderManager_,
|
||||||
level2ProductRecords_,
|
level2ProductRecords_,
|
||||||
level2ProductRecordMutex_,
|
level2ProductRecordMutex_,
|
||||||
time);
|
time,
|
||||||
|
update);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadarProductManagerImpl::PopulateLevel3ProductTimes(
|
void RadarProductManagerImpl::PopulateLevel3ProductTimes(
|
||||||
const std::string& product, std::chrono::system_clock::time_point time)
|
const std::string& product,
|
||||||
|
std::chrono::system_clock::time_point time,
|
||||||
|
bool update)
|
||||||
{
|
{
|
||||||
// Get provider manager
|
// Get provider manager
|
||||||
auto level3ProviderManager = GetLevel3ProviderManager(product);
|
auto level3ProviderManager = GetLevel3ProviderManager(product);
|
||||||
|
|
@ -1229,21 +1295,38 @@ void RadarProductManagerImpl::PopulateLevel3ProductTimes(
|
||||||
PopulateProductTimes(level3ProviderManager,
|
PopulateProductTimes(level3ProviderManager,
|
||||||
level3ProductRecords,
|
level3ProductRecords,
|
||||||
level3ProductRecordMutex_,
|
level3ProductRecordMutex_,
|
||||||
time);
|
time,
|
||||||
|
update);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadarProductManagerImpl::PopulateProductTimes(
|
void RadarProductManagerImpl::PopulateProductTimes(
|
||||||
std::shared_ptr<ProviderManager> providerManager,
|
std::shared_ptr<ProviderManager> providerManager,
|
||||||
RadarProductRecordMap& productRecordMap,
|
RadarProductRecordMap& productRecordMap,
|
||||||
std::shared_mutex& productRecordMutex,
|
std::shared_mutex& productRecordMutex,
|
||||||
std::chrono::system_clock::time_point time)
|
std::chrono::system_clock::time_point time,
|
||||||
|
bool update)
|
||||||
{
|
{
|
||||||
const auto today = std::chrono::floor<std::chrono::days>(time);
|
if (update)
|
||||||
|
{
|
||||||
|
logger_->debug("Populating product times: {}, {}, {}",
|
||||||
|
common::GetRadarProductGroupName(providerManager->group_),
|
||||||
|
providerManager->product_,
|
||||||
|
scwx::util::time::TimeString(time));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger_->trace("Populating cached product times: {}, {}, {}",
|
||||||
|
common::GetRadarProductGroupName(providerManager->group_),
|
||||||
|
providerManager->product_,
|
||||||
|
scwx::util::time::TimeString(time));
|
||||||
|
}
|
||||||
|
|
||||||
// Don't query for the epoch
|
auto today = std::chrono::floor<std::chrono::days>(time);
|
||||||
|
|
||||||
|
// Assume a query for the epoch is a query for now
|
||||||
if (today == std::chrono::system_clock::time_point {})
|
if (today == std::chrono::system_clock::time_point {})
|
||||||
{
|
{
|
||||||
return;
|
today = std::chrono::floor<std::chrono::days>(scwx::util::time::now());
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto yesterday = today - std::chrono::days {1};
|
const auto yesterday = today - std::chrono::days {1};
|
||||||
|
|
@ -1267,7 +1350,8 @@ void RadarProductManagerImpl::PopulateProductTimes(
|
||||||
|
|
||||||
// Query the provider for volume time points
|
// Query the provider for volume time points
|
||||||
auto timePoints =
|
auto timePoints =
|
||||||
providerManager->provider_->GetTimePointsByDate(date);
|
providerManager->provider_->GetTimePointsByDate(date,
|
||||||
|
update);
|
||||||
|
|
||||||
// Lock the merged volume time list
|
// Lock the merged volume time list
|
||||||
std::unique_lock volumeTimesLock {volumeTimesMutex};
|
std::unique_lock volumeTimesLock {volumeTimesMutex};
|
||||||
|
|
@ -1293,8 +1377,9 @@ void RadarProductManagerImpl::PopulateProductTimes(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::chrono::system_clock::time_point,
|
std::tuple<std::map<std::chrono::system_clock::time_point,
|
||||||
std::shared_ptr<types::RadarProductRecord>>
|
std::shared_ptr<types::RadarProductRecord>>,
|
||||||
|
types::RadarProductLoadStatus>
|
||||||
RadarProductManagerImpl::GetLevel2ProductRecords(
|
RadarProductManagerImpl::GetLevel2ProductRecords(
|
||||||
std::chrono::system_clock::time_point time)
|
std::chrono::system_clock::time_point time)
|
||||||
{
|
{
|
||||||
|
|
@ -1302,9 +1387,40 @@ RadarProductManagerImpl::GetLevel2ProductRecords(
|
||||||
std::shared_ptr<types::RadarProductRecord>>
|
std::shared_ptr<types::RadarProductRecord>>
|
||||||
records {};
|
records {};
|
||||||
std::vector<RadarProductRecordMap::const_pointer> recordPtrs {};
|
std::vector<RadarProductRecordMap::const_pointer> recordPtrs {};
|
||||||
|
types::RadarProductLoadStatus status {
|
||||||
|
types::RadarProductLoadStatus::ListingProducts};
|
||||||
|
|
||||||
|
std::size_t recordPtrCount = 0u;
|
||||||
|
std::size_t recordCount = 0u;
|
||||||
|
|
||||||
// Ensure Level 2 product records are updated
|
// Ensure Level 2 product records are updated
|
||||||
PopulateLevel2ProductTimes(time);
|
if (!AreLevel2ProductTimesPopulated(time))
|
||||||
|
{
|
||||||
|
logger_->debug("Level 2 product times need populated: {}",
|
||||||
|
scwx::util::time::TimeString(time));
|
||||||
|
|
||||||
|
// Populate level 2 product times asynchronously
|
||||||
|
boost::asio::post(threadPool_,
|
||||||
|
[time, this]()
|
||||||
|
{
|
||||||
|
// Populate product times
|
||||||
|
PopulateLevel2ProductTimes(time);
|
||||||
|
|
||||||
|
// Signal finished
|
||||||
|
Q_EMIT self_->ProductTimesPopulated(
|
||||||
|
common::RadarProductGroup::Level2, "", time);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return listing products status
|
||||||
|
return {records, status};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PopulateLevel2ProductTimes(time, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance to loading product
|
||||||
|
status = types::RadarProductLoadStatus::LoadingProduct;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::shared_lock lock {level2ProductRecordMutex_};
|
std::shared_lock lock {level2ProductRecordMutex_};
|
||||||
|
|
@ -1343,9 +1459,29 @@ RadarProductManagerImpl::GetLevel2ProductRecords(
|
||||||
|
|
||||||
if (recordPtr != nullptr)
|
if (recordPtr != nullptr)
|
||||||
{
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
// Don't check for an exact time match for level 2 products
|
// Don't check for an exact time match for level 2 products
|
||||||
recordTime = recordPtr->first;
|
recordTime = recordPtr->first;
|
||||||
record = recordPtr->second.lock();
|
|
||||||
|
if (
|
||||||
|
// For latest data, ensure it is from the last 24 hours
|
||||||
|
(time == std::chrono::system_clock::time_point {} &&
|
||||||
|
(recordTime > scwx::util::time::now() - 24h ||
|
||||||
|
recordTime == time)) ||
|
||||||
|
// For time queries, ensure data is within 24 hours of the request
|
||||||
|
(time != std::chrono::system_clock::time_point {} &&
|
||||||
|
std::chrono::abs(recordTime - time) < 24h))
|
||||||
|
{
|
||||||
|
record = recordPtr->second.lock();
|
||||||
|
++recordPtrCount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Reset the record
|
||||||
|
recordPtr = nullptr;
|
||||||
|
recordTime = time;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recordPtr != nullptr && record == nullptr &&
|
if (recordPtr != nullptr && record == nullptr &&
|
||||||
|
|
@ -1368,29 +1504,73 @@ RadarProductManagerImpl::GetLevel2ProductRecords(
|
||||||
});
|
});
|
||||||
|
|
||||||
self_->LoadLevel2Data(recordTime, request);
|
self_->LoadLevel2Data(recordTime, request);
|
||||||
|
|
||||||
|
// Status is already set to LoadingProduct
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record != nullptr)
|
if (record != nullptr)
|
||||||
{
|
{
|
||||||
// Return valid records
|
// Return valid records
|
||||||
records.insert_or_assign(recordTime, record);
|
records.insert_or_assign(recordTime, record);
|
||||||
|
++recordCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return records;
|
if (recordPtrCount == 0)
|
||||||
|
{
|
||||||
|
// If all records are empty, the product is not available
|
||||||
|
status = types::RadarProductLoadStatus::ProductNotAvailable;
|
||||||
|
}
|
||||||
|
else if (recordCount == recordPtrCount)
|
||||||
|
{
|
||||||
|
// If all records were populated, the product has been loaded
|
||||||
|
status = types::RadarProductLoadStatus::ProductLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {records, status};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<std::shared_ptr<types::RadarProductRecord>,
|
std::tuple<std::shared_ptr<types::RadarProductRecord>,
|
||||||
std::chrono::system_clock::time_point>
|
std::chrono::system_clock::time_point,
|
||||||
|
types::RadarProductLoadStatus>
|
||||||
RadarProductManagerImpl::GetLevel3ProductRecord(
|
RadarProductManagerImpl::GetLevel3ProductRecord(
|
||||||
const std::string& product, std::chrono::system_clock::time_point time)
|
const std::string& product, std::chrono::system_clock::time_point time)
|
||||||
{
|
{
|
||||||
std::shared_ptr<types::RadarProductRecord> record {nullptr};
|
std::shared_ptr<types::RadarProductRecord> record {nullptr};
|
||||||
RadarProductRecordMap::const_pointer recordPtr {nullptr};
|
RadarProductRecordMap::const_pointer recordPtr {nullptr};
|
||||||
std::chrono::system_clock::time_point recordTime {time};
|
std::chrono::system_clock::time_point recordTime {time};
|
||||||
|
types::RadarProductLoadStatus status {
|
||||||
|
types::RadarProductLoadStatus::ListingProducts};
|
||||||
|
|
||||||
// Ensure Level 3 product records are updated
|
// Ensure Level 3 product records are updated
|
||||||
PopulateLevel3ProductTimes(product, time);
|
if (!AreLevel3ProductTimesPopulated(product, time))
|
||||||
|
{
|
||||||
|
logger_->debug("Level 3 product times need populated: {}, {}",
|
||||||
|
product,
|
||||||
|
scwx::util::time::TimeString(time));
|
||||||
|
|
||||||
|
// Populate level 3 product times asynchronously
|
||||||
|
boost::asio::post(threadPool_,
|
||||||
|
[product, time, this]()
|
||||||
|
{
|
||||||
|
// Populate product times
|
||||||
|
PopulateLevel3ProductTimes(product, time);
|
||||||
|
|
||||||
|
// Signal finished
|
||||||
|
Q_EMIT self_->ProductTimesPopulated(
|
||||||
|
common::RadarProductGroup::Level3, product, time);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return listing products status
|
||||||
|
return {record, recordTime, status};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PopulateLevel3ProductTimes(product, time, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance to loading product
|
||||||
|
status = types::RadarProductLoadStatus::LoadingProduct;
|
||||||
|
|
||||||
std::unique_lock lock {level3ProductRecordMutex_};
|
std::unique_lock lock {level3ProductRecordMutex_};
|
||||||
|
|
||||||
|
|
@ -1415,9 +1595,27 @@ RadarProductManagerImpl::GetLevel3ProductRecord(
|
||||||
|
|
||||||
if (recordPtr != nullptr)
|
if (recordPtr != nullptr)
|
||||||
{
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
// Don't check for an exact time match for level 3 products
|
// Don't check for an exact time match for level 3 products
|
||||||
recordTime = recordPtr->first;
|
recordTime = recordPtr->first;
|
||||||
record = recordPtr->second.lock();
|
|
||||||
|
if (
|
||||||
|
// For latest data, ensure it is from the last 24 hours
|
||||||
|
(time == std::chrono::system_clock::time_point {} &&
|
||||||
|
(recordTime > scwx::util::time::now() - 24h || recordTime == time)) ||
|
||||||
|
// For time queries, ensure data is within 24 hours of the request
|
||||||
|
(time != std::chrono::system_clock::time_point {} &&
|
||||||
|
std::chrono::abs(recordTime - time) < 24h))
|
||||||
|
{
|
||||||
|
record = recordPtr->second.lock();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Reset the record
|
||||||
|
recordPtr = nullptr;
|
||||||
|
recordTime = time;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recordPtr != nullptr && record == nullptr &&
|
if (recordPtr != nullptr && record == nullptr &&
|
||||||
|
|
@ -1440,9 +1638,22 @@ RadarProductManagerImpl::GetLevel3ProductRecord(
|
||||||
});
|
});
|
||||||
|
|
||||||
self_->LoadLevel3Data(product, recordTime, request);
|
self_->LoadLevel3Data(product, recordTime, request);
|
||||||
|
|
||||||
|
// Status is already set to LoadingProduct
|
||||||
}
|
}
|
||||||
|
|
||||||
return {record, recordTime};
|
if (recordPtr == nullptr)
|
||||||
|
{
|
||||||
|
// If the record is empty, the product is not available
|
||||||
|
status = types::RadarProductLoadStatus::ProductNotAvailable;
|
||||||
|
}
|
||||||
|
else if (record != nullptr)
|
||||||
|
{
|
||||||
|
// If the record was populated, the product has been loaded
|
||||||
|
status = types::RadarProductLoadStatus::ProductLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {record, recordTime, status};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<types::RadarProductRecord>
|
std::shared_ptr<types::RadarProductRecord>
|
||||||
|
|
@ -1543,7 +1754,8 @@ void RadarProductManagerImpl::UpdateRecentRecords(
|
||||||
std::tuple<std::shared_ptr<wsr88d::rda::ElevationScan>,
|
std::tuple<std::shared_ptr<wsr88d::rda::ElevationScan>,
|
||||||
float,
|
float,
|
||||||
std::vector<float>,
|
std::vector<float>,
|
||||||
std::chrono::system_clock::time_point>
|
std::chrono::system_clock::time_point,
|
||||||
|
types::RadarProductLoadStatus>
|
||||||
RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType,
|
RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType,
|
||||||
float elevation,
|
float elevation,
|
||||||
std::chrono::system_clock::time_point time)
|
std::chrono::system_clock::time_point time)
|
||||||
|
|
@ -1552,6 +1764,8 @@ RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType,
|
||||||
float elevationCut = 0.0f;
|
float elevationCut = 0.0f;
|
||||||
std::vector<float> elevationCuts {};
|
std::vector<float> elevationCuts {};
|
||||||
std::chrono::system_clock::time_point foundTime {};
|
std::chrono::system_clock::time_point foundTime {};
|
||||||
|
types::RadarProductLoadStatus loadStatus {
|
||||||
|
types::RadarProductLoadStatus::ProductNotLoaded};
|
||||||
|
|
||||||
const bool isEpox = time == std::chrono::system_clock::time_point {};
|
const bool isEpox = time == std::chrono::system_clock::time_point {};
|
||||||
bool needArchive = true;
|
bool needArchive = true;
|
||||||
|
|
@ -1587,6 +1801,7 @@ RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType,
|
||||||
if (foundTime >= firstValidChunkTime)
|
if (foundTime >= firstValidChunkTime)
|
||||||
{
|
{
|
||||||
needArchive = false;
|
needArchive = false;
|
||||||
|
loadStatus = types::RadarProductLoadStatus::ProductLoaded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1594,7 +1809,11 @@ RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType,
|
||||||
// It is not in the chunk provider, so get it from the archive
|
// It is not in the chunk provider, so get it from the archive
|
||||||
if (needArchive)
|
if (needArchive)
|
||||||
{
|
{
|
||||||
auto records = p->GetLevel2ProductRecords(time);
|
std::map<std::chrono::system_clock::time_point,
|
||||||
|
std::shared_ptr<types::RadarProductRecord>>
|
||||||
|
records;
|
||||||
|
|
||||||
|
std::tie(records, loadStatus) = p->GetLevel2ProductRecords(time);
|
||||||
for (auto& recordPair : records)
|
for (auto& recordPair : records)
|
||||||
{
|
{
|
||||||
auto& record = recordPair.second;
|
auto& record = recordPair.second;
|
||||||
|
|
@ -1639,25 +1858,35 @@ RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {radarData, elevationCut, elevationCuts, foundTime};
|
if (loadStatus == types::RadarProductLoadStatus::ProductLoaded &&
|
||||||
|
radarData == nullptr)
|
||||||
|
{
|
||||||
|
// If all data was available for the time point, but there is no matching
|
||||||
|
// radar data, consider this as no product available
|
||||||
|
loadStatus = types::RadarProductLoadStatus::ProductNotAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {radarData, elevationCut, elevationCuts, foundTime, loadStatus};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<std::shared_ptr<wsr88d::rpg::Level3Message>,
|
std::tuple<std::shared_ptr<wsr88d::rpg::Level3Message>,
|
||||||
std::chrono::system_clock::time_point>
|
std::chrono::system_clock::time_point,
|
||||||
|
types::RadarProductLoadStatus>
|
||||||
RadarProductManager::GetLevel3Data(const std::string& product,
|
RadarProductManager::GetLevel3Data(const std::string& product,
|
||||||
std::chrono::system_clock::time_point time)
|
std::chrono::system_clock::time_point time)
|
||||||
{
|
{
|
||||||
std::shared_ptr<wsr88d::rpg::Level3Message> message = nullptr;
|
std::shared_ptr<wsr88d::rpg::Level3Message> message = nullptr;
|
||||||
|
types::RadarProductLoadStatus status {};
|
||||||
|
|
||||||
std::shared_ptr<types::RadarProductRecord> record;
|
std::shared_ptr<types::RadarProductRecord> record;
|
||||||
std::tie(record, time) = p->GetLevel3ProductRecord(product, time);
|
std::tie(record, time, status) = p->GetLevel3ProductRecord(product, time);
|
||||||
|
|
||||||
if (record != nullptr)
|
if (record != nullptr)
|
||||||
{
|
{
|
||||||
message = record->level3_file()->message();
|
message = record->level3_file()->message();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {message, time};
|
return {message, time, status};
|
||||||
}
|
}
|
||||||
|
|
||||||
common::Level3ProductCategoryMap
|
common::Level3ProductCategoryMap
|
||||||
|
|
@ -1809,6 +2038,4 @@ RadarProductManager::Instance(const std::string& radarSite)
|
||||||
|
|
||||||
#include "radar_product_manager.moc"
|
#include "radar_product_manager.moc"
|
||||||
|
|
||||||
} // namespace manager
|
} // namespace scwx::qt::manager
|
||||||
} // namespace qt
|
|
||||||
} // namespace scwx
|
|
||||||
|
|
|
||||||
|
|
@ -5,23 +5,19 @@
|
||||||
#include <scwx/qt/config/radar_site.hpp>
|
#include <scwx/qt/config/radar_site.hpp>
|
||||||
#include <scwx/qt/request/nexrad_file_request.hpp>
|
#include <scwx/qt/request/nexrad_file_request.hpp>
|
||||||
#include <scwx/qt/types/radar_product_record.hpp>
|
#include <scwx/qt/types/radar_product_record.hpp>
|
||||||
|
#include <scwx/qt/types/radar_product_types.hpp>
|
||||||
#include <scwx/util/time.hpp>
|
#include <scwx/util/time.hpp>
|
||||||
#include <scwx/wsr88d/ar2v_file.hpp>
|
#include <scwx/wsr88d/ar2v_file.hpp>
|
||||||
#include <scwx/wsr88d/level3_file.hpp>
|
#include <scwx/wsr88d/level3_file.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/uuid/nil_generator.hpp>
|
#include <boost/uuid/nil_generator.hpp>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
namespace scwx
|
namespace scwx::qt::manager
|
||||||
{
|
|
||||||
namespace qt
|
|
||||||
{
|
|
||||||
namespace manager
|
|
||||||
{
|
{
|
||||||
|
|
||||||
class RadarProductManagerImpl;
|
class RadarProductManagerImpl;
|
||||||
|
|
@ -89,12 +85,13 @@ public:
|
||||||
* @param [in] time Radar product time
|
* @param [in] time Radar product time
|
||||||
*
|
*
|
||||||
* @return Level 2 radar data, selected elevation cut, available elevation
|
* @return Level 2 radar data, selected elevation cut, available elevation
|
||||||
* cuts and selected time
|
* cuts, selected time and product load status
|
||||||
*/
|
*/
|
||||||
std::tuple<std::shared_ptr<wsr88d::rda::ElevationScan>,
|
std::tuple<std::shared_ptr<wsr88d::rda::ElevationScan>,
|
||||||
float,
|
float,
|
||||||
std::vector<float>,
|
std::vector<float>,
|
||||||
std::chrono::system_clock::time_point>
|
std::chrono::system_clock::time_point,
|
||||||
|
types::RadarProductLoadStatus>
|
||||||
GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType,
|
GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType,
|
||||||
float elevation,
|
float elevation,
|
||||||
std::chrono::system_clock::time_point time = {});
|
std::chrono::system_clock::time_point time = {});
|
||||||
|
|
@ -105,10 +102,11 @@ public:
|
||||||
* @param [in] product Radar product name
|
* @param [in] product Radar product name
|
||||||
* @param [in] time Radar product time
|
* @param [in] time Radar product time
|
||||||
*
|
*
|
||||||
* @return Level 3 message data and selected time
|
* @return Level 3 message data, selected time and product load status
|
||||||
*/
|
*/
|
||||||
std::tuple<std::shared_ptr<wsr88d::rpg::Level3Message>,
|
std::tuple<std::shared_ptr<wsr88d::rpg::Level3Message>,
|
||||||
std::chrono::system_clock::time_point>
|
std::chrono::system_clock::time_point,
|
||||||
|
types::RadarProductLoadStatus>
|
||||||
GetLevel3Data(const std::string& product,
|
GetLevel3Data(const std::string& product,
|
||||||
std::chrono::system_clock::time_point time = {});
|
std::chrono::system_clock::time_point time = {});
|
||||||
|
|
||||||
|
|
@ -151,6 +149,9 @@ signals:
|
||||||
bool isChunks,
|
bool isChunks,
|
||||||
std::chrono::system_clock::time_point latestTime);
|
std::chrono::system_clock::time_point latestTime);
|
||||||
void IncomingLevel2ElevationChanged(std::optional<float> incomingElevation);
|
void IncomingLevel2ElevationChanged(std::optional<float> incomingElevation);
|
||||||
|
void ProductTimesPopulated(common::RadarProductGroup group,
|
||||||
|
const std::string& product,
|
||||||
|
std::chrono::system_clock::time_point queryTime);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<RadarProductManagerImpl> p;
|
std::unique_ptr<RadarProductManagerImpl> p;
|
||||||
|
|
@ -158,6 +159,4 @@ private:
|
||||||
friend class RadarProductManagerImpl;
|
friend class RadarProductManagerImpl;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace manager
|
} // namespace scwx::qt::manager
|
||||||
} // namespace qt
|
|
||||||
} // namespace scwx
|
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,13 @@ public:
|
||||||
// Lock mutexes before destroying
|
// Lock mutexes before destroying
|
||||||
std::unique_lock animationTimerLock {animationTimerMutex_};
|
std::unique_lock animationTimerLock {animationTimerMutex_};
|
||||||
animationTimer_.cancel();
|
animationTimer_.cancel();
|
||||||
|
animationTimerLock.unlock();
|
||||||
|
|
||||||
std::unique_lock selectTimeLock {selectTimeMutex_};
|
selectThreadPool_.stop();
|
||||||
|
playThreadPool_.stop();
|
||||||
|
|
||||||
|
selectThreadPool_.join();
|
||||||
|
playThreadPool_.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineManager* self_;
|
TimelineManager* self_;
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
#include <scwx/qt/util/color.hpp>
|
#include <scwx/qt/util/color.hpp>
|
||||||
#include <scwx/qt/util/tooltip.hpp>
|
#include <scwx/qt/util/tooltip.hpp>
|
||||||
#include <scwx/util/logger.hpp>
|
#include <scwx/util/logger.hpp>
|
||||||
#include <scwx/util/time.hpp>
|
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <boost/algorithm/string/join.hpp>
|
#include <boost/algorithm/string/join.hpp>
|
||||||
#include <boost/asio/system_timer.hpp>
|
#include <boost/asio/system_timer.hpp>
|
||||||
|
|
@ -22,7 +23,6 @@
|
||||||
#include <boost/container/stable_vector.hpp>
|
#include <boost/container/stable_vector.hpp>
|
||||||
#include <boost/container_hash/hash.hpp>
|
#include <boost/container_hash/hash.hpp>
|
||||||
#include <QEvent>
|
#include <QEvent>
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace scwx::qt::map
|
namespace scwx::qt::map
|
||||||
{
|
{
|
||||||
|
|
@ -153,9 +153,11 @@ public:
|
||||||
~Impl()
|
~Impl()
|
||||||
{
|
{
|
||||||
std::unique_lock refreshLock(refreshMutex_);
|
std::unique_lock refreshLock(refreshMutex_);
|
||||||
|
refreshEnabled_ = false;
|
||||||
refreshTimer_.cancel();
|
refreshTimer_.cancel();
|
||||||
refreshLock.unlock();
|
refreshLock.unlock();
|
||||||
|
|
||||||
|
threadPool_.stop();
|
||||||
threadPool_.join();
|
threadPool_.join();
|
||||||
|
|
||||||
receiver_ = nullptr;
|
receiver_ = nullptr;
|
||||||
|
|
@ -213,6 +215,7 @@ public:
|
||||||
|
|
||||||
AlertLayer* self_;
|
AlertLayer* self_;
|
||||||
|
|
||||||
|
std::atomic<bool> refreshEnabled_ {true};
|
||||||
boost::asio::system_timer refreshTimer_ {threadPool_};
|
boost::asio::system_timer refreshTimer_ {threadPool_};
|
||||||
std::mutex refreshMutex_;
|
std::mutex refreshMutex_;
|
||||||
|
|
||||||
|
|
@ -582,7 +585,8 @@ void AlertLayer::Impl::ScheduleRefresh()
|
||||||
|
|
||||||
// Expires at the top of the next minute
|
// Expires at the top of the next minute
|
||||||
std::chrono::system_clock::time_point now =
|
std::chrono::system_clock::time_point now =
|
||||||
std::chrono::floor<std::chrono::minutes>(scwx::util::time::now());
|
std::chrono::floor<std::chrono::minutes>(
|
||||||
|
std::chrono::system_clock::now());
|
||||||
refreshTimer_.expires_at(now + 1min);
|
refreshTimer_.expires_at(now + 1min);
|
||||||
|
|
||||||
refreshTimer_.async_wait(
|
refreshTimer_.async_wait(
|
||||||
|
|
@ -599,7 +603,11 @@ void AlertLayer::Impl::ScheduleRefresh()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Q_EMIT self_->NeedsRendering();
|
Q_EMIT self_->NeedsRendering();
|
||||||
ScheduleRefresh();
|
|
||||||
|
if (refreshEnabled_)
|
||||||
|
{
|
||||||
|
ScheduleRefresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -883,7 +891,7 @@ void AlertLayer::Impl::HandleGeoLinesEvent(
|
||||||
|
|
||||||
switch (ev->type())
|
switch (ev->type())
|
||||||
{
|
{
|
||||||
case QEvent::Type::MouseButtonPress:
|
case QEvent::Type::MouseButtonRelease:
|
||||||
{
|
{
|
||||||
auto it = segmentsByLine_.find(di);
|
auto it = segmentsByLine_.find(di);
|
||||||
if (it != segmentsByLine_.cend())
|
if (it != segmentsByLine_.cend())
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ static const std::unordered_map<MapProvider, MapProviderInfo> mapProviderInfo_ {
|
||||||
.drawBelow_ {mapboxDrawBelow_}},
|
.drawBelow_ {mapboxDrawBelow_}},
|
||||||
{.name_ {"Mineral"},
|
{.name_ {"Mineral"},
|
||||||
.url_ {"mapbox://styles/mapbox/cjtep62gq54l21frr1whf27ak"},
|
.url_ {"mapbox://styles/mapbox/cjtep62gq54l21frr1whf27ak"},
|
||||||
.drawBelow_ {mapboxDrawBelow_}},
|
.drawBelow_ {"tunnel"}},
|
||||||
{.name_ {"Minimo"},
|
{.name_ {"Minimo"},
|
||||||
.url_ {
|
.url_ {
|
||||||
"mapbox://styles/mapbox-map-design/cksjc2nsq1bg117pnekb655h1"},
|
"mapbox://styles/mapbox-map-design/cksjc2nsq1bg117pnekb655h1"},
|
||||||
|
|
|
||||||
|
|
@ -92,8 +92,15 @@ public:
|
||||||
Impl(const Impl&&) = delete;
|
Impl(const Impl&&) = delete;
|
||||||
Impl& operator=(const Impl&&) = delete;
|
Impl& operator=(const Impl&&) = delete;
|
||||||
|
|
||||||
|
void RenderProductName(const std::shared_ptr<MapContext>& mapContext);
|
||||||
|
void
|
||||||
|
RenderProductDetails(const std::shared_ptr<MapContext>& mapContext,
|
||||||
|
const QMapLibre::CustomLayerRenderParameters& params);
|
||||||
|
void RenderAttribution(const std::shared_ptr<MapContext>& mapContext,
|
||||||
|
const QMapLibre::CustomLayerRenderParameters& params);
|
||||||
|
|
||||||
void SetupGeoIcons();
|
void SetupGeoIcons();
|
||||||
void SetCusorLocation(common::Coordinate coordinate);
|
void SetCursorLocation(common::Coordinate coordinate);
|
||||||
|
|
||||||
OverlayLayer* self_;
|
OverlayLayer* self_;
|
||||||
|
|
||||||
|
|
@ -173,7 +180,7 @@ OverlayLayer::~OverlayLayer()
|
||||||
p->cursorScaleConnection_.disconnect();
|
p->cursorScaleConnection_.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayLayer::Impl::SetCusorLocation(common::Coordinate coordinate)
|
void OverlayLayer::Impl::SetCursorLocation(common::Coordinate coordinate)
|
||||||
{
|
{
|
||||||
geoIcons_->SetIconLocation(
|
geoIcons_->SetIconLocation(
|
||||||
cursorIcon_, coordinate.latitude_, coordinate.longitude_);
|
cursorIcon_, coordinate.latitude_, coordinate.longitude_);
|
||||||
|
|
@ -388,7 +395,7 @@ void OverlayLayer::Render(const std::shared_ptr<MapContext>& mapContext,
|
||||||
p->geoIcons_->SetIconVisible(p->cursorIcon_, cursorIconVisible);
|
p->geoIcons_->SetIconVisible(p->cursorIcon_, cursorIconVisible);
|
||||||
if (cursorIconVisible)
|
if (cursorIconVisible)
|
||||||
{
|
{
|
||||||
p->SetCusorLocation(mapContext->mouse_coordinate());
|
p->SetCursorLocation(mapContext->mouse_coordinate());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Location Icon
|
// Location Icon
|
||||||
|
|
@ -425,6 +432,58 @@ void OverlayLayer::Render(const std::shared_ptr<MapContext>& mapContext,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p->RenderProductName(mapContext);
|
||||||
|
p->RenderProductDetails(mapContext, params);
|
||||||
|
|
||||||
|
// Map Center Icon
|
||||||
|
if (params.width != p->lastWidth_ || params.height != p->lastHeight_)
|
||||||
|
{
|
||||||
|
static constexpr double xPosition = 0.5;
|
||||||
|
static constexpr double yPosition = 0.5;
|
||||||
|
|
||||||
|
// Draw the icon in the center of the widget
|
||||||
|
p->icons_->SetIconLocation(p->mapCenterIcon_,
|
||||||
|
params.width * xPosition,
|
||||||
|
params.height * yPosition);
|
||||||
|
}
|
||||||
|
p->icons_->SetIconVisible(p->mapCenterIcon_,
|
||||||
|
generalSettings.show_map_center().GetValue());
|
||||||
|
|
||||||
|
const QMargins colorTableMargins = mapContext->color_table_margins();
|
||||||
|
if (colorTableMargins != p->lastColorTableMargins_ || p->firstRender_)
|
||||||
|
{
|
||||||
|
static constexpr int xOffset = 10;
|
||||||
|
static constexpr int yOffset = 10;
|
||||||
|
|
||||||
|
// Draw map logo with a 10x10 indent from the bottom left
|
||||||
|
p->icons_->SetIconLocation(p->mapLogoIcon_,
|
||||||
|
colorTableMargins.left() + xOffset,
|
||||||
|
colorTableMargins.bottom() + yOffset);
|
||||||
|
}
|
||||||
|
p->icons_->SetIconVisible(p->mapLogoIcon_,
|
||||||
|
generalSettings.show_map_logo().GetValue());
|
||||||
|
|
||||||
|
DrawLayer::RenderWithoutImGui(params);
|
||||||
|
|
||||||
|
p->RenderAttribution(mapContext, params);
|
||||||
|
|
||||||
|
p->firstRender_ = false;
|
||||||
|
p->lastWidth_ = params.width;
|
||||||
|
p->lastHeight_ = params.height;
|
||||||
|
p->lastBearing_ = params.bearing;
|
||||||
|
p->lastFontSize_ = ImGui::GetFontSize();
|
||||||
|
p->lastColorTableMargins_ = colorTableMargins;
|
||||||
|
|
||||||
|
ImGuiFrameEnd();
|
||||||
|
|
||||||
|
SCWX_GL_CHECK_ERROR();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OverlayLayer::Impl::RenderProductName(
|
||||||
|
const std::shared_ptr<MapContext>& mapContext)
|
||||||
|
{
|
||||||
|
auto radarProductView = mapContext->radar_product_view();
|
||||||
|
|
||||||
if (radarProductView != nullptr)
|
if (radarProductView != nullptr)
|
||||||
{
|
{
|
||||||
// Render product name
|
// Render product name
|
||||||
|
|
@ -456,14 +515,68 @@ void OverlayLayer::Render(const std::shared_ptr<MapContext>& mapContext,
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (p->sweepTimeString_.length() > 0)
|
void OverlayLayer::Impl::RenderProductDetails(
|
||||||
|
const std::shared_ptr<MapContext>& mapContext,
|
||||||
|
const QMapLibre::CustomLayerRenderParameters& params)
|
||||||
|
{
|
||||||
|
auto radarProductView = mapContext->radar_product_view();
|
||||||
|
|
||||||
|
ImGui::SetNextWindowPos(ImVec2 {static_cast<float>(params.width), 0.0f},
|
||||||
|
ImGuiCond_Always,
|
||||||
|
ImVec2 {1.0f, 0.0f});
|
||||||
|
|
||||||
|
bool productNotAvailable = false;
|
||||||
|
types::RadarProductLoadStatus newLoadStatus =
|
||||||
|
types::RadarProductLoadStatus::ProductNotLoaded;
|
||||||
|
|
||||||
|
if (radarProductView != nullptr)
|
||||||
|
{
|
||||||
|
newLoadStatus = radarProductView->load_status();
|
||||||
|
|
||||||
|
switch (newLoadStatus)
|
||||||
|
{
|
||||||
|
case types::RadarProductLoadStatus::ProductNotAvailable:
|
||||||
|
productNotAvailable = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
productNotAvailable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (productNotAvailable)
|
||||||
|
{
|
||||||
|
ImGui::Begin("Product Not Available",
|
||||||
|
nullptr,
|
||||||
|
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize);
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 0, 0, 255));
|
||||||
|
ImGui::TextUnformatted("NO DATA AVAILABLE");
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
if (ImGui::BeginItemTooltip())
|
||||||
|
{
|
||||||
|
static constexpr float kFontSizeFactor_ = 20.0f;
|
||||||
|
static constexpr double kMaxWidthPercent_ = 0.8;
|
||||||
|
|
||||||
|
ImGui::PushTextWrapPos(
|
||||||
|
std::min(ImGui::GetFontSize() * kFontSizeFactor_,
|
||||||
|
static_cast<float>(params.width * kMaxWidthPercent_)));
|
||||||
|
ImGui::TextUnformatted(
|
||||||
|
"No data found for the selected product and time. Please select a "
|
||||||
|
"different product, or update your time selection.");
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
ImGui::EndTooltip();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
else if (sweepTimeString_.length() > 0)
|
||||||
{
|
{
|
||||||
// Render time
|
// Render time
|
||||||
ImGui::SetNextWindowPos(ImVec2 {static_cast<float>(params.width), 0.0f},
|
ImGui::Begin("Product Details",
|
||||||
ImGuiCond_Always,
|
|
||||||
ImVec2 {1.0f, 0.0f});
|
|
||||||
ImGui::Begin("Sweep Time",
|
|
||||||
nullptr,
|
nullptr,
|
||||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
|
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
|
||||||
ImGuiWindowFlags_AlwaysAutoResize);
|
ImGuiWindowFlags_AlwaysAutoResize);
|
||||||
|
|
@ -471,12 +584,12 @@ void OverlayLayer::Render(const std::shared_ptr<MapContext>& mapContext,
|
||||||
if (radarProductView != nullptr && ImGui::IsWindowHovered())
|
if (radarProductView != nullptr && ImGui::IsWindowHovered())
|
||||||
{
|
{
|
||||||
// Show a detailed product description when the sweep time is hovered
|
// Show a detailed product description when the sweep time is hovered
|
||||||
p->sweepTimePicked_ = true;
|
sweepTimePicked_ = true;
|
||||||
|
|
||||||
auto fields = radarProductView->GetDescriptionFields();
|
auto fields = radarProductView->GetDescriptionFields();
|
||||||
if (fields.empty())
|
if (fields.empty())
|
||||||
{
|
{
|
||||||
ImGui::TextUnformatted(p->sweepTimeString_.c_str());
|
ImGui::TextUnformatted(sweepTimeString_.c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -496,34 +609,19 @@ void OverlayLayer::Render(const std::shared_ptr<MapContext>& mapContext,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImGui::TextUnformatted(p->sweepTimeString_.c_str());
|
ImGui::TextUnformatted(sweepTimeString_.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Map Center Icon
|
void OverlayLayer::Impl::RenderAttribution(
|
||||||
if (params.width != p->lastWidth_ || params.height != p->lastHeight_)
|
const std::shared_ptr<MapContext>& mapContext,
|
||||||
{
|
const QMapLibre::CustomLayerRenderParameters& params)
|
||||||
// Draw the icon in the center of the widget
|
{
|
||||||
p->icons_->SetIconLocation(
|
|
||||||
p->mapCenterIcon_, params.width / 2.0, params.height / 2.0);
|
|
||||||
}
|
|
||||||
p->icons_->SetIconVisible(p->mapCenterIcon_,
|
|
||||||
generalSettings.show_map_center().GetValue());
|
|
||||||
|
|
||||||
const QMargins colorTableMargins = mapContext->color_table_margins();
|
const QMargins colorTableMargins = mapContext->color_table_margins();
|
||||||
if (colorTableMargins != p->lastColorTableMargins_ || p->firstRender_)
|
auto& generalSettings = settings::GeneralSettings::Instance();
|
||||||
{
|
|
||||||
// Draw map logo with a 10x10 indent from the bottom left
|
|
||||||
p->icons_->SetIconLocation(p->mapLogoIcon_,
|
|
||||||
10 + colorTableMargins.left(),
|
|
||||||
10 + colorTableMargins.bottom());
|
|
||||||
}
|
|
||||||
p->icons_->SetIconVisible(p->mapLogoIcon_,
|
|
||||||
generalSettings.show_map_logo().GetValue());
|
|
||||||
|
|
||||||
DrawLayer::RenderWithoutImGui(params);
|
|
||||||
|
|
||||||
auto mapCopyrights = mapContext->map_copyrights();
|
auto mapCopyrights = mapContext->map_copyrights();
|
||||||
if (mapCopyrights.length() > 0 &&
|
if (mapCopyrights.length() > 0 &&
|
||||||
|
|
@ -532,13 +630,19 @@ void OverlayLayer::Render(const std::shared_ptr<MapContext>& mapContext,
|
||||||
auto attributionFont = manager::FontManager::Instance().GetImGuiFont(
|
auto attributionFont = manager::FontManager::Instance().GetImGuiFont(
|
||||||
types::FontCategory::Attribution);
|
types::FontCategory::Attribution);
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2 {static_cast<float>(params.width),
|
static constexpr float kWindowBgAlpha_ = 0.5f;
|
||||||
static_cast<float>(params.height) -
|
static constexpr float kWindowPaddingX_ = 3.0f;
|
||||||
colorTableMargins.bottom()},
|
static constexpr float kWindowPaddingY_ = 2.0f;
|
||||||
ImGuiCond_Always,
|
|
||||||
ImVec2 {1.0f, 1.0f});
|
ImGui::SetNextWindowPos(
|
||||||
ImGui::SetNextWindowBgAlpha(0.5f);
|
ImVec2 {
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2 {3.0f, 2.0f});
|
static_cast<float>(params.width),
|
||||||
|
static_cast<float>(params.height - colorTableMargins.bottom())},
|
||||||
|
ImGuiCond_Always,
|
||||||
|
ImVec2 {1.0f, 1.0f});
|
||||||
|
ImGui::SetNextWindowBgAlpha(kWindowBgAlpha_);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,
|
||||||
|
ImVec2 {kWindowPaddingX_, kWindowPaddingY_});
|
||||||
ImGui::PushFont(attributionFont.first->font(),
|
ImGui::PushFont(attributionFont.first->font(),
|
||||||
attributionFont.second.value());
|
attributionFont.second.value());
|
||||||
ImGui::Begin("Attribution",
|
ImGui::Begin("Attribution",
|
||||||
|
|
@ -550,17 +654,6 @@ void OverlayLayer::Render(const std::shared_ptr<MapContext>& mapContext,
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
}
|
}
|
||||||
|
|
||||||
p->firstRender_ = false;
|
|
||||||
p->lastWidth_ = params.width;
|
|
||||||
p->lastHeight_ = params.height;
|
|
||||||
p->lastBearing_ = params.bearing;
|
|
||||||
p->lastFontSize_ = ImGui::GetFontSize();
|
|
||||||
p->lastColorTableMargins_ = colorTableMargins;
|
|
||||||
|
|
||||||
ImGuiFrameEnd();
|
|
||||||
|
|
||||||
SCWX_GL_CHECK_ERROR();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayLayer::Deinitialize()
|
void OverlayLayer::Deinitialize()
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,9 @@ public:
|
||||||
|
|
||||||
bool colorTableNeedsUpdate_ {false};
|
bool colorTableNeedsUpdate_ {false};
|
||||||
bool sweepNeedsUpdate_ {false};
|
bool sweepNeedsUpdate_ {false};
|
||||||
|
|
||||||
|
types::RadarProductLoadStatus latchedLoadStatus_ {
|
||||||
|
types::RadarProductLoadStatus::ProductNotAvailable};
|
||||||
};
|
};
|
||||||
|
|
||||||
RadarProductLayer::RadarProductLayer(std::shared_ptr<gl::GlContext> glContext) :
|
RadarProductLayer::RadarProductLayer(std::shared_ptr<gl::GlContext> glContext) :
|
||||||
|
|
@ -146,6 +149,26 @@ void RadarProductLayer::Initialize(
|
||||||
&view::RadarProductView::SweepComputed,
|
&view::RadarProductView::SweepComputed,
|
||||||
this,
|
this,
|
||||||
[this]() { p->sweepNeedsUpdate_ = true; });
|
[this]() { p->sweepNeedsUpdate_ = true; });
|
||||||
|
connect(radarProductView.get(),
|
||||||
|
&view::RadarProductView::SweepNotComputed,
|
||||||
|
this,
|
||||||
|
[this](types::NoUpdateReason reason)
|
||||||
|
{
|
||||||
|
if (reason == types::NoUpdateReason::NotAvailable)
|
||||||
|
{
|
||||||
|
// Ensure the radar product is hidden by re-rendering
|
||||||
|
Q_EMIT NeedsRendering();
|
||||||
|
}
|
||||||
|
if (reason == types::NoUpdateReason::NoChange)
|
||||||
|
{
|
||||||
|
if (p->latchedLoadStatus_ ==
|
||||||
|
types::RadarProductLoadStatus::ProductNotAvailable)
|
||||||
|
{
|
||||||
|
// Ensure the radar product is shown by re-rendering
|
||||||
|
Q_EMIT NeedsRendering();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadarProductLayer::UpdateSweep(
|
void RadarProductLayer::UpdateSweep(
|
||||||
|
|
@ -189,10 +212,10 @@ void RadarProductLayer::UpdateSweep(
|
||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(0);
|
||||||
|
|
||||||
// Buffer data moments
|
// Buffer data moments
|
||||||
const GLvoid* data;
|
const GLvoid* data {};
|
||||||
GLsizeiptr dataSize;
|
GLsizeiptr dataSize {};
|
||||||
size_t componentSize;
|
size_t componentSize {};
|
||||||
GLenum type;
|
GLenum type {};
|
||||||
|
|
||||||
std::tie(data, dataSize, componentSize) = radarProductView->GetMomentData();
|
std::tie(data, dataSize, componentSize) = radarProductView->GetMomentData();
|
||||||
|
|
||||||
|
|
@ -215,10 +238,10 @@ void RadarProductLayer::UpdateSweep(
|
||||||
glEnableVertexAttribArray(1);
|
glEnableVertexAttribArray(1);
|
||||||
|
|
||||||
// Buffer CFP data
|
// Buffer CFP data
|
||||||
const GLvoid* cfpData;
|
const GLvoid* cfpData {};
|
||||||
GLsizeiptr cfpDataSize;
|
GLsizeiptr cfpDataSize {};
|
||||||
size_t cfpComponentSize;
|
size_t cfpComponentSize {};
|
||||||
GLenum cfpType;
|
GLenum cfpType {};
|
||||||
|
|
||||||
std::tie(cfpData, cfpDataSize, cfpComponentSize) =
|
std::tie(cfpData, cfpDataSize, cfpComponentSize) =
|
||||||
radarProductView->GetCfpMomentData();
|
radarProductView->GetCfpMomentData();
|
||||||
|
|
@ -258,7 +281,6 @@ void RadarProductLayer::Render(
|
||||||
const std::shared_ptr<MapContext>& mapContext,
|
const std::shared_ptr<MapContext>& mapContext,
|
||||||
const QMapLibre::CustomLayerRenderParameters& params)
|
const QMapLibre::CustomLayerRenderParameters& params)
|
||||||
{
|
{
|
||||||
|
|
||||||
p->shaderProgram_->Use();
|
p->shaderProgram_->Use();
|
||||||
|
|
||||||
// Set OpenGL blend mode for transparency
|
// Set OpenGL blend mode for transparency
|
||||||
|
|
@ -281,34 +303,65 @@ void RadarProductLayer::Render(
|
||||||
UpdateSweep(mapContext);
|
UpdateSweep(mapContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
const float scale = std::pow(2.0, params.zoom) * 2.0f *
|
const std::shared_ptr<view::RadarProductView> radarProductView =
|
||||||
mbgl::util::tileSize_D / mbgl::util::DEGREES_MAX;
|
mapContext->radar_product_view();
|
||||||
const float xScale = scale / params.width;
|
|
||||||
const float yScale = scale / params.height;
|
|
||||||
|
|
||||||
glm::mat4 uMVPMatrix(1.0f);
|
bool sweepVisible = false;
|
||||||
uMVPMatrix = glm::scale(uMVPMatrix, glm::vec3(xScale, yScale, 1.0f));
|
types::RadarProductLoadStatus newLoadStatus =
|
||||||
uMVPMatrix = glm::rotate(uMVPMatrix,
|
types::RadarProductLoadStatus::ProductNotLoaded;
|
||||||
glm::radians<float>(params.bearing),
|
|
||||||
glm::vec3(0.0f, 0.0f, 1.0f));
|
|
||||||
|
|
||||||
glUniform2fv(p->uMapScreenCoordLocation_,
|
if (radarProductView != nullptr)
|
||||||
1,
|
{
|
||||||
glm::value_ptr(util::maplibre::LatLongToScreenCoordinate(
|
newLoadStatus = radarProductView->load_status();
|
||||||
{params.latitude, params.longitude})));
|
|
||||||
|
|
||||||
glUniformMatrix4fv(
|
switch (newLoadStatus)
|
||||||
p->uMVPMatrixLocation_, 1, GL_FALSE, glm::value_ptr(uMVPMatrix));
|
{
|
||||||
|
case types::RadarProductLoadStatus::ProductNotAvailable:
|
||||||
|
sweepVisible = false;
|
||||||
|
break;
|
||||||
|
|
||||||
glUniform1i(p->uCFPEnabledLocation_, p->cfpEnabled_ ? 1 : 0);
|
case types::RadarProductLoadStatus::ListingProducts:
|
||||||
|
sweepVisible = p->latchedLoadStatus_ !=
|
||||||
|
types::RadarProductLoadStatus::ProductNotAvailable;
|
||||||
|
break;
|
||||||
|
|
||||||
glUniform1ui(p->uDataMomentOffsetLocation_, p->rangeMin_);
|
default:
|
||||||
glUniform1f(p->uDataMomentScaleLocation_, p->scale_);
|
sweepVisible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
if (sweepVisible)
|
||||||
glBindTexture(GL_TEXTURE_1D, p->texture_);
|
{
|
||||||
glBindVertexArray(p->vao_);
|
const double scale = std::pow(2.0, params.zoom) * 2.0 *
|
||||||
glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(p->numVertices_));
|
mbgl::util::tileSize_D / mbgl::util::DEGREES_MAX;
|
||||||
|
const auto xScale = static_cast<float>(scale / params.width);
|
||||||
|
const auto yScale = static_cast<float>(scale / params.height);
|
||||||
|
|
||||||
|
glm::mat4 uMVPMatrix(1.0f);
|
||||||
|
uMVPMatrix = glm::scale(uMVPMatrix, glm::vec3(xScale, yScale, 1.0f));
|
||||||
|
uMVPMatrix = glm::rotate(uMVPMatrix,
|
||||||
|
glm::radians(static_cast<float>(params.bearing)),
|
||||||
|
glm::vec3(0.0f, 0.0f, 1.0f));
|
||||||
|
|
||||||
|
glUniform2fv(p->uMapScreenCoordLocation_,
|
||||||
|
1,
|
||||||
|
glm::value_ptr(util::maplibre::LatLongToScreenCoordinate(
|
||||||
|
{params.latitude, params.longitude})));
|
||||||
|
|
||||||
|
glUniformMatrix4fv(
|
||||||
|
p->uMVPMatrixLocation_, 1, GL_FALSE, glm::value_ptr(uMVPMatrix));
|
||||||
|
|
||||||
|
glUniform1i(p->uCFPEnabledLocation_, p->cfpEnabled_ ? 1 : 0);
|
||||||
|
|
||||||
|
glUniform1ui(p->uDataMomentOffsetLocation_, p->rangeMin_);
|
||||||
|
glUniform1f(p->uDataMomentScaleLocation_, p->scale_);
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_1D, p->texture_);
|
||||||
|
glBindVertexArray(p->vao_);
|
||||||
|
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(p->numVertices_));
|
||||||
|
}
|
||||||
|
|
||||||
if (wireframeEnabled)
|
if (wireframeEnabled)
|
||||||
{
|
{
|
||||||
|
|
@ -316,6 +369,16 @@ void RadarProductLayer::Render(
|
||||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (radarProductView != nullptr &&
|
||||||
|
// Don't latch a transition from Not Available to Listing Products
|
||||||
|
!(p->latchedLoadStatus_ ==
|
||||||
|
types::RadarProductLoadStatus::ProductNotAvailable &&
|
||||||
|
newLoadStatus == types::RadarProductLoadStatus::ListingProducts))
|
||||||
|
{
|
||||||
|
// Latch last load status
|
||||||
|
p->latchedLoadStatus_ = newLoadStatus;
|
||||||
|
}
|
||||||
|
|
||||||
SCWX_GL_CHECK_ERROR();
|
SCWX_GL_CHECK_ERROR();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -543,7 +606,7 @@ void RadarProductLayer::UpdateColorTable(
|
||||||
const uint16_t rangeMin = radarProductView->color_table_min();
|
const uint16_t rangeMin = radarProductView->color_table_min();
|
||||||
const uint16_t rangeMax = radarProductView->color_table_max();
|
const uint16_t rangeMax = radarProductView->color_table_max();
|
||||||
|
|
||||||
const float scale = rangeMax - rangeMin;
|
const auto scale = static_cast<float>(rangeMax - rangeMin);
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_1D, p->texture_);
|
glBindTexture(GL_TEXTURE_1D, p->texture_);
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,10 @@ public:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO: Validate level 3 product
|
// Validate level 3 product
|
||||||
return true;
|
const auto level3Product =
|
||||||
|
common::GetLevel3ProductByAwipsId(value);
|
||||||
|
return !level3Product.empty() && level3Product != "?";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ enum class NoUpdateReason
|
||||||
{
|
{
|
||||||
NoChange,
|
NoChange,
|
||||||
NotLoaded,
|
NotLoaded,
|
||||||
|
NotAvailable,
|
||||||
InvalidProduct,
|
InvalidProduct,
|
||||||
InvalidData
|
InvalidData
|
||||||
};
|
};
|
||||||
|
|
|
||||||
17
scwx-qt/source/scwx/qt/types/radar_product_types.hpp
Normal file
17
scwx-qt/source/scwx/qt/types/radar_product_types.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace scwx::qt::types
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class RadarProductLoadStatus : std::uint8_t
|
||||||
|
{
|
||||||
|
ProductNotLoaded,
|
||||||
|
ProductLoaded,
|
||||||
|
ListingProducts,
|
||||||
|
LoadingProduct,
|
||||||
|
ProductNotAvailable
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -93,9 +93,9 @@ void UpdateDialog::Impl::HandleAsset(const types::gh::ReleaseAsset& asset)
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
|
||||||
# if defined(_M_AMD64)
|
# if defined(_M_AMD64)
|
||||||
static constexpr std::string assetSuffix = "-x64.msi";
|
static const std::string assetSuffix = "-x64.msi";
|
||||||
# else
|
# else
|
||||||
static constexpr std::string assetSuffix = "-arm64.msi";
|
static const std::string assetSuffix = "-arm64.msi";
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
if (asset.name_.ends_with(assetSuffix))
|
if (asset.name_.ends_with(assetSuffix))
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,13 @@
|
||||||
#include <scwx/util/threads.hpp>
|
#include <scwx/util/threads.hpp>
|
||||||
#include <scwx/util/time.hpp>
|
#include <scwx/util/time.hpp>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <boost/range/irange.hpp>
|
#include <boost/range/irange.hpp>
|
||||||
#include <boost/timer/timer.hpp>
|
#include <boost/timer/timer.hpp>
|
||||||
|
|
||||||
namespace scwx
|
namespace scwx::qt::view
|
||||||
{
|
|
||||||
namespace qt
|
|
||||||
{
|
|
||||||
namespace view
|
|
||||||
{
|
{
|
||||||
|
|
||||||
static const std::string logPrefix_ = "scwx::qt::view::level2_product_view";
|
static const std::string logPrefix_ = "scwx::qt::view::level2_product_view";
|
||||||
|
|
@ -164,11 +163,13 @@ public:
|
||||||
|
|
||||||
float latitude_;
|
float latitude_;
|
||||||
float longitude_;
|
float longitude_;
|
||||||
float elevationCut_;
|
std::atomic<float> elevationCut_;
|
||||||
std::vector<float> elevationCuts_;
|
std::vector<float> elevationCuts_;
|
||||||
units::kilometers<float> range_;
|
units::kilometers<float> range_;
|
||||||
uint16_t vcp_;
|
uint16_t vcp_;
|
||||||
|
|
||||||
|
std::mutex elevationCutsMutex_ {};
|
||||||
|
|
||||||
std::chrono::system_clock::time_point sweepTime_;
|
std::chrono::system_clock::time_point sweepTime_;
|
||||||
|
|
||||||
std::shared_ptr<common::ColorTable> colorTable_;
|
std::shared_ptr<common::ColorTable> colorTable_;
|
||||||
|
|
@ -214,6 +215,22 @@ void Level2ProductView::ConnectRadarProductManager()
|
||||||
Update();
|
Update();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(radar_product_manager().get(),
|
||||||
|
&manager::RadarProductManager::ProductTimesPopulated,
|
||||||
|
this,
|
||||||
|
[this](common::RadarProductGroup group,
|
||||||
|
const std::string& /* product */,
|
||||||
|
std::chrono::system_clock::time_point queryTime)
|
||||||
|
{
|
||||||
|
if (group == common::RadarProductGroup::Level2 &&
|
||||||
|
queryTime == selected_time())
|
||||||
|
{
|
||||||
|
// If the data associated with the currently selected time is
|
||||||
|
// reloaded, update the view
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Level2ProductView::DisconnectRadarProductManager()
|
void Level2ProductView::DisconnectRadarProductManager()
|
||||||
|
|
@ -356,6 +373,7 @@ std::string Level2ProductView::GetRadarProductName() const
|
||||||
|
|
||||||
std::vector<float> Level2ProductView::GetElevationCuts() const
|
std::vector<float> Level2ProductView::GetElevationCuts() const
|
||||||
{
|
{
|
||||||
|
const std::unique_lock lock {p->elevationCutsMutex_};
|
||||||
return p->elevationCuts_;
|
return p->elevationCuts_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -552,13 +570,26 @@ void Level2ProductView::ComputeSweep()
|
||||||
|
|
||||||
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()};
|
||||||
std::tie(radarData, p->elevationCut_, p->elevationCuts_, std::ignore) =
|
types::RadarProductLoadStatus loadStatus {};
|
||||||
|
|
||||||
|
std::vector<float> newElevationCuts {};
|
||||||
|
std::tie(
|
||||||
|
radarData, p->elevationCut_, newElevationCuts, std::ignore, loadStatus) =
|
||||||
radarProductManager->GetLevel2Data(
|
radarProductManager->GetLevel2Data(
|
||||||
p->dataBlockType_, p->selectedElevation_, requestedTime);
|
p->dataBlockType_, p->selectedElevation_, requestedTime);
|
||||||
|
|
||||||
|
std::unique_lock elevationCutsLock {p->elevationCutsMutex_};
|
||||||
|
p->elevationCuts_ = newElevationCuts;
|
||||||
|
elevationCutsLock.unlock();
|
||||||
|
|
||||||
|
set_load_status(loadStatus);
|
||||||
|
|
||||||
if (radarData == nullptr)
|
if (radarData == nullptr)
|
||||||
{
|
{
|
||||||
Q_EMIT SweepNotComputed(types::NoUpdateReason::NotLoaded);
|
Q_EMIT SweepNotComputed(
|
||||||
|
loadStatus == types::RadarProductLoadStatus::ProductNotAvailable ?
|
||||||
|
types::NoUpdateReason::NotAvailable :
|
||||||
|
types::NoUpdateReason::NotLoaded);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((radarData == p->elevationScan_) &&
|
if ((radarData == p->elevationScan_) &&
|
||||||
|
|
@ -1369,7 +1400,7 @@ Level2ProductView::GetBinLevel(const common::Coordinate& coordinate) const
|
||||||
auto nextRadial = radarData->find((i + 1) % numRadials);
|
auto nextRadial = radarData->find((i + 1) % numRadials);
|
||||||
if (nextRadial != radarData->cend())
|
if (nextRadial != radarData->cend())
|
||||||
{
|
{
|
||||||
nextAngle = nextRadial->second->azimuth_angle();
|
nextAngle = nextRadial->second->azimuth_angle();
|
||||||
|
|
||||||
// Level 2 angles are the center of the bins.
|
// Level 2 angles are the center of the bins.
|
||||||
const units::degrees<float> deltaAngle =
|
const units::degrees<float> deltaAngle =
|
||||||
|
|
@ -1564,6 +1595,4 @@ std::shared_ptr<Level2ProductView> Level2ProductView::Create(
|
||||||
return std::make_shared<Level2ProductView>(product, radarProductManager);
|
return std::make_shared<Level2ProductView>(product, radarProductManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace view
|
} // namespace scwx::qt::view
|
||||||
} // namespace qt
|
|
||||||
} // namespace scwx
|
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,12 @@
|
||||||
#include <scwx/wsr88d/rpg/radial_data_packet.hpp>
|
#include <scwx/wsr88d/rpg/radial_data_packet.hpp>
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
#include <boost/range/irange.hpp>
|
#include <boost/range/irange.hpp>
|
||||||
#include <boost/timer/timer.hpp>
|
#include <boost/timer/timer.hpp>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
namespace scwx
|
namespace scwx::qt::view
|
||||||
{
|
|
||||||
namespace qt
|
|
||||||
{
|
|
||||||
namespace view
|
|
||||||
{
|
{
|
||||||
|
|
||||||
static const std::string logPrefix_ = "scwx::qt::view::level3_product_view";
|
static const std::string logPrefix_ = "scwx::qt::view::level3_product_view";
|
||||||
|
|
@ -151,6 +146,22 @@ void Level3ProductView::ConnectRadarProductManager()
|
||||||
Update();
|
Update();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(radar_product_manager().get(),
|
||||||
|
&manager::RadarProductManager::ProductTimesPopulated,
|
||||||
|
this,
|
||||||
|
[this](common::RadarProductGroup group,
|
||||||
|
const std::string& product,
|
||||||
|
std::chrono::system_clock::time_point queryTime)
|
||||||
|
{
|
||||||
|
if (group == common::RadarProductGroup::Level3 &&
|
||||||
|
product == p->product_ && queryTime == selected_time())
|
||||||
|
{
|
||||||
|
// If the data associated with the currently selected time is
|
||||||
|
// reloaded, update the view
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Level3ProductView::DisconnectRadarProductManager()
|
void Level3ProductView::DisconnectRadarProductManager()
|
||||||
|
|
@ -596,6 +607,4 @@ bool Level3ProductView::IgnoreUnits() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace view
|
} // namespace scwx::qt::view
|
||||||
} // namespace qt
|
|
||||||
} // namespace scwx
|
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,7 @@
|
||||||
#include <boost/range/irange.hpp>
|
#include <boost/range/irange.hpp>
|
||||||
#include <boost/timer/timer.hpp>
|
#include <boost/timer/timer.hpp>
|
||||||
|
|
||||||
namespace scwx
|
namespace scwx::qt::view
|
||||||
{
|
|
||||||
namespace qt
|
|
||||||
{
|
|
||||||
namespace view
|
|
||||||
{
|
{
|
||||||
|
|
||||||
static const std::string logPrefix_ = "scwx::qt::view::level3_radial_view";
|
static const std::string logPrefix_ = "scwx::qt::view::level3_radial_view";
|
||||||
|
|
@ -31,15 +27,7 @@ static constexpr std::uint32_t VALUES_PER_VERTEX = 2u;
|
||||||
class Level3RadialView::Impl
|
class Level3RadialView::Impl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Impl(Level3RadialView* self) :
|
explicit Impl(Level3RadialView* self) : self_ {self} {}
|
||||||
self_ {self},
|
|
||||||
latitude_ {},
|
|
||||||
longitude_ {},
|
|
||||||
range_ {},
|
|
||||||
vcp_ {},
|
|
||||||
sweepTime_ {}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
~Impl() { threadPool_.join(); };
|
~Impl() { threadPool_.join(); };
|
||||||
|
|
||||||
void ComputeCoordinates(
|
void ComputeCoordinates(
|
||||||
|
|
@ -65,13 +53,13 @@ public:
|
||||||
bool lastShowSmoothedRangeFolding_ {false};
|
bool lastShowSmoothedRangeFolding_ {false};
|
||||||
bool lastSmoothingEnabled_ {false};
|
bool lastSmoothingEnabled_ {false};
|
||||||
|
|
||||||
float latitude_;
|
float latitude_ {};
|
||||||
float longitude_;
|
float longitude_ {};
|
||||||
std::optional<float> elevation_ {};
|
std::optional<float> elevation_ {};
|
||||||
float range_;
|
float range_ {};
|
||||||
std::uint16_t vcp_;
|
std::uint16_t vcp_ {};
|
||||||
|
|
||||||
std::chrono::system_clock::time_point sweepTime_;
|
std::chrono::system_clock::time_point sweepTime_ {};
|
||||||
};
|
};
|
||||||
|
|
||||||
Level3RadialView::Level3RadialView(
|
Level3RadialView::Level3RadialView(
|
||||||
|
|
@ -148,9 +136,12 @@ void Level3RadialView::ComputeSweep()
|
||||||
std::shared_ptr<wsr88d::rpg::Level3Message> message;
|
std::shared_ptr<wsr88d::rpg::Level3Message> message;
|
||||||
std::chrono::system_clock::time_point requestedTime {selected_time()};
|
std::chrono::system_clock::time_point requestedTime {selected_time()};
|
||||||
std::chrono::system_clock::time_point foundTime;
|
std::chrono::system_clock::time_point foundTime;
|
||||||
std::tie(message, foundTime) =
|
types::RadarProductLoadStatus loadStatus {};
|
||||||
|
std::tie(message, foundTime, loadStatus) =
|
||||||
radarProductManager->GetLevel3Data(GetRadarProductName(), requestedTime);
|
radarProductManager->GetLevel3Data(GetRadarProductName(), requestedTime);
|
||||||
|
|
||||||
|
set_load_status(loadStatus);
|
||||||
|
|
||||||
// If a different time was found than what was requested, update it
|
// If a different time was found than what was requested, update it
|
||||||
if (requestedTime != foundTime)
|
if (requestedTime != foundTime)
|
||||||
{
|
{
|
||||||
|
|
@ -160,7 +151,10 @@ void Level3RadialView::ComputeSweep()
|
||||||
if (message == nullptr)
|
if (message == nullptr)
|
||||||
{
|
{
|
||||||
logger_->debug("Level 3 data not found");
|
logger_->debug("Level 3 data not found");
|
||||||
Q_EMIT SweepNotComputed(types::NoUpdateReason::NotLoaded);
|
Q_EMIT SweepNotComputed(
|
||||||
|
loadStatus == types::RadarProductLoadStatus::ProductNotAvailable ?
|
||||||
|
types::NoUpdateReason::NotAvailable :
|
||||||
|
types::NoUpdateReason::NotLoaded);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -752,6 +746,4 @@ std::shared_ptr<Level3RadialView> Level3RadialView::Create(
|
||||||
return std::make_shared<Level3RadialView>(product, radarProductManager);
|
return std::make_shared<Level3RadialView>(product, radarProductManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace view
|
} // namespace scwx::qt::view
|
||||||
} // namespace qt
|
|
||||||
} // namespace scwx
|
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,7 @@
|
||||||
#include <boost/timer/timer.hpp>
|
#include <boost/timer/timer.hpp>
|
||||||
#include <units/angle.h>
|
#include <units/angle.h>
|
||||||
|
|
||||||
namespace scwx
|
namespace scwx::qt::view
|
||||||
{
|
|
||||||
namespace qt
|
|
||||||
{
|
|
||||||
namespace view
|
|
||||||
{
|
{
|
||||||
|
|
||||||
static const std::string logPrefix_ = "scwx::qt::view::level3_raster_view";
|
static const std::string logPrefix_ = "scwx::qt::view::level3_raster_view";
|
||||||
|
|
@ -125,9 +121,12 @@ void Level3RasterView::ComputeSweep()
|
||||||
std::shared_ptr<wsr88d::rpg::Level3Message> message;
|
std::shared_ptr<wsr88d::rpg::Level3Message> message;
|
||||||
std::chrono::system_clock::time_point requestedTime {selected_time()};
|
std::chrono::system_clock::time_point requestedTime {selected_time()};
|
||||||
std::chrono::system_clock::time_point foundTime;
|
std::chrono::system_clock::time_point foundTime;
|
||||||
std::tie(message, foundTime) =
|
types::RadarProductLoadStatus loadStatus {};
|
||||||
|
std::tie(message, foundTime, loadStatus) =
|
||||||
radarProductManager->GetLevel3Data(GetRadarProductName(), requestedTime);
|
radarProductManager->GetLevel3Data(GetRadarProductName(), requestedTime);
|
||||||
|
|
||||||
|
set_load_status(loadStatus);
|
||||||
|
|
||||||
// If a different time was found than what was requested, update it
|
// If a different time was found than what was requested, update it
|
||||||
if (requestedTime != foundTime)
|
if (requestedTime != foundTime)
|
||||||
{
|
{
|
||||||
|
|
@ -137,7 +136,10 @@ void Level3RasterView::ComputeSweep()
|
||||||
if (message == nullptr)
|
if (message == nullptr)
|
||||||
{
|
{
|
||||||
logger_->debug("Level 3 data not found");
|
logger_->debug("Level 3 data not found");
|
||||||
Q_EMIT SweepNotComputed(types::NoUpdateReason::NotLoaded);
|
Q_EMIT SweepNotComputed(
|
||||||
|
loadStatus == types::RadarProductLoadStatus::ProductNotAvailable ?
|
||||||
|
types::NoUpdateReason::NotAvailable :
|
||||||
|
types::NoUpdateReason::NotLoaded);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -538,6 +540,4 @@ std::shared_ptr<Level3RasterView> Level3RasterView::Create(
|
||||||
return std::make_shared<Level3RasterView>(product, radarProductManager);
|
return std::make_shared<Level3RasterView>(product, radarProductManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace view
|
} // namespace scwx::qt::view
|
||||||
} // namespace qt
|
|
||||||
} // namespace scwx
|
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,7 @@
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include <boost/uuid/random_generator.hpp>
|
#include <boost/uuid/random_generator.hpp>
|
||||||
|
|
||||||
namespace scwx
|
namespace scwx::qt::view
|
||||||
{
|
|
||||||
namespace qt
|
|
||||||
{
|
|
||||||
namespace view
|
|
||||||
{
|
{
|
||||||
|
|
||||||
static const std::string logPrefix_ = "scwx::qt::view::overlay_product_view";
|
static const std::string logPrefix_ = "scwx::qt::view::overlay_product_view";
|
||||||
|
|
@ -128,6 +124,22 @@ void OverlayProductView::Impl::ConnectRadarProductManager()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
|
|
||||||
|
connect(radarProductManager_.get(),
|
||||||
|
&manager::RadarProductManager::ProductTimesPopulated,
|
||||||
|
self_,
|
||||||
|
[this](common::RadarProductGroup group,
|
||||||
|
const std::string& product,
|
||||||
|
std::chrono::system_clock::time_point queryTime)
|
||||||
|
{
|
||||||
|
if (group == common::RadarProductGroup::Level3 &&
|
||||||
|
product == kNst_ && queryTime == selectedTime_)
|
||||||
|
{
|
||||||
|
// If the data associated with the currently selected time is
|
||||||
|
// reloaded, update the view
|
||||||
|
Update(product);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayProductView::Impl::DisconnectRadarProductManager()
|
void OverlayProductView::Impl::DisconnectRadarProductManager()
|
||||||
|
|
@ -286,7 +298,7 @@ void OverlayProductView::Impl::Update(const std::string& product)
|
||||||
std::shared_ptr<wsr88d::rpg::Level3Message> message;
|
std::shared_ptr<wsr88d::rpg::Level3Message> message;
|
||||||
std::chrono::system_clock::time_point requestedTime {selectedTime_};
|
std::chrono::system_clock::time_point requestedTime {selectedTime_};
|
||||||
std::chrono::system_clock::time_point foundTime;
|
std::chrono::system_clock::time_point foundTime;
|
||||||
std::tie(message, foundTime) =
|
std::tie(message, foundTime, std::ignore) =
|
||||||
radarProductManager_->GetLevel3Data(product, requestedTime);
|
radarProductManager_->GetLevel3Data(product, requestedTime);
|
||||||
|
|
||||||
// If a different time was found than what was requested, update it
|
// If a different time was found than what was requested, update it
|
||||||
|
|
@ -329,6 +341,4 @@ void OverlayProductView::SetAutoUpdate(bool enabled)
|
||||||
p->autoUpdateEnabled_ = enabled;
|
p->autoUpdateEnabled_ = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace view
|
} // namespace scwx::qt::view
|
||||||
} // namespace qt
|
|
||||||
} // namespace scwx
|
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,8 @@ public:
|
||||||
std::chrono::system_clock::time_point selectedTime_;
|
std::chrono::system_clock::time_point selectedTime_;
|
||||||
bool showSmoothedRangeFolding_ {false};
|
bool showSmoothedRangeFolding_ {false};
|
||||||
bool smoothingEnabled_ {false};
|
bool smoothingEnabled_ {false};
|
||||||
|
types::RadarProductLoadStatus loadStatus_ {
|
||||||
|
types::RadarProductLoadStatus::ProductNotLoaded};
|
||||||
|
|
||||||
std::shared_ptr<manager::RadarProductManager> radarProductManager_;
|
std::shared_ptr<manager::RadarProductManager> radarProductManager_;
|
||||||
|
|
||||||
|
|
@ -90,6 +92,11 @@ std::optional<float> RadarProductView::elevation() const
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
types::RadarProductLoadStatus RadarProductView::load_status() const
|
||||||
|
{
|
||||||
|
return p->loadStatus_;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<manager::RadarProductManager>
|
std::shared_ptr<manager::RadarProductManager>
|
||||||
RadarProductView::radar_product_manager() const
|
RadarProductView::radar_product_manager() const
|
||||||
{
|
{
|
||||||
|
|
@ -126,6 +133,11 @@ std::mutex& RadarProductView::sweep_mutex()
|
||||||
return p->sweepMutex_;
|
return p->sweepMutex_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RadarProductView::set_load_status(types::RadarProductLoadStatus loadStatus)
|
||||||
|
{
|
||||||
|
p->loadStatus_ = loadStatus;
|
||||||
|
}
|
||||||
|
|
||||||
void RadarProductView::set_radar_product_manager(
|
void RadarProductView::set_radar_product_manager(
|
||||||
std::shared_ptr<manager::RadarProductManager> radarProductManager)
|
std::shared_ptr<manager::RadarProductManager> radarProductManager)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,12 @@ public:
|
||||||
[[nodiscard]] virtual std::shared_ptr<common::ColorTable>
|
[[nodiscard]] virtual std::shared_ptr<common::ColorTable>
|
||||||
color_table() const = 0;
|
color_table() const = 0;
|
||||||
[[nodiscard]] virtual const std::vector<boost::gil::rgba8_pixel_t>&
|
[[nodiscard]] virtual const std::vector<boost::gil::rgba8_pixel_t>&
|
||||||
color_table_lut() const;
|
color_table_lut() const;
|
||||||
[[nodiscard]] virtual std::uint16_t color_table_min() const;
|
[[nodiscard]] virtual std::uint16_t color_table_min() const;
|
||||||
[[nodiscard]] virtual std::uint16_t color_table_max() const;
|
[[nodiscard]] virtual std::uint16_t color_table_max() const;
|
||||||
[[nodiscard]] virtual std::optional<float> elevation() const;
|
[[nodiscard]] virtual std::optional<float> elevation() const;
|
||||||
[[nodiscard]] virtual float range() const;
|
[[nodiscard]] types::RadarProductLoadStatus load_status() const;
|
||||||
|
[[nodiscard]] virtual float range() const;
|
||||||
[[nodiscard]] virtual std::chrono::system_clock::time_point
|
[[nodiscard]] virtual std::chrono::system_clock::time_point
|
||||||
sweep_time() const;
|
sweep_time() const;
|
||||||
[[nodiscard]] virtual float unit_scale() const = 0;
|
[[nodiscard]] virtual float unit_scale() const = 0;
|
||||||
|
|
@ -98,6 +99,8 @@ protected:
|
||||||
virtual void DisconnectRadarProductManager() = 0;
|
virtual void DisconnectRadarProductManager() = 0;
|
||||||
virtual void UpdateColorTableLut() = 0;
|
virtual void UpdateColorTableLut() = 0;
|
||||||
|
|
||||||
|
void set_load_status(types::RadarProductLoadStatus loadStatus);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
virtual void ComputeSweep();
|
virtual void ComputeSweep();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit c68bee74549963e9a02e0fa998efad0f10f8256b
|
Subproject commit fd8bc8bf1d07474886ce6773abffab4d315d0cd0
|
||||||
|
|
@ -76,7 +76,7 @@ TEST(AwsLevel3DataProvider, GetTimePointsByDate)
|
||||||
|
|
||||||
AwsLevel3DataProvider provider("KLSX", "N0Q");
|
AwsLevel3DataProvider provider("KLSX", "N0Q");
|
||||||
|
|
||||||
auto timePoints = provider.GetTimePointsByDate(date);
|
auto timePoints = provider.GetTimePointsByDate(date, true);
|
||||||
|
|
||||||
EXPECT_GT(timePoints.size(), 0);
|
EXPECT_GT(timePoints.size(), 0);
|
||||||
for (auto timePoint : timePoints)
|
for (auto timePoint : timePoints)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
id: net.supercellwx.app
|
id: net.supercellwx.app
|
||||||
version: '0.5.1'
|
version: '0.5.2'
|
||||||
runtime: "org.freedesktop.Platform"
|
runtime: "org.freedesktop.Platform"
|
||||||
runtime-version: "23.08"
|
runtime-version: "23.08"
|
||||||
sdk: "org.freedesktop.Sdk"
|
sdk: "org.freedesktop.Sdk"
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,9 @@ public:
|
||||||
std::string FindLatestKey() override;
|
std::string FindLatestKey() override;
|
||||||
std::chrono::system_clock::time_point FindLatestTime() override;
|
std::chrono::system_clock::time_point FindLatestTime() override;
|
||||||
std::vector<std::chrono::system_clock::time_point>
|
std::vector<std::chrono::system_clock::time_point>
|
||||||
GetTimePointsByDate(std::chrono::system_clock::time_point date) override;
|
GetTimePointsByDate(std::chrono::system_clock::time_point date,
|
||||||
|
bool update) override;
|
||||||
|
bool IsDateCached(std::chrono::system_clock::time_point date) override;
|
||||||
std::tuple<bool, size_t, size_t>
|
std::tuple<bool, size_t, size_t>
|
||||||
ListObjects(std::chrono::system_clock::time_point date) override;
|
ListObjects(std::chrono::system_clock::time_point date) override;
|
||||||
std::shared_ptr<wsr88d::NexradFile>
|
std::shared_ptr<wsr88d::NexradFile>
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,12 @@
|
||||||
|
|
||||||
#include <scwx/provider/nexrad_data_provider.hpp>
|
#include <scwx/provider/nexrad_data_provider.hpp>
|
||||||
|
|
||||||
namespace Aws
|
namespace Aws::S3
|
||||||
{
|
|
||||||
namespace S3
|
|
||||||
{
|
{
|
||||||
class S3Client;
|
class S3Client;
|
||||||
} // namespace S3
|
} // namespace Aws::S3
|
||||||
} // namespace Aws
|
|
||||||
|
|
||||||
namespace scwx
|
namespace scwx::provider
|
||||||
{
|
|
||||||
namespace provider
|
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -32,20 +27,23 @@ public:
|
||||||
AwsNexradDataProvider(AwsNexradDataProvider&&) noexcept;
|
AwsNexradDataProvider(AwsNexradDataProvider&&) noexcept;
|
||||||
AwsNexradDataProvider& operator=(AwsNexradDataProvider&&) noexcept;
|
AwsNexradDataProvider& operator=(AwsNexradDataProvider&&) noexcept;
|
||||||
|
|
||||||
size_t cache_size() const override;
|
[[nodiscard]] std::size_t cache_size() const override;
|
||||||
|
|
||||||
std::chrono::system_clock::time_point last_modified() const override;
|
[[nodiscard]] std::chrono::system_clock::time_point
|
||||||
std::chrono::seconds update_period() const override;
|
last_modified() const override;
|
||||||
|
[[nodiscard]] std::chrono::seconds update_period() const override;
|
||||||
|
|
||||||
std::string FindKey(std::chrono::system_clock::time_point time) override;
|
std::string FindKey(std::chrono::system_clock::time_point time) override;
|
||||||
std::string FindLatestKey() override;
|
std::string FindLatestKey() override;
|
||||||
std::chrono::system_clock::time_point FindLatestTime() override;
|
std::chrono::system_clock::time_point FindLatestTime() override;
|
||||||
std::vector<std::chrono::system_clock::time_point>
|
std::vector<std::chrono::system_clock::time_point>
|
||||||
GetTimePointsByDate(std::chrono::system_clock::time_point date) override;
|
GetTimePointsByDate(std::chrono::system_clock::time_point date,
|
||||||
|
bool update) override;
|
||||||
|
bool IsDateCached(std::chrono::system_clock::time_point date) override;
|
||||||
std::tuple<bool, size_t, size_t>
|
std::tuple<bool, size_t, size_t>
|
||||||
ListObjects(std::chrono::system_clock::time_point date) override;
|
ListObjects(std::chrono::system_clock::time_point date) override;
|
||||||
std::shared_ptr<wsr88d::NexradFile>
|
std::shared_ptr<wsr88d::NexradFile>
|
||||||
LoadObjectByKey(const std::string& key) override;
|
LoadObjectByKey(const std::string& key) override;
|
||||||
std::shared_ptr<wsr88d::NexradFile>
|
std::shared_ptr<wsr88d::NexradFile>
|
||||||
LoadObjectByTime(std::chrono::system_clock::time_point time) override;
|
LoadObjectByTime(std::chrono::system_clock::time_point time) override;
|
||||||
std::pair<size_t, size_t> Refresh() override;
|
std::pair<size_t, size_t> Refresh() override;
|
||||||
|
|
@ -61,5 +59,4 @@ private:
|
||||||
std::unique_ptr<Impl> p;
|
std::unique_ptr<Impl> p;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace provider
|
} // namespace scwx::provider
|
||||||
} // namespace scwx
|
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace scwx
|
namespace scwx::provider
|
||||||
{
|
|
||||||
namespace provider
|
|
||||||
{
|
{
|
||||||
|
|
||||||
class NexradDataProvider
|
class NexradDataProvider
|
||||||
|
|
@ -24,7 +22,7 @@ public:
|
||||||
NexradDataProvider(NexradDataProvider&&) noexcept;
|
NexradDataProvider(NexradDataProvider&&) noexcept;
|
||||||
NexradDataProvider& operator=(NexradDataProvider&&) noexcept;
|
NexradDataProvider& operator=(NexradDataProvider&&) noexcept;
|
||||||
|
|
||||||
virtual size_t cache_size() const = 0;
|
[[nodiscard]] virtual size_t cache_size() const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the last modified time. This is equal to the most recent object's
|
* Gets the last modified time. This is equal to the most recent object's
|
||||||
|
|
@ -32,7 +30,8 @@ public:
|
||||||
*
|
*
|
||||||
* @return Last modified time
|
* @return Last modified time
|
||||||
*/
|
*/
|
||||||
virtual std::chrono::system_clock::time_point last_modified() const = 0;
|
[[nodiscard]] virtual std::chrono::system_clock::time_point
|
||||||
|
last_modified() const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current update period. This is equal to the difference between
|
* Gets the current update period. This is equal to the difference between
|
||||||
|
|
@ -41,7 +40,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return Update period
|
* @return Update period
|
||||||
*/
|
*/
|
||||||
virtual std::chrono::seconds update_period() const = 0;
|
[[nodiscard]] virtual std::chrono::seconds update_period() const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the most recent key in the cache, no later than the time provided.
|
* Finds the most recent key in the cache, no later than the time provided.
|
||||||
|
|
@ -116,7 +115,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return NEXRAD data time point
|
* @return NEXRAD data time point
|
||||||
*/
|
*/
|
||||||
virtual std::chrono::system_clock::time_point
|
[[nodiscard]] virtual std::chrono::system_clock::time_point
|
||||||
GetTimePointByKey(const std::string& key) const = 0;
|
GetTimePointByKey(const std::string& key) const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -124,11 +123,22 @@ public:
|
||||||
* to the cache if required.
|
* to the cache if required.
|
||||||
*
|
*
|
||||||
* @param date Date for which to get NEXRAD data time points
|
* @param date Date for which to get NEXRAD data time points
|
||||||
|
* @param update Whether or not to list and add data not present in the cache
|
||||||
*
|
*
|
||||||
* @return NEXRAD data time points
|
* @return NEXRAD data time points
|
||||||
*/
|
*/
|
||||||
virtual std::vector<std::chrono::system_clock::time_point>
|
virtual std::vector<std::chrono::system_clock::time_point>
|
||||||
GetTimePointsByDate(std::chrono::system_clock::time_point date) = 0;
|
GetTimePointsByDate(std::chrono::system_clock::time_point date,
|
||||||
|
bool update) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if time points for the requested date are cached.
|
||||||
|
*
|
||||||
|
* @param date Date for which to query the cache
|
||||||
|
*
|
||||||
|
* @return Whether or not the requested date is cached
|
||||||
|
*/
|
||||||
|
virtual bool IsDateCached(std::chrono::system_clock::time_point date) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests available NEXRAD products for the current radar site, and adds
|
* Requests available NEXRAD products for the current radar site, and adds
|
||||||
|
|
@ -148,5 +158,4 @@ private:
|
||||||
std::unique_ptr<Impl> p;
|
std::unique_ptr<Impl> p;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace provider
|
} // namespace scwx::provider
|
||||||
} // namespace scwx
|
|
||||||
|
|
|
||||||
|
|
@ -289,11 +289,18 @@ AwsLevel2ChunksDataProvider::FindLatestTime()
|
||||||
|
|
||||||
std::vector<std::chrono::system_clock::time_point>
|
std::vector<std::chrono::system_clock::time_point>
|
||||||
AwsLevel2ChunksDataProvider::GetTimePointsByDate(
|
AwsLevel2ChunksDataProvider::GetTimePointsByDate(
|
||||||
std::chrono::system_clock::time_point /*date*/)
|
std::chrono::system_clock::time_point /* date */, bool /* update */)
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AwsLevel2ChunksDataProvider::IsDateCached(
|
||||||
|
std::chrono::system_clock::time_point /* date */)
|
||||||
|
{
|
||||||
|
// No cache, default to true
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::chrono::system_clock::time_point
|
std::chrono::system_clock::time_point
|
||||||
AwsLevel2ChunksDataProvider::Impl::GetScanTime(const std::string& prefix)
|
AwsLevel2ChunksDataProvider::Impl::GetScanTime(const std::string& prefix)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ static const std::string logPrefix_ =
|
||||||
"scwx::provider::aws_level2_data_provider";
|
"scwx::provider::aws_level2_data_provider";
|
||||||
static const auto logger_ = util::Logger::Create(logPrefix_);
|
static const auto logger_ = util::Logger::Create(logPrefix_);
|
||||||
|
|
||||||
static const std::string kDefaultBucketName_ = "noaa-nexrad-level2";
|
static const std::string kDefaultBucketName_ = "unidata-nexrad-level2";
|
||||||
static const std::string kDefaultRegion_ = "us-east-1";
|
static const std::string kDefaultRegion_ = "us-east-1";
|
||||||
|
|
||||||
class AwsLevel2DataProvider::Impl
|
class AwsLevel2DataProvider::Impl
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,7 @@
|
||||||
#include <aws/s3/model/ListObjectsV2Request.h>
|
#include <aws/s3/model/ListObjectsV2Request.h>
|
||||||
#include <fmt/chrono.h>
|
#include <fmt/chrono.h>
|
||||||
|
|
||||||
namespace scwx
|
namespace scwx::provider
|
||||||
{
|
|
||||||
namespace provider
|
|
||||||
{
|
{
|
||||||
|
|
||||||
static const std::string logPrefix_ =
|
static const std::string logPrefix_ =
|
||||||
|
|
@ -177,7 +175,7 @@ std::chrono::system_clock::time_point AwsNexradDataProvider::FindLatestTime()
|
||||||
|
|
||||||
std::vector<std::chrono::system_clock::time_point>
|
std::vector<std::chrono::system_clock::time_point>
|
||||||
AwsNexradDataProvider::GetTimePointsByDate(
|
AwsNexradDataProvider::GetTimePointsByDate(
|
||||||
std::chrono::system_clock::time_point date)
|
std::chrono::system_clock::time_point date, bool update)
|
||||||
{
|
{
|
||||||
const auto day = std::chrono::floor<std::chrono::days>(date);
|
const auto day = std::chrono::floor<std::chrono::days>(date);
|
||||||
|
|
||||||
|
|
@ -188,23 +186,26 @@ AwsNexradDataProvider::GetTimePointsByDate(
|
||||||
std::shared_lock lock(p->objectsMutex_);
|
std::shared_lock lock(p->objectsMutex_);
|
||||||
|
|
||||||
// Is the date present in the date list?
|
// Is the date present in the date list?
|
||||||
bool currentDatePresent;
|
bool currentDatePresent = false;
|
||||||
auto currentDateIterator =
|
auto currentDateIterator =
|
||||||
std::find(p->objectDates_.cbegin(), p->objectDates_.cend(), day);
|
std::find(p->objectDates_.cbegin(), p->objectDates_.cend(), day);
|
||||||
if (currentDateIterator == p->objectDates_.cend())
|
if (currentDateIterator == p->objectDates_.cend())
|
||||||
{
|
{
|
||||||
// Temporarily unlock mutex
|
if (update)
|
||||||
lock.unlock();
|
|
||||||
|
|
||||||
// List objects, since the date is not present in the date list
|
|
||||||
auto [success, newObjects, totalObjects] = ListObjects(date);
|
|
||||||
if (success)
|
|
||||||
{
|
{
|
||||||
p->UpdateObjectDates(date);
|
// Temporarily unlock mutex
|
||||||
}
|
lock.unlock();
|
||||||
|
|
||||||
// Re-lock mutex
|
// List objects, since the date is not present in the date list
|
||||||
lock.lock();
|
const auto [success, newObjects, totalObjects] = ListObjects(date);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
p->UpdateObjectDates(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-lock mutex
|
||||||
|
lock.lock();
|
||||||
|
}
|
||||||
|
|
||||||
currentDatePresent = false;
|
currentDatePresent = false;
|
||||||
}
|
}
|
||||||
|
|
@ -214,8 +215,8 @@ AwsNexradDataProvider::GetTimePointsByDate(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine objects to retrieve
|
// Determine objects to retrieve
|
||||||
auto objectsBegin = p->objects_.lower_bound(day);
|
const auto objectsBegin = p->objects_.lower_bound(day);
|
||||||
auto objectsEnd = p->objects_.lower_bound(day + std::chrono::days {1});
|
const auto objectsEnd = p->objects_.lower_bound(day + std::chrono::days {1});
|
||||||
|
|
||||||
// Copy time points to destination vector
|
// Copy time points to destination vector
|
||||||
std::transform(objectsBegin,
|
std::transform(objectsBegin,
|
||||||
|
|
@ -236,6 +237,20 @@ AwsNexradDataProvider::GetTimePointsByDate(
|
||||||
return timePoints;
|
return timePoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AwsNexradDataProvider::IsDateCached(
|
||||||
|
std::chrono::system_clock::time_point date)
|
||||||
|
{
|
||||||
|
const auto day = std::chrono::floor<std::chrono::days>(date);
|
||||||
|
|
||||||
|
const std::shared_lock lock(p->objectsMutex_);
|
||||||
|
|
||||||
|
// Is the date present in the date list?
|
||||||
|
const auto currentDateIterator =
|
||||||
|
std::find(p->objectDates_.cbegin(), p->objectDates_.cend(), day);
|
||||||
|
|
||||||
|
return currentDateIterator != p->objectDates_.cend();
|
||||||
|
}
|
||||||
|
|
||||||
std::tuple<bool, size_t, size_t>
|
std::tuple<bool, size_t, size_t>
|
||||||
AwsNexradDataProvider::ListObjects(std::chrono::system_clock::time_point date)
|
AwsNexradDataProvider::ListObjects(std::chrono::system_clock::time_point date)
|
||||||
{
|
{
|
||||||
|
|
@ -446,5 +461,4 @@ void AwsNexradDataProvider::Impl::UpdateObjectDates(
|
||||||
objectDates_.push_back(day);
|
objectDates_.push_back(day);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace provider
|
} // namespace scwx::provider
|
||||||
} // namespace scwx
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue