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