Merge pull request #32 from dpaulat/feature/linux

Add support for Linux
This commit is contained in:
Dan Paulat 2023-04-20 16:52:22 -05:00 committed by GitHub
commit 331fc84794
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
76 changed files with 653 additions and 305 deletions

View file

@ -4,11 +4,9 @@ on:
workflow_dispatch:
push:
branches:
- 'master'
- 'develop'
pull_request:
branches:
- 'master'
- 'develop'
concurrency:
@ -24,6 +22,8 @@ jobs:
- name: win64_msvc2022
os: windows-2022
build_type: Release
env_cc: ''
env_cxx: ''
compiler: msvc
msvc_arch: x64
msvc_toolset: 14.34
@ -34,8 +34,28 @@ jobs:
conan_arch: x86_64
conan_compiler: Visual Studio
conan_compiler_version: 17
conan_compiler_runtime: MD
conan_compiler_runtime: --settings compiler.runtime=MD
conan_package_manager: ''
artifact_suffix: windows-x64
- name: linux64_gcc
os: ubuntu-22.04
build_type: Release
env_cc: gcc-11
env_cxx: g++-11
compiler: gcc
qt_version: 6.4.2
qt_arch: gcc_64
qt_tools: ''
conan_arch: x86_64
conan_compiler: gcc
conan_compiler_version: 11
conan_compiler_runtime: ''
conan_package_manager: --conf tools.system.package_manager:mode=install --conf tools.system.package_manager:sudo=True
artifact_suffix: linux-x64
name: ${{ matrix.name }}
env:
CC: ${{ matrix.env_cc }}
CXX: ${{ matrix.env_cxx }}
runs-on: ${{ matrix.os }}
steps:
@ -63,6 +83,13 @@ jobs:
toolset: ${{ matrix.msvc_toolset }}
vsversion: ${{ matrix.msvc_version }}
- name: Setup Ubuntu Environment
if: matrix.os == 'ubuntu-22.04'
shell: bash
run: |
sudo apt-get install doxygen \
ninja-build
- name: Setup Python Environment
shell: pwsh
run: |
@ -81,7 +108,8 @@ jobs:
--settings build_type=${{ matrix.build_type }} `
--settings compiler="${{ matrix.conan_compiler }}" `
--settings compiler.version=${{ matrix.conan_compiler_version }} `
--settings compiler.runtime=${{ matrix.conan_compiler_runtime }}
${{ matrix.conan_compiler_runtime }} `
${{ matrix.conan_package_manager }}
- name: Build Supercell Wx
shell: pwsh

3
.gitmodules vendored
View file

@ -28,3 +28,6 @@
[submodule "external/aws-sdk-cpp"]
path = external/aws-sdk-cpp
url = https://github.com/aws/aws-sdk-cpp.git
[submodule "external/date"]
path = external/date
url = https://github.com/HowardHinnant/date.git

View file

@ -16,6 +16,7 @@ Supercell Wx uses code from the following dependencies:
| [cmake-conan](https://github.com/conan-io/cmake-conan) | [MIT License](https://spdx.org/licenses/MIT.html) |
| [cpr](https://github.com/libcpr/cpr) | [MIT License](https://spdx.org/licenses/MIT.html) |
| [CSS Color Parser](https://github.com/deanm/css-color-parser-js) | [MIT License](https://spdx.org/licenses/MIT.html) | Ported to C++ for MapLibre GL Native |
| [Date](https://github.com/HowardHinnant/date) | [MIT License](https://spdx.org/licenses/MIT.html) |
| [Dear ImGui](https://github.com/ocornut/imgui) | [MIT License](https://spdx.org/licenses/MIT.html) |
| [FreeType](https://freetype.org/) | [Freetype Project License](https://spdx.org/licenses/FTL.html) |
| [FreeType GL](https://github.com/rougier/freetype-gl) | [BSD 2-Clause with views sentence](https://spdx.org/licenses/BSD-2-Clause-Views.html) |

View file

@ -1,12 +1,23 @@
# Supercell Wx
[![CI](https://github.com/dpaulat/supercell-wx/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/dpaulat/supercell-wx/actions/workflows/ci.yml)
[![CI](https://github.com/dpaulat/supercell-wx/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/dpaulat/supercell-wx/actions/workflows/ci.yml)
[![Documentation Status](https://readthedocs.org/projects/supercell-wx/badge/?version=latest)](https://supercell-wx.readthedocs.io/en/latest/?badge=latest)
[![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat&logo=discord&logoColor=white&labelColor=%235865f2)](https://discord.gg/snH4tNav7g)
Supercell Wx is a free, open source application to visualize live NEXRAD Level 2
and Level 3 data, and severe weather alerts. It displays continously updating
and Level 3 data, and severe weather alerts. It displays continuously updating
weather data on top of a responsive map, providing the capability to monitor
weather events using reflectivity, velocity, and other products.
![image](https://supercell-wx.readthedocs.io/en/latest/_images/initial-setup-03-initial-configured-small.png)
## Supported Platforms
Supercell Wx supports the following 64-bit operating systems:
- Windows 10 (1809 or later)
- Windows 11
- Linux
- Ubuntu 22.04+
- Fedora Linux 34+
- Most distributions supporting the GCC Standard C++ Library 11+

View file

@ -5,6 +5,7 @@ set_property(DIRECTORY
APPEND
PROPERTY CMAKE_CONFIGURE_DEPENDS
aws-sdk-cpp.cmake
date.cmake
freetype-gl.cmake
hsluv-c.cmake
imgui.cmake
@ -12,6 +13,7 @@ set_property(DIRECTORY
stb.cmake)
include(aws-sdk-cpp.cmake)
include(date.cmake)
include(freetype-gl.cmake)
include(hsluv-c.cmake)
include(imgui.cmake)

1
external/date vendored Submodule

@ -0,0 +1 @@
Subproject commit 50acf3ffd8b09deeec6980be824f2ac54a50b095

7
external/date.cmake vendored Normal file
View file

@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.20)
set(PROJECT_NAME scwx-date)
set(USE_SYSTEM_TZ_DB ON)
set(BUILD_TZ_LIB ON)
add_subdirectory(date)

View file

@ -3,6 +3,7 @@ set(PROJECT_NAME scwx-mbgl)
set(gtest_disable_pthreads ON)
set(MBGL_WITH_QT ON)
set(MBGL_QT_WITH_INTERNAL_ICU ON)
add_subdirectory(mapbox-gl-native)
find_package(ZLIB)

View file

@ -373,7 +373,7 @@ target_link_libraries(scwx-qt PUBLIC Qt${QT_VERSION_MAJOR}::Widgets
Boost::json
Boost::timer
qmaplibregl
opengl32
$<$<CXX_COMPILER_ID:MSVC>:opengl32>
freetype-gl
GeographicLib::GeographicLib
glm::glm
@ -389,8 +389,12 @@ install(TARGETS supercell-wx
RUNTIME_DEPENDENCIES
PRE_EXCLUDE_REGEXES "api-ms-" "ext-ms-" "qt6"
POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
"^(/usr)?/lib/.*\\.so(\\..*)?"
RUNTIME
COMPONENT supercell-wx)
COMPONENT supercell-wx
LIBRARY
COMPONENT supercell-wx
OPTIONAL)
qt_generate_deploy_app_script(TARGET qmaplibregl
FILENAME_VARIABLE deploy_script_qmaplibregl

View file

@ -29,7 +29,7 @@ static bool initialized_ {false};
static std::unordered_map<std::string, std::string> countyMap_;
static std::shared_mutex countyMutex_;
void CountyDatabase::Initialize()
void Initialize()
{
if (initialized_)
{

View file

@ -4,7 +4,6 @@
#include <scwx/common/sites.hpp>
#include <scwx/util/logger.hpp>
#include <format>
#include <shared_mutex>
#include <unordered_map>
@ -118,18 +117,18 @@ std::string RadarSite::location_name() const
if (p->country_ == "USA")
{
locationName = std::format("{}, {}", p->place_, p->state_);
locationName = fmt::format("{}, {}", p->place_, p->state_);
}
else if (std::all_of(p->state_.cbegin(),
p->state_.cend(),
[](char c) { return std::isdigit(c); }))
{
locationName = std::format("{}, {}", p->place_, p->country_);
locationName = fmt::format("{}, {}", p->place_, p->country_);
}
else
{
locationName =
std::format("{}, {}, {}", p->place_, p->state_, p->country_);
fmt::format("{}, {}, {}", p->place_, p->state_, p->country_);
}
return locationName;

View file

@ -2,12 +2,18 @@
#include <string>
#pragma warning(push, 0)
#if defined(_MSC_VER)
# pragma warning(push, 0)
#endif
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <mbgl/util/constants.hpp>
#pragma warning(pop)
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
namespace scwx
{

View file

@ -255,8 +255,8 @@ void GeoLine::Impl::Update()
const float ty = points_[1].longitude_;
// Offset x/y in pixels
const float ox = width_ * 0.5f * std::cosf(angle_);
const float oy = width_ * 0.5f * std::sinf(angle_);
const float ox = width_ * 0.5f * cosf(angle_);
const float oy = width_ * 0.5f * sinf(angle_);
// Texture coordinates
const float ls = texture_.sLeft_;

View file

@ -35,8 +35,8 @@ public:
z_ {0.0f},
width_ {0.0f},
height_ {0.0f},
borderColor_ {0, 0, 0, 0},
borderWidth_ {0.0f},
borderColor_ {0, 0, 0, 0},
fillColor_ {std::nullopt},
shaderProgram_ {nullptr},
uMVPMatrixLocation_(GL_INVALID_INDEX),

View file

@ -2,9 +2,15 @@
#include <scwx/qt/gl/shader_program.hpp>
#include <scwx/util/logger.hpp>
#pragma warning(push, 0)
#if defined(_MSC_VER)
# pragma warning(push, 0)
#endif
#include <glm/gtc/type_ptr.hpp>
#pragma warning(pop)
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
namespace scwx
{

View file

@ -1,6 +1,8 @@
#include <scwx/qt/main/application.hpp>
#include <scwx/util/logger.hpp>
#include <condition_variable>
namespace scwx
{
namespace qt

View file

@ -30,6 +30,10 @@
#include <QStandardPaths>
#include <QToolButton>
#if !defined(_MSC_VER)
# include <date/date.h>
#endif
namespace scwx
{
namespace qt
@ -265,14 +269,14 @@ void MainWindow::on_actionOpenNexrad_triggered()
dialog,
&QFileDialog::finished,
this,
[=]() { update(); },
[this]() { update(); },
Qt::QueuedConnection);
connect(
dialog,
&QFileDialog::fileSelected,
this,
[=](const QString& file)
[=, this](const QString& file)
{
logger_->info("Selected: {}", file.toStdString());
@ -283,7 +287,7 @@ void MainWindow::on_actionOpenNexrad_triggered()
request.get(),
&request::NexradFileRequest::RequestComplete,
this,
[=](std::shared_ptr<request::NexradFileRequest> request)
[=, this](std::shared_ptr<request::NexradFileRequest> request)
{
std::shared_ptr<types::RadarProductRecord> record =
request->radar_product_record();
@ -327,13 +331,13 @@ void MainWindow::on_actionOpenTextEvent_triggered()
dialog,
&QFileDialog::finished,
this,
[=]() { update(); },
[this]() { update(); },
Qt::QueuedConnection);
connect(dialog,
&QFileDialog::fileSelected,
this,
[=](const QString& file)
[this](const QString& file)
{
logger_->info("Selected: {}", file.toStdString());
p->textEventManager_->LoadFile(file.toStdString());
@ -407,8 +411,14 @@ void MainWindow::on_resourceTreeView_doubleClicked(const QModelIndex& index)
static const std::string timeFormat {"%Y-%m-%d %H:%M:%S"};
using namespace std::chrono;
#if !defined(_MSC_VER)
using namespace date;
#endif
std::istringstream in {selectedString};
in >> std::chrono::parse(timeFormat, time);
in >> parse(timeFormat, time);
if (in.fail())
{
@ -473,7 +483,7 @@ void MainWindowImpl::ConfigureMapLayout()
maps_.resize(mapCount);
auto MoveSplitter = [=](int /*pos*/, int /*index*/)
auto MoveSplitter = [=, this](int /*pos*/, int /*index*/)
{
QSplitter* s = static_cast<QSplitter*>(sender());
@ -565,7 +575,7 @@ void MainWindowImpl::ConnectOtherSignals()
connect(qApp,
&QApplication::focusChanged,
mainWindow_,
[=](QWidget* /*old*/, QWidget* now) { HandleFocusChange(now); });
[this](QWidget* /*old*/, QWidget* now) { HandleFocusChange(now); });
connect(level2ProductsWidget_,
&ui::Level2ProductsWidget::RadarProductSelected,
mainWindow_,
@ -595,7 +605,7 @@ void MainWindowImpl::ConnectOtherSignals()
alertDockWidget_,
&ui::AlertDockWidget::MoveMap,
this,
[=](double latitude, double longitude)
[this](double latitude, double longitude)
{
for (map::MapWidget* map : maps_)
{

View file

@ -14,14 +14,20 @@
#include <mutex>
#include <shared_mutex>
#pragma warning(push, 0)
#if defined(_MSC_VER)
# pragma warning(push, 0)
#endif
#include <boost/asio/steady_timer.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/range/irange.hpp>
#include <boost/timer/timer.hpp>
#include <fmt/chrono.h>
#include <QMapLibreGL/QMapLibreGL>
#pragma warning(pop)
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
namespace scwx
{
@ -254,14 +260,14 @@ std::string ProviderManager::name() const
if (group_ == common::RadarProductGroup::Level3)
{
name = std::format("{}, {}, {}",
name = fmt::format("{}, {}, {}",
radarId_,
common::GetRadarProductGroupName(group_),
product_);
}
else
{
name = std::format(
name = fmt::format(
"{}, {}", radarId_, common::GetRadarProductGroupName(group_));
}
@ -356,7 +362,7 @@ RadarProductManager::coordinates(common::RadialSize radialSize) const
return p->coordinates1Degree_;
}
throw std::exception("Invalid radial size");
throw std::invalid_argument("Invalid radial size");
}
float RadarProductManager::gate_size() const
@ -506,7 +512,7 @@ void RadarProductManager::EnableRefresh(common::RadarProductGroup group,
// Only enable refresh on available products
scwx::util::async(
[=]()
[=, this]()
{
providerManager->provider_->RequestAvailableProducts();
auto availableProducts =
@ -597,7 +603,7 @@ void RadarProductManagerImpl::RefreshData(
}
scwx::util::async(
[=]()
[=, this]()
{
auto [newObjects, totalObjects] =
providerManager->provider_->Refresh();
@ -647,7 +653,7 @@ void RadarProductManagerImpl::RefreshData(
{
providerManager->refreshTimer_.expires_after(interval);
providerManager->refreshTimer_.async_wait(
[=](const boost::system::error_code& e)
[=, this](const boost::system::error_code& e)
{
if (e == boost::system::errc::success)
{
@ -1151,7 +1157,7 @@ void RadarProductManager::UpdateAvailableProducts()
logger_->debug("UpdateAvailableProducts()");
scwx::util::async(
[=]()
[this]()
{
auto level3ProviderManager =
p->GetLevel3ProviderManager(kDefaultLevel3Product_);

View file

@ -35,7 +35,7 @@ public:
warningsProvider_ {kDefaultWarningsProviderUrl}
{
util::async(
[=]()
[this]()
{
main::Application::WaitForInitialization();
logger_->debug("Start Refresh");
@ -105,7 +105,7 @@ void TextEventManager::LoadFile(const std::string& filename)
logger_->debug("LoadFile: {}", filename);
util::async(
[=]()
[=, this]()
{
awips::TextProductFile file;
@ -217,7 +217,7 @@ void TextEventManager::Impl::Refresh()
using namespace std::chrono;
refreshTimer_.expires_after(15s);
refreshTimer_.async_wait(
[=](const boost::system::error_code& e)
[this](const boost::system::error_code& e)
{
if (e == boost::asio::error::operation_aborted)
{

View file

@ -332,7 +332,7 @@ void AlertLayerHandler::UpdateAlerts()
using namespace std::chrono;
alertUpdateTimer_.expires_after(15s);
alertUpdateTimer_.async_wait(
[=](const boost::system::error_code& e)
[this](const boost::system::error_code& e)
{
if (e == boost::asio::error::operation_aborted)
{

View file

@ -2,10 +2,16 @@
#include <scwx/qt/gl/shader_program.hpp>
#include <scwx/util/logger.hpp>
#pragma warning(push, 0)
#if defined(_MSC_VER)
# pragma warning(push, 0)
#endif
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#pragma warning(pop)
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
namespace scwx
{
@ -20,12 +26,13 @@ static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
class ColorTableLayerImpl
{
public:
explicit ColorTableLayerImpl(std::shared_ptr<MapContext> context) :
explicit ColorTableLayerImpl() :
shaderProgram_(nullptr),
uMVPMatrixLocation_(GL_INVALID_INDEX),
vbo_ {GL_INVALID_INDEX},
vao_ {GL_INVALID_INDEX},
texture_ {GL_INVALID_INDEX},
colorTable_ {},
colorTableNeedsUpdate_ {true}
{
}
@ -44,7 +51,7 @@ public:
};
ColorTableLayer::ColorTableLayer(std::shared_ptr<MapContext> context) :
GenericLayer(context), p(std::make_unique<ColorTableLayerImpl>(context))
GenericLayer(context), p(std::make_unique<ColorTableLayerImpl>())
{
}
ColorTableLayer::~ColorTableLayer() = default;
@ -104,7 +111,7 @@ void ColorTableLayer::Initialize()
connect(context()->radar_product_view().get(),
&view::RadarProductView::ColorTableUpdated,
this,
[=]() { p->colorTableNeedsUpdate_ = true; });
[this]() { p->colorTableNeedsUpdate_ = true; });
}
void ColorTableLayer::Render(

View file

@ -18,6 +18,7 @@
#include <backends/imgui_impl_opengl3.h>
#include <backends/imgui_impl_qt.hpp>
#include <boost/uuid/random_generator.hpp>
#include <fmt/format.h>
#include <imgui.h>
#include <QApplication>
#include <QColor>
@ -89,7 +90,7 @@ public:
// Create ImGui Context
static size_t currentMapId_ {0u};
imGuiContextName_ = std::format("Map {}", ++currentMapId_);
imGuiContextName_ = fmt::format("Map {}", ++currentMapId_);
imGuiContext_ =
model::ImGuiContextModel::Instance().CreateContext(imGuiContextName_);
@ -501,8 +502,8 @@ void MapWidget::SetMapLocation(double latitude,
double longitude,
bool updateRadarSite)
{
if (p->map_ != nullptr && p->prevLatitude_ != latitude ||
p->prevLongitude_ != longitude)
if (p->map_ != nullptr &&
(p->prevLatitude_ != latitude || p->prevLongitude_ != longitude))
{
// Update the map location
p->map_->setCoordinate({latitude, longitude});
@ -786,6 +787,9 @@ void MapWidget::mapChanged(QMapLibreGL::Map::MapChange mapChange)
case QMapLibreGL::Map::MapChangeDidFinishLoadingStyle:
AddLayers();
break;
default:
break;
}
}
@ -796,15 +800,15 @@ void MapWidgetImpl::RadarProductManagerConnect()
connect(radarProductManager_.get(),
&manager::RadarProductManager::Level3ProductsChanged,
this,
[&]() { emit widget_->Level3ProductsChanged(); });
[this]() { emit widget_->Level3ProductsChanged(); });
connect(
radarProductManager_.get(),
&manager::RadarProductManager::NewDataAvailable,
this,
[&](common::RadarProductGroup group,
const std::string& product,
std::chrono::system_clock::time_point latestTime)
[this](common::RadarProductGroup group,
const std::string& product,
std::chrono::system_clock::time_point latestTime)
{
if (autoRefreshEnabled_ &&
context_->radar_product_group() == group &&
@ -816,23 +820,24 @@ void MapWidgetImpl::RadarProductManagerConnect()
std::make_shared<request::NexradFileRequest>();
// File request callback
connect(request.get(),
&request::NexradFileRequest::RequestComplete,
this,
[&](std::shared_ptr<request::NexradFileRequest> request)
{
// Select loaded record
auto record = request->radar_product_record();
connect(
request.get(),
&request::NexradFileRequest::RequestComplete,
this,
[this](std::shared_ptr<request::NexradFileRequest> request)
{
// Select loaded record
auto record = request->radar_product_record();
if (record != nullptr)
{
widget_->SelectRadarProduct(record);
}
});
if (record != nullptr)
{
widget_->SelectRadarProduct(record);
}
});
// Load file
scwx::util::async(
[=]()
[=, this]()
{
if (group == common::RadarProductGroup::Level2)
{
@ -866,7 +871,7 @@ void MapWidgetImpl::InitializeNewRadarProductView(
const std::string& colorPalette)
{
scwx::util::async(
[=]()
[=, this]()
{
auto radarProductView = context_->radar_product_view();
@ -902,13 +907,13 @@ void MapWidgetImpl::RadarProductViewConnect()
radarProductView.get(),
&view::RadarProductView::ColorTableUpdated,
this,
[&]() { widget_->update(); },
[this]() { widget_->update(); },
Qt::QueuedConnection);
connect(
radarProductView.get(),
&view::RadarProductView::SweepComputed,
this,
[=]()
[=, this]()
{
std::shared_ptr<config::RadarSite> radarSite =
radarProductManager_->radar_site();

View file

@ -8,7 +8,10 @@
#include <chrono>
#include <execution>
#pragma warning(push, 0)
#if defined(_MSC_VER)
# pragma warning(push, 0)
#endif
#include <boost/date_time.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/timer/timer.hpp>
@ -18,7 +21,14 @@
#include <glm/gtc/type_ptr.hpp>
#include <imgui.h>
#include <mbgl/util/constants.hpp>
#pragma warning(pop)
#if !defined(_MSC_VER)
# include <date/date.h>
#endif
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
namespace scwx
{
@ -89,16 +99,19 @@ void OverlayLayer::Render(
if (p->sweepTimeNeedsUpdate_ && radarProductView != nullptr)
{
const scwx::util::time_zone* currentZone;
#if defined(_MSC_VER)
currentZone = std::chrono::current_zone();
#else
currentZone = date::current_zone();
#endif
p->sweepTimeString_ = scwx::util::TimeString(
radarProductView->sweep_time(), std::chrono::current_zone(), false);
radarProductView->sweep_time(), currentZone, false);
p->sweepTimeNeedsUpdate_ = false;
}
glm::mat4 projection = glm::ortho(0.0f,
static_cast<float>(params.width),
0.0f,
static_cast<float>(params.height));
// Active Box
p->activeBoxOuter_->SetVisible(settings.isActive_);
p->activeBoxInner_->SetVisible(settings.isActive_);
@ -121,7 +134,7 @@ void OverlayLayer::Render(
nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Text(productName.c_str());
ImGui::TextUnformatted(productName.c_str());
ImGui::End();
}
}
@ -136,7 +149,7 @@ void OverlayLayer::Render(
nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Text(p->sweepTimeString_.c_str());
ImGui::TextUnformatted(p->sweepTimeString_.c_str());
ImGui::End();
}

View file

@ -4,13 +4,19 @@
#include <execution>
#pragma warning(push, 0)
#if defined(_MSC_VER)
# pragma warning(push, 0)
#endif
#include <boost/timer/timer.hpp>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <mbgl/util/constants.hpp>
#pragma warning(pop)
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
namespace scwx
{
@ -31,7 +37,7 @@ LatLongToScreenCoordinate(const QMapLibreGL::Coordinate& coordinate);
class RadarProductLayerImpl
{
public:
explicit RadarProductLayerImpl(std::shared_ptr<MapContext> context) :
explicit RadarProductLayerImpl() :
shaderProgram_(nullptr),
uMVPMatrixLocation_(GL_INVALID_INDEX),
uMapScreenCoordLocation_(GL_INVALID_INDEX),
@ -69,7 +75,7 @@ public:
};
RadarProductLayer::RadarProductLayer(std::shared_ptr<MapContext> context) :
GenericLayer(context), p(std::make_unique<RadarProductLayerImpl>(context))
GenericLayer(context), p(std::make_unique<RadarProductLayerImpl>())
{
}
RadarProductLayer::~RadarProductLayer() = default;
@ -143,11 +149,11 @@ void RadarProductLayer::Initialize()
connect(radarProductView.get(),
&view::RadarProductView::ColorTableUpdated,
this,
[=]() { p->colorTableNeedsUpdate_ = true; });
[this]() { p->colorTableNeedsUpdate_ = true; });
connect(radarProductView.get(),
&view::RadarProductView::SweepComputed,
this,
[=]() { p->sweepNeedsUpdate_ = true; });
[this]() { p->sweepNeedsUpdate_ = true; });
}
void RadarProductLayer::UpdateSweep()

View file

@ -108,7 +108,7 @@ void AlertProxyModelImpl::UpdateAlerts()
using namespace std::chrono;
alertUpdateTimer_.expires_after(15s);
alertUpdateTimer_.async_wait(
[=](const boost::system::error_code& e)
[this](const boost::system::error_code& e)
{
if (e == boost::asio::error::operation_aborted)
{

View file

@ -45,7 +45,7 @@ QVariant ImGuiContextModel::data(const QModelIndex& index, int role) const
}
const int row = index.row();
if (row >= p->contexts_.size() || row < 0)
if (row >= static_cast<int>(p->contexts_.size()) || row < 0)
{
return {};
}

View file

@ -47,7 +47,7 @@ RadarProductModelImpl::RadarProductModelImpl(RadarProductModel* self) :
&manager::RadarProductManagerNotifier::Instance(),
&manager::RadarProductManagerNotifier::RadarProductManagerCreated,
this,
[=](const std::string& radarSite)
[this](const std::string& radarSite)
{
logger_->debug("Adding radar site: {}", radarSite);
@ -67,9 +67,9 @@ RadarProductModelImpl::RadarProductModelImpl(RadarProductModel* self) :
manager::RadarProductManager::Instance(radarSite).get(),
&manager::RadarProductManager::NewDataAvailable,
this,
[=](common::RadarProductGroup group,
const std::string& product,
std::chrono::system_clock::time_point latestTime)
[=, this](common::RadarProductGroup group,
const std::string& product,
std::chrono::system_clock::time_point latestTime)
{
const QString groupName {QString::fromStdString(
common::GetRadarProductGroupName(group))};

View file

@ -5,8 +5,6 @@
#include <scwx/common/geographic.hpp>
#include <scwx/util/logger.hpp>
#include <format>
namespace scwx
{
namespace qt

View file

@ -81,7 +81,7 @@ int TreeItem::column_count() const
QVariant TreeItem::data(int column) const
{
if (0 <= column && column < p->itemData_.size())
if (0 <= column && column < static_cast<int>(p->itemData_.size()))
{
return p->itemData_[column];
}
@ -152,7 +152,7 @@ bool TreeItem::InsertChildren(int position, int count, int columns)
bool TreeItem::SetData(int column, const QVariant& value)
{
if (column < 0 || column >= p->itemData_.size())
if (column < 0 || column >= static_cast<int>(p->itemData_.size()))
{
return false;
}

View file

@ -2,10 +2,10 @@
#include <scwx/qt/settings/settings_variable.hpp>
#include <scwx/qt/util/color.hpp>
#include <format>
#include <regex>
#include <boost/gil.hpp>
#include <fmt/format.h>
namespace scwx
{
@ -16,6 +16,27 @@ namespace settings
static const std::string logPrefix_ = "scwx::qt::settings::palette_settings";
static const std::array<std::string, 17> kPaletteKeys_ {
// Level 2 / Common Products
"BR",
"BV",
"SW",
"CC",
"ZDR",
"PHI2",
// Level 3 Products
"DOD",
"DSD",
"ET",
"STP",
"OHP",
"STPIN",
"OHPIN",
"PHI3",
"SRV",
"VIL",
"???"};
static const std::unordered_map<std::string, std::string> kDefaultPalettes_ {
// Level 2 / Common Products
{"BR", ":/res/palettes/wct/DR.pal"},
@ -56,10 +77,9 @@ class PaletteSettingsImpl
public:
explicit PaletteSettingsImpl()
{
for (const auto& palette : kDefaultPalettes_)
for (const auto& name : kPaletteKeys_)
{
const std::string& name = palette.first;
const std::string& defaultValue = palette.second;
const std::string& defaultValue = kDefaultPalettes_.at(name);
auto result =
palette_.emplace(name, SettingsVariable<std::string> {name});
@ -74,8 +94,8 @@ public:
for (auto& alert : kAlertColors_)
{
std::string phenomenonCode = awips::GetPhenomenonCode(alert.first);
std::string activeName = std::format("{}-active", phenomenonCode);
std::string inactiveName = std::format("{}-inactive", phenomenonCode);
std::string activeName = fmt::format("{}-active", phenomenonCode);
std::string inactiveName = fmt::format("{}-inactive", phenomenonCode);
auto activeResult = activeAlertColor_.emplace(
alert.first, SettingsVariable<std::string> {activeName});

View file

@ -164,7 +164,7 @@ void SettingsInterface<T>::SetEditWidget(QWidget* widget)
{
// Error value
value.push_back(
std::numeric_limits<T::value_type>::min());
std::numeric_limits<typename T::value_type>::min());
}
}
}

View file

@ -1,8 +1,7 @@
#include <scwx/qt/types/text_event_key.hpp>
#include <format>
#include <boost/container_hash/hash.hpp>
#include <fmt/format.h>
namespace scwx
{
@ -15,7 +14,7 @@ static const std::string logPrefix_ = "scwx::qt::types::text_event_key";
std::string TextEventKey::ToFullString() const
{
return std::format("{} {} {} {:04}",
return fmt::format("{} {} {} {:04}",
officeId_,
awips::GetPhenomenonText(phenomenon_),
awips::GetSignificanceText(significance_),
@ -24,7 +23,7 @@ std::string TextEventKey::ToFullString() const
std::string TextEventKey::ToString() const
{
return std::format("{}.{}.{}.{:04}",
return fmt::format("{}.{}.{}.{:04}",
officeId_,
awips::GetPhenomenonCode(phenomenon_),
awips::GetSignificanceCode(significance_),

View file

@ -74,7 +74,7 @@ void AlertDialogImpl::ConnectSignals()
textEventManager_.get(),
&manager::TextEventManager::AlertUpdated,
this,
[=](const types::TextEventKey& key)
[this](const types::TextEventKey& key)
{
if (key == key_)
{
@ -85,7 +85,7 @@ void AlertDialogImpl::ConnectSignals()
connect(goButton_,
&QPushButton::clicked,
this,
[=]()
[this]()
{
emit self_->MoveMap(centroid_.latitude_, centroid_.longitude_);
self_->close();

View file

@ -127,48 +127,48 @@ void AlertDockWidgetImpl::ConnectSignals()
alertModel_.get(),
&model::AlertModel::HandleAlert,
Qt::QueuedConnection);
connect(self_->ui->alertView->selectionModel(),
&QItemSelectionModel::selectionChanged,
this,
[=](const QItemSelection& selected, const QItemSelection& deselected)
{
if (selected.size() == 0 && deselected.size() == 0)
{
// Items which stay selected but change their index are not
// included in selected and deselected. Thus, this signal might
// be emitted with both selected and deselected empty, if only
// the indices of selected items change.
return;
}
connect(
self_->ui->alertView->selectionModel(),
&QItemSelectionModel::selectionChanged,
this,
[this](const QItemSelection& selected, const QItemSelection& deselected)
{
if (selected.size() == 0 && deselected.size() == 0)
{
// Items which stay selected but change their index are not
// included in selected and deselected. Thus, this signal might
// be emitted with both selected and deselected empty, if only
// the indices of selected items change.
return;
}
bool itemSelected = selected.size() > 0;
bool itemHasCoordinates = false;
bool itemSelected = selected.size() > 0;
bool itemHasCoordinates = false;
if (itemSelected)
{
QModelIndex selectedIndex =
proxyModel_->mapToSource(selected[0].indexes()[0]);
selectedAlertKey_ = alertModel_->key(selectedIndex);
selectedAlertCentroid_ =
alertModel_->centroid(selectedAlertKey_);
itemHasCoordinates =
selectedAlertCentroid_ != common::Coordinate {};
}
else
{
selectedAlertKey_ = {};
selectedAlertCentroid_ = {};
}
if (itemSelected)
{
QModelIndex selectedIndex =
proxyModel_->mapToSource(selected[0].indexes()[0]);
selectedAlertKey_ = alertModel_->key(selectedIndex);
selectedAlertCentroid_ = alertModel_->centroid(selectedAlertKey_);
itemHasCoordinates =
selectedAlertCentroid_ != common::Coordinate {};
}
else
{
selectedAlertKey_ = {};
selectedAlertCentroid_ = {};
}
self_->ui->alertViewButton->setEnabled(itemSelected);
self_->ui->alertGoButton->setEnabled(itemHasCoordinates);
self_->ui->alertViewButton->setEnabled(itemSelected);
self_->ui->alertGoButton->setEnabled(itemHasCoordinates);
logger_->debug("Selected: {}", selectedAlertKey_.ToString());
});
logger_->debug("Selected: {}", selectedAlertKey_.ToString());
});
connect(self_->ui->alertViewButton,
&QPushButton::clicked,
this,
[=]()
[this]()
{
// View alert
alertDialog_->SelectAlert(selectedAlertKey_);
@ -177,7 +177,7 @@ void AlertDockWidgetImpl::ConnectSignals()
connect(self_->ui->alertGoButton,
&QPushButton::clicked,
this,
[=]()
[this]()
{
emit self_->MoveMap(selectedAlertCentroid_.latitude_,
selectedAlertCentroid_.longitude_);

View file

@ -45,7 +45,7 @@ ImGuiDebugDialog::ImGuiDebugDialog(QWidget* parent) :
connect(
ui->contextComboBox,
&QComboBox::currentIndexChanged,
[=](int row)
[this](int row)
{
auto& contextModel = model::ImGuiContextModel::Instance();
auto index = contextModel.index(row, 0);

View file

@ -6,6 +6,7 @@
#include <imgui.h>
#include <backends/imgui_impl_opengl3.h>
#include <backends/imgui_impl_qt.hpp>
#include <fmt/format.h>
namespace scwx
{
@ -23,7 +24,7 @@ public:
{
// Create ImGui Context
static size_t currentIndex_ {0u};
contextName_ = std::format("ImGui Debug {}", ++currentIndex_);
contextName_ = fmt::format("ImGui Debug {}", ++currentIndex_);
context_ =
model::ImGuiContextModel::Instance().CreateContext(contextName_);
currentContext_ = context_;

View file

@ -39,7 +39,7 @@ public:
QObject::connect(toolButton,
&QToolButton::clicked,
this,
[=]() { SelectProduct(product); });
[=, this]() { SelectProduct(product); });
}
}
~Level2ProductsWidgetImpl() = default;

View file

@ -179,7 +179,7 @@ void Level2SettingsWidget::UpdateSettings(map::MapWidget* activeMap)
connect(toolButton,
&QToolButton::clicked,
this,
[=]() { p->SelectElevation(elevationCut); });
[=, this]() { p->SelectElevation(elevationCut); });
}
p->elevationCuts_ = elevationCuts;

View file

@ -48,7 +48,7 @@ public:
QObject::connect(toolButton,
&QToolButton::clicked,
this,
[=]() { SelectProductCategory(category); });
[=, this]() { SelectProductCategory(category); });
QMenu* categoryMenu = new QMenu();
toolButton->setMenu(categoryMenu);
@ -79,7 +79,7 @@ public:
action,
&QAction::triggered,
this,
[=]()
[=, this]()
{
std::shared_lock lock {awipsProductMutex_};
std::string awipsProductName {awipsProductMap_.at(action)};

View file

@ -72,47 +72,47 @@ RadarSiteDialog::RadarSiteDialog(QWidget* parent) :
connect(ui->radarSiteView,
&QTreeView::doubleClicked,
this,
[=]() { emit accept(); });
connect(ui->radarSiteView->selectionModel(),
&QItemSelectionModel::selectionChanged,
this,
[=](const QItemSelection& selected, const QItemSelection& deselected)
{
if (selected.size() == 0 && deselected.size() == 0)
{
// Items which stay selected but change their index are not
// included in selected and deselected. Thus, this signal might
// be emitted with both selected and deselected empty, if only
// the indices of selected items change.
return;
}
[this]() { emit accept(); });
connect(
ui->radarSiteView->selectionModel(),
&QItemSelectionModel::selectionChanged,
this,
[this](const QItemSelection& selected, const QItemSelection& deselected)
{
if (selected.size() == 0 && deselected.size() == 0)
{
// Items which stay selected but change their index are not
// included in selected and deselected. Thus, this signal might
// be emitted with both selected and deselected empty, if only
// the indices of selected items change.
return;
}
ui->buttonBox->button(QDialogButtonBox::Ok)
->setEnabled(selected.size() > 0);
ui->buttonBox->button(QDialogButtonBox::Ok)
->setEnabled(selected.size() > 0);
if (selected.size() > 0)
{
QModelIndex selectedIndex =
p->proxyModel_->mapToSource(selected[0].indexes()[0]);
QVariant variantData = p->radarSiteModel_->data(selectedIndex);
if (variantData.typeId() == QMetaType::QString)
{
p->selectedRadarSite_ =
variantData.toString().toStdString();
}
else
{
logger_->warn("Unexpected selection data type");
p->selectedRadarSite_ = "?";
}
}
else
{
p->selectedRadarSite_ = "?";
}
if (selected.size() > 0)
{
QModelIndex selectedIndex =
p->proxyModel_->mapToSource(selected[0].indexes()[0]);
QVariant variantData = p->radarSiteModel_->data(selectedIndex);
if (variantData.typeId() == QMetaType::QString)
{
p->selectedRadarSite_ = variantData.toString().toStdString();
}
else
{
logger_->warn("Unexpected selection data type");
p->selectedRadarSite_ = std::string {"?"};
}
}
else
{
p->selectedRadarSite_ = std::string {"?"};
}
logger_->debug("Selected: {}", p->selectedRadarSite_);
});
logger_->debug("Selected: {}", p->selectedRadarSite_);
});
}
RadarSiteDialog::~RadarSiteDialog()

View file

@ -12,8 +12,7 @@
#include <scwx/util/logger.hpp>
#include <scwx/util/threads.hpp>
#include <format>
#include <fmt/format.h>
#include <QColorDialog>
#include <QFileDialog>
#include <QToolButton>
@ -236,6 +235,9 @@ void SettingsDialogImpl::ConnectSignals()
case QDialogButtonBox::ButtonRole::ResetRole: // Restore Defaults
ResetToDefault();
break;
default:
break;
}
});
}
@ -515,11 +517,13 @@ void SettingsDialogImpl::SetupPalettesAlertsTab()
QObject::connect(activeButton,
&QAbstractButton::clicked,
self_,
[=]() { ShowColorDialog(activeEdit, activeFrame); });
[=, this]()
{ ShowColorDialog(activeEdit, activeFrame); });
QObject::connect(inactiveButton,
&QAbstractButton::clicked,
self_,
[=]() { ShowColorDialog(inactiveEdit, inactiveFrame); });
[=, this]()
{ ShowColorDialog(inactiveEdit, inactiveFrame); });
}
}
@ -622,7 +626,7 @@ void SettingsDialogImpl::SetBackgroundColor(const std::string& value,
QFrame* frame)
{
frame->setStyleSheet(
QString::fromStdString(std::format("background-color: {}", value)));
QString::fromStdString(fmt::format("background-color: {}", value)));
}
void SettingsDialogImpl::UpdateRadarDialogLocation(const std::string& id)
@ -676,7 +680,7 @@ void SettingsDialogImpl::ResetToDefault()
std::string SettingsDialogImpl::RadarSiteLabel(
std::shared_ptr<config::RadarSite>& radarSite)
{
return std::format("{} ({})", radarSite->id(), radarSite->location_name());
return fmt::format("{} ({})", radarSite->id(), radarSite->location_name());
}
} // namespace ui

View file

@ -1,6 +1,6 @@
#include <scwx/qt/util/color.hpp>
#include <format>
#include <fmt/format.h>
#include <QColor>
namespace scwx
@ -16,7 +16,7 @@ static const std::string logPrefix_ = "scwx::qt::util::color";
std::string ToArgbString(const boost::gil::rgba8_pixel_t& color)
{
return std::format(
return fmt::format(
"#{:02x}{:02x}{:02x}{:02x}", color[3], color[0], color[1], color[2]);
}

View file

@ -13,6 +13,7 @@
#include <unordered_map>
#include <boost/timer/timer.hpp>
#include <fmt/format.h>
#include <imgui.h>
#include <QFile>
#include <QFileInfo>
@ -22,7 +23,15 @@
#include FT_SFNT_NAMES_H
#include FT_TRUETYPE_IDS_H
#pragma warning(push, 0)
#if defined(_MSC_VER)
# pragma warning(push, 0)
#endif
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wpedantic"
#endif
// #include <freetype-gl.h> (exclude opengl.h)
#include <platform.h>
#include <vec234.h>
@ -30,7 +39,14 @@
#include <texture-atlas.h>
#include <texture-font.h>
#include <ftgl-utils.h>
#pragma warning(pop)
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
#ifdef WIN32
# include <WinSock2.h>
@ -267,9 +283,9 @@ void FontImpl::CreateImGuiFont(QFile& fontFile,
// Assign name to font
strncpy(fontConfig.Name,
std::format("{}:{}", fileInfo.fileName().toStdString(), fontSize)
fmt::format("{}:{}", fileInfo.fileName().toStdString(), fontSize)
.c_str(),
sizeof(fontConfig.Name));
sizeof(fontConfig.Name) - 1);
fontConfig.Name[sizeof(fontConfig.Name) - 1] = 0;
// Add font to atlas

View file

@ -1,5 +1,6 @@
#pragma once
#include <memory>
#include <streambuf>
namespace scwx

View file

@ -1,6 +1,7 @@
#pragma once
#include <istream>
#include <memory>
namespace scwx
{

View file

@ -5,13 +5,19 @@
#include <shared_mutex>
#include <unordered_map>
#pragma warning(push, 0)
#pragma warning(disable : 4714)
#if defined(_MSC_VER)
# pragma warning(push, 0)
# pragma warning(disable : 4714)
#endif
#include <boost/gil/extension/io/png.hpp>
#include <boost/iostreams/stream.hpp>
#include <stb_rect_pack.h>
#include <QFile>
#pragma warning(pop)
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
namespace scwx
{

View file

@ -292,7 +292,7 @@ void Level2ProductViewImpl::SetProduct(common::Level2Product product)
void Level2ProductView::Update()
{
util::async([=]() { ComputeSweep(); });
util::async([this]() { ComputeSweep(); });
}
void Level2ProductView::UpdateColorTable()

View file

@ -163,7 +163,7 @@ void Level3ProductView::LoadColorTable(
void Level3ProductView::Update()
{
util::async([=]() { ComputeSweep(); });
util::async([this]() { ComputeSweep(); });
}
void Level3ProductView::UpdateColorTable()

5
setup-debug.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/bash
./tools/setup-common.sh
mkdir -p build-debug
cd build-debug
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CONFIGURATION_TYPES=Debug -DCMAKE_PREFIX_PATH=/opt/Qt/6.4.2/gcc_64 ..

5
setup-release.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/bash
./tools/setup-common.sh
mkdir -p build-release
cd build-release
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CONFIGURATION_TYPES=Release -DCMAKE_PREFIX_PATH=/opt/Qt/6.4.2/gcc_64 ..

5
tools/setup-common.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/bash
pip install "conan<2.0"
pip install geopandas
pip install GitPython
conan profile new default --detect

View file

@ -1,6 +1,7 @@
#pragma once
#include <array>
#include <cstring>
#include <execution>
#include <istream>
#include <map>
@ -24,18 +25,18 @@ class Message
protected:
explicit Message();
Message(const Message&) = delete;
Message(const Message&) = delete;
Message& operator=(const Message&) = delete;
Message(Message&&) noexcept;
Message& operator=(Message&&) noexcept;
virtual bool ValidateMessage(std::istream& is, size_t bytesRead) const;
virtual bool ValidateMessage(std::istream& is, std::size_t bytesRead) const;
public:
virtual ~Message();
virtual size_t data_size() const = 0;
virtual std::size_t data_size() const = 0;
virtual bool Parse(std::istream& is) = 0;
@ -55,11 +56,16 @@ public:
static float SwapFloat(float f)
{
return ntohf(*reinterpret_cast<uint32_t*>(&f));
std::uint32_t temp;
std::memcpy(&temp, &f, sizeof(std::uint32_t));
temp = ntohl(temp);
std::memcpy(&f, &temp, sizeof(float));
return f;
}
template<size_t _Size>
static void SwapArray(std::array<float, _Size>& arr, size_t size = _Size)
template<std::size_t _Size>
static void SwapArray(std::array<float, _Size>& arr,
std::size_t size = _Size)
{
std::transform(std::execution::par_unseq,
arr.begin(),
@ -68,34 +74,37 @@ public:
[](float f) { return SwapFloat(f); });
}
template<size_t _Size>
static void SwapArray(std::array<int16_t, _Size>& arr, size_t size = _Size)
template<std::size_t _Size>
static void SwapArray(std::array<std::int16_t, _Size>& arr,
std::size_t size = _Size)
{
std::transform(std::execution::par_unseq,
arr.begin(),
arr.begin() + size,
arr.begin(),
[](int16_t u) { return ntohs(u); });
[](std::int16_t u) { return ntohs(u); });
}
template<size_t _Size>
static void SwapArray(std::array<uint16_t, _Size>& arr, size_t size = _Size)
template<std::size_t _Size>
static void SwapArray(std::array<std::uint16_t, _Size>& arr,
std::size_t size = _Size)
{
std::transform(std::execution::par_unseq,
arr.begin(),
arr.begin() + size,
arr.begin(),
[](uint16_t u) { return ntohs(u); });
[](std::uint16_t u) { return ntohs(u); });
}
template<size_t _Size>
static void SwapArray(std::array<uint32_t, _Size>& arr, size_t size = _Size)
template<std::size_t _Size>
static void SwapArray(std::array<std::uint32_t, _Size>& arr,
std::size_t size = _Size)
{
std::transform(std::execution::par_unseq,
arr.begin(),
arr.begin() + size,
arr.begin(),
[](uint32_t u) { return ntohl(u); });
[](std::uint32_t u) { return ntohl(u); });
}
template<typename T>
@ -107,13 +116,13 @@ public:
[](auto& p) { p.second = SwapFloat(p.second); });
}
static void SwapVector(std::vector<uint16_t>& v)
static void SwapVector(std::vector<std::uint16_t>& v)
{
std::transform(std::execution::par_unseq,
v.begin(),
v.end(),
v.begin(),
[](uint16_t u) { return ntohs(u); });
[](std::uint16_t u) { return ntohs(u); });
}
private:

View file

@ -3,6 +3,7 @@
#include <chrono>
#include <filesystem>
#include <string>
#include <vector>
namespace scwx
{

View file

@ -5,6 +5,7 @@
#include <chrono>
#include <memory>
#include <string>
#include <vector>
namespace scwx
{

View file

@ -3,9 +3,15 @@
#include <memory>
#include <string>
#pragma warning(push, 0)
#if defined(_MSC_VER)
# pragma warning(push, 0)
#endif
#include <spdlog/logger.h>
#pragma warning(pop)
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
namespace scwx
{

View file

@ -2,16 +2,26 @@
#include <chrono>
#if !defined(_MSC_VER)
# include <date/tz.h>
#endif
namespace scwx
{
namespace util
{
#if defined(_MSC_VER)
typedef std::chrono::time_zone time_zone;
#else
typedef date::time_zone time_zone;
#endif
std::chrono::system_clock::time_point TimePoint(uint32_t modifiedJulianDate,
uint32_t milliseconds);
std::string TimeString(std::chrono::system_clock::time_point time,
const std::chrono::time_zone* timeZone = nullptr,
const time_zone* timeZone = nullptr,
bool epochValid = true);
} // namespace util

View file

@ -8,6 +8,10 @@
#include <sstream>
#if !defined(_MSC_VER)
# include <date/date.h>
#endif
namespace scwx
{
namespace awips
@ -98,6 +102,10 @@ bool CodedTimeMotionLocation::Parse(const StringRange& lines,
{
using namespace std::chrono;
#if !defined(_MSC_VER)
using namespace date;
#endif
static const std::string timeFormat {"%H%MZ"};
std::istringstream in {time};
@ -106,12 +114,12 @@ bool CodedTimeMotionLocation::Parse(const StringRange& lines,
if (time.size() == 5 && !in.fail())
{
p->time_ = hh_mm_ss {tp};
p->time_ = std::chrono::hh_mm_ss {tp};
}
else
{
logger_->warn("Invalid time: \"{}\"", time);
p->time_ = hh_mm_ss<minutes> {};
p->time_ = std::chrono::hh_mm_ss<minutes> {};
dataValid = false;
}
}

View file

@ -12,6 +12,10 @@
#include <boost/bimap.hpp>
#include <boost/bimap/unordered_set_of.hpp>
#if !defined(_MSC_VER)
# include <date/date.h>
#endif
namespace scwx
{
namespace awips
@ -87,7 +91,7 @@ public:
PVtec::PVtec() : p(std::make_unique<PVtecImpl>()) {}
PVtec::~PVtec() = default;
PVtec::PVtec(PVtec&&) noexcept = default;
PVtec::PVtec(PVtec&&) noexcept = default;
PVtec& PVtec::operator=(PVtec&&) noexcept = default;
PVtec::ProductType PVtec::fixed_identifier() const
@ -134,6 +138,10 @@ bool PVtec::Parse(const std::string& s)
{
using namespace std::chrono;
#if !defined(_MSC_VER)
using namespace date;
#endif
// P-VTEC takes the form:
// /k.aaa.cccc.pp.s.####.yymmddThhnnZ-yymmddThhnnZ/
// 012345678901234567890123456789012345678901234567
@ -187,8 +195,8 @@ bool PVtec::Parse(const std::string& s)
std::istringstream ssEventBegin {sEventBegin};
std::istringstream ssEventEnd {sEventEnd};
sys_time<minutes> eventBegin;
sys_time<minutes> eventEnd;
std::chrono::sys_time<minutes> eventBegin;
std::chrono::sys_time<minutes> eventEnd;
ssEventBegin >> parse(dateTimeFormat, eventBegin);
ssEventEnd >> parse(dateTimeFormat, eventEnd);

View file

@ -84,7 +84,7 @@ std::vector<std::string> Ugc::fips_ids() const
{
for (auto& id : fipsIdList.second)
{
fipsIds.push_back(std::format("{}{}{:03}",
fipsIds.push_back(fmt::format("{}{}{:03}",
fipsIdList.first,
ugcFormatMap_.left.at(p->format_),
id));

View file

@ -1,9 +1,10 @@
#include <scwx/common/geographic.hpp>
#include <scwx/common/characters.hpp>
#include <format>
#include <numbers>
#include <fmt/format.h>
namespace scwx
{
namespace common
@ -95,7 +96,7 @@ static std::string GetDegreeString(double degrees,
{
case DegreeStringType::Decimal:
degreeString =
std::format("{:.6f}{}{}", degrees, Unicode::kDegree, suffix);
fmt::format("{:.6f}{}{}", degrees, Unicode::kDegree, suffix);
break;
case DegreeStringType::DegreesMinutesSeconds:
{
@ -103,7 +104,7 @@ static std::string GetDegreeString(double degrees,
degrees = (degrees - dd) * 60.0;
uint32_t mm = static_cast<uint32_t>(degrees);
double ss = (degrees - mm) * 60.0;
degreeString = std::format(
degreeString = fmt::format(
"{}{} {}' {:.2f}\"{}", dd, Unicode::kDegree, mm, ss, suffix);
break;
}

View file

@ -3,11 +3,21 @@
#include <scwx/network/dir_list.hpp>
#include <scwx/util/logger.hpp>
#pragma warning(push, 0)
#if defined(_MSC_VER)
# pragma warning(push, 0)
#endif
#include <boost/algorithm/string/trim.hpp>
#include <cpr/cpr.h>
#include <libxml/HTMLparser.h>
#pragma warning(pop)
#if !defined(_MSC_VER)
# include <date/date.h>
#endif
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
namespace scwx
{
@ -53,6 +63,12 @@ struct DirListSAXData
std::vector<DirListRecord> records_;
};
// Unspecified fields are initialized to zero, ignore warning
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
static htmlSAXHandler saxHandler_ //
{.startElement = &DirListSAXHandler::StartElement,
.endElement = &DirListSAXHandler::EndElement,
@ -61,6 +77,10 @@ static htmlSAXHandler saxHandler_ //
.error = &DirListSAXHandler::Error,
.fatalError = &DirListSAXHandler::Critical};
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
std::vector<DirListRecord> DirList(const std::string& baseUrl)
{
using namespace std::chrono;
@ -171,13 +191,16 @@ void DirListSAXHandler::Characters(void* userData, const xmlChar* ch, int len)
{
using namespace std::chrono;
#if !defined(_MSC_VER)
using namespace date;
#endif
// Date time format: yyyy-mm-dd hh:mm
static const std::string kDateTimeFormat {"%Y-%m-%d %H:%M"};
static constexpr size_t kDateTimeSize {16u};
// Attempt to parse the date time
std::istringstream ssCharacters {characters};
sys_time<minutes> mtime;
std::istringstream ssCharacters {characters};
std::chrono::sys_time<minutes> mtime;
ssCharacters >> parse(kDateTimeFormat, mtime);
if (!ssCharacters.fail())

View file

@ -5,6 +5,10 @@
#include <fmt/chrono.h>
#include <fmt/format.h>
#if !defined(_MSC_VER)
# include <date/date.h>
#endif
namespace scwx
{
namespace provider
@ -67,12 +71,16 @@ AwsLevel2DataProvider::GetTimePointFromKey(const std::string& key)
(lastSeparator == std::string::npos) ? 0 : lastSeparator + 5;
// Filename format is GGGGYYYYMMDD_TTTTTT(_V##).gz
static constexpr size_t formatSize = std::string("YYYYMMDD_TTTTTT").size();
static const size_t formatSize = std::string("YYYYMMDD_TTTTTT").size();
if (key.size() >= offset + formatSize)
{
using namespace std::chrono;
#if !defined(_MSC_VER)
using namespace date;
#endif
static const std::string timeFormat {"%Y%m%d_%H%M%S"};
std::string timeStr {key.substr(offset, formatSize)};

View file

@ -9,6 +9,10 @@
#include <fmt/chrono.h>
#include <fmt/format.h>
#if !defined(_MSC_VER)
# include <date/date.h>
#endif
namespace scwx
{
namespace provider
@ -92,13 +96,16 @@ AwsLevel3DataProvider::GetTimePointFromKey(const std::string& key)
constexpr size_t offset = 8;
// Filename format is GGG_PPP_YYYY_MM_DD_HH_MM_SS
static constexpr size_t formatSize =
std::string("YYYY_MM_DD_HH_MM_SS").size();
static const size_t formatSize = std::string("YYYY_MM_DD_HH_MM_SS").size();
if (key.size() >= offset + formatSize)
{
using namespace std::chrono;
#if !defined(_MSC_VER)
using namespace date;
#endif
static const std::string timeFormat {"%Y_%m_%d_%H_%M_%S"};
std::string timeStr {key.substr(offset, formatSize)};
@ -150,13 +157,15 @@ void AwsLevel3DataProvider::Impl::ListProducts()
logger_->debug("ListProducts()");
static const std::string delimiter {"_"};
// Prefix format: GGG_
const std::string prefix = fmt::format("{0}_", siteId_);
Aws::S3::Model::ListObjectsV2Request request;
request.SetBucket(bucketName_);
request.SetPrefix(prefix);
request.SetDelimiter("_");
request.SetDelimiter(delimiter);
auto outcome = self_->client()->ListObjectsV2(request);

View file

@ -6,11 +6,21 @@
#include <regex>
#include <shared_mutex>
#pragma warning(push, 0)
#if defined(_MSC_VER)
# pragma warning(push, 0)
#endif
#define LIBXML_HTML_ENABLED
#include <cpr/cpr.h>
#include <libxml/HTMLparser.h>
#pragma warning(pop)
#if !defined(_MSC_VER)
# include <date/date.h>
#endif
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
namespace scwx
{
@ -63,6 +73,10 @@ WarningsProvider::ListFiles(std::chrono::system_clock::time_point newerThan)
{
using namespace std::chrono;
#if !defined(_MSC_VER)
using namespace date;
#endif
static const std::regex reWarningsFilename {
"warnings_[0-9]{8}_[0-9]{2}.txt"};
static const std::string dateTimeFormat {"warnings_%Y%m%d_%H.txt"};
@ -98,8 +112,8 @@ WarningsProvider::ListFiles(std::chrono::system_clock::time_point newerThan)
for (auto& record : warningRecords)
{
// Determine start time
sys_time<hours> startTime;
std::istringstream ssFilename {record.filename_};
std::chrono::sys_time<hours> startTime;
std::istringstream ssFilename {record.filename_};
ssFilename >> parse(dateTimeFormat, startTime);

View file

@ -1,6 +1,7 @@
#include <scwx/util/float.hpp>
#include <cmath>
#include <cstring>
#ifdef WIN32
# include <WinSock2.h>
@ -13,21 +14,21 @@ namespace scwx
namespace util
{
float DecodeFloat16(uint16_t hex)
float DecodeFloat16(std::uint16_t hex)
{
static constexpr uint16_t S_MASK = 0x8000;
static constexpr uint16_t S_LSB = 0;
static constexpr uint16_t S_SHIFT = 15 - S_LSB;
static constexpr uint16_t E_MASK = 0x7a00;
static constexpr uint16_t E_LSB = 5;
static constexpr uint16_t E_SHIFT = 15 - E_LSB;
static constexpr uint16_t F_MASK = 0x03ff;
static constexpr uint16_t F_LSB = 15;
static constexpr uint16_t F_SHIFT = 15 - F_LSB;
static constexpr std::uint16_t S_MASK = 0x8000;
static constexpr std::uint16_t S_LSB = 0;
static constexpr std::uint16_t S_SHIFT = 15 - S_LSB;
static constexpr std::uint16_t E_MASK = 0x7a00;
static constexpr std::uint16_t E_LSB = 5;
static constexpr std::uint16_t E_SHIFT = 15 - E_LSB;
static constexpr std::uint16_t F_MASK = 0x03ff;
static constexpr std::uint16_t F_LSB = 15;
static constexpr std::uint16_t F_SHIFT = 15 - F_LSB;
uint16_t sHex = (hex & S_MASK) >> S_SHIFT;
uint16_t eHex = (hex & E_MASK) >> E_SHIFT;
uint16_t fHex = (hex & F_MASK) >> F_SHIFT;
std::uint16_t sHex = (hex & S_MASK) >> S_SHIFT;
std::uint16_t eHex = (hex & E_MASK) >> E_SHIFT;
std::uint16_t fHex = (hex & F_MASK) >> F_SHIFT;
float value;
@ -51,11 +52,14 @@ float DecodeFloat16(uint16_t hex)
return value;
}
float DecodeFloat32(uint16_t msw, uint16_t lsw)
float DecodeFloat32(std::uint16_t msw, std::uint16_t lsw)
{
uint32_t value = msw << 16 | lsw;
std::uint32_t value = msw << 16 | lsw;
float floatValue;
return reinterpret_cast<float&>(value);
std::memcpy(&floatValue, &value, sizeof(float));
return floatValue;
}
} // namespace util

View file

@ -22,10 +22,15 @@ std::chrono::system_clock::time_point TimePoint(uint32_t modifiedJulianDate,
}
std::string TimeString(std::chrono::system_clock::time_point time,
const std::chrono::time_zone* timeZone,
const time_zone* timeZone,
bool epochValid)
{
using namespace std::chrono;
#if !defined(_MSC_VER)
using namespace date;
#endif
auto timeInSeconds = time_point_cast<seconds>(time);
std::ostringstream os;

View file

@ -33,8 +33,12 @@ vectorbuf::pos_type vectorbuf::seekoff(std::streamoff off,
off_type newOffset;
switch (way)
{
case std::ios_base::beg: newOffset = 0; break;
case std::ios_base::end: newOffset = seekDist; break;
case std::ios_base::beg:
newOffset = 0;
break;
case std::ios_base::end:
newOffset = seekDist;
break;
case std::ios_base::cur:
{
constexpr auto BOTH = std::ios_base::in | std::ios_base::out;
@ -54,9 +58,11 @@ vectorbuf::pos_type vectorbuf::seekoff(std::streamoff off,
break;
}
}
return pos_type(off_type(-1));
}
default: return pos_type(off_type(-1));
default:
return pos_type(off_type(-1));
}
if (static_cast<unsigned long long>(off) + newOffset >
@ -80,7 +86,9 @@ vectorbuf::pos_type vectorbuf::seekoff(std::streamoff off,
if ((which & std::ios_base::out) && pptrOld)
{
setp(seekLow, next, epptr());
setp(seekLow, epptr());
// If offset is > 4 GB, this won't properly position the put pointer
pbump(static_cast<int>(off));
}
return pos_type(off);

View file

@ -8,10 +8,19 @@
#include <fstream>
#include <sstream>
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-copy"
#endif
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/filter/bzip2.hpp>
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
namespace scwx
{
namespace wsr88d
@ -24,35 +33,35 @@ class Ar2vFileImpl
{
public:
explicit Ar2vFileImpl() :
tapeFilename_(),
extensionNumber_(),
tapeFilename_ {},
extensionNumber_ {},
julianDate_ {0},
milliseconds_ {0},
icao_(),
rawRecords_(),
icao_ {},
vcpData_ {nullptr},
radarData_ {},
index_ {} {};
index_ {},
rawRecords_ {} {};
~Ar2vFileImpl() = default;
size_t DecompressLDMRecords(std::istream& is);
void HandleMessage(std::shared_ptr<rda::Level2Message>& message);
void IndexFile();
void ParseLDMRecords();
void ParseLDMRecord(std::istream& is);
void ProcessRadarData(std::shared_ptr<rda::DigitalRadarData> message);
std::size_t DecompressLDMRecords(std::istream& is);
void HandleMessage(std::shared_ptr<rda::Level2Message>& message);
void IndexFile();
void ParseLDMRecords();
void ParseLDMRecord(std::istream& is);
void ProcessRadarData(std::shared_ptr<rda::DigitalRadarData> message);
std::string tapeFilename_;
std::string extensionNumber_;
uint32_t julianDate_;
uint32_t milliseconds_;
std::string icao_;
std::string tapeFilename_;
std::string extensionNumber_;
std::uint32_t julianDate_;
std::uint32_t milliseconds_;
std::string icao_;
std::shared_ptr<rda::VolumeCoveragePatternData> vcpData_;
std::map<uint16_t, std::shared_ptr<rda::ElevationScan>> radarData_;
std::shared_ptr<rda::VolumeCoveragePatternData> vcpData_;
std::map<std::uint16_t, std::shared_ptr<rda::ElevationScan>> radarData_;
std::map<rda::DataBlockType,
std::map<uint16_t, std::shared_ptr<rda::ElevationScan>>>
std::map<std::uint16_t, std::shared_ptr<rda::ElevationScan>>>
index_;
std::list<std::stringstream> rawRecords_;
@ -61,7 +70,7 @@ public:
Ar2vFile::Ar2vFile() : p(std::make_unique<Ar2vFileImpl>()) {}
Ar2vFile::~Ar2vFile() = default;
Ar2vFile::Ar2vFile(Ar2vFile&&) noexcept = default;
Ar2vFile::Ar2vFile(Ar2vFile&&) noexcept = default;
Ar2vFile& Ar2vFile::operator=(Ar2vFile&&) noexcept = default;
uint32_t Ar2vFile::julian_date() const

View file

@ -4,13 +4,29 @@
#include <scwx/util/logger.hpp>
#include <fstream>
#include <sstream>
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable : 4706)
#endif
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-copy"
#endif
#pragma warning(push)
#pragma warning(disable : 4706)
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#pragma warning(pop)
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
namespace scwx
{
@ -39,7 +55,7 @@ public:
Level3File::Level3File() : p(std::make_unique<Level3FileImpl>()) {}
Level3File::~Level3File() = default;
Level3File::Level3File(Level3File&&) noexcept = default;
Level3File::Level3File(Level3File&&) noexcept = default;
Level3File& Level3File::operator=(Level3File&&) noexcept = default;
std::shared_ptr<awips::WmoHeader> Level3File::wmo_header() const

View file

@ -6,12 +6,27 @@
#include <fstream>
#include <sstream>
#pragma warning(push)
#pragma warning(disable : 4706)
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable : 4706)
#endif
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-copy"
#endif
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#pragma warning(pop)
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
namespace scwx
{

View file

@ -43,7 +43,7 @@ DataBlock::DataBlock(const std::string& dataBlockType,
}
DataBlock::~DataBlock() = default;
DataBlock::DataBlock(DataBlock&&) noexcept = default;
DataBlock::DataBlock(DataBlock&&) noexcept = default;
DataBlock& DataBlock::operator=(DataBlock&&) noexcept = default;
class MomentDataBlockImpl
@ -198,7 +198,7 @@ bool MomentDataBlock::Parse(std::istream& is)
p->scale_ = awips::Message::SwapFloat(p->scale_);
p->offset_ = awips::Message::SwapFloat(p->offset_);
if (p->numberOfDataMomentGates_ >= 0 && p->numberOfDataMomentGates_ <= 1840)
if (p->numberOfDataMomentGates_ <= 1840)
{
if (p->dataWordSize_ == 8)
{

View file

@ -64,7 +64,7 @@ DigitalRadialDataArrayPacket::DigitalRadialDataArrayPacket() :
DigitalRadialDataArrayPacket::~DigitalRadialDataArrayPacket() = default;
DigitalRadialDataArrayPacket::DigitalRadialDataArrayPacket(
DigitalRadialDataArrayPacket&&) noexcept = default;
DigitalRadialDataArrayPacket&&) noexcept = default;
DigitalRadialDataArrayPacket& DigitalRadialDataArrayPacket::operator=(
DigitalRadialDataArrayPacket&&) noexcept = default;
@ -158,13 +158,13 @@ bool DigitalRadialDataArrayPacket::Parse(std::istream& is)
logger_->warn("Invalid packet code: {}", p->packetCode_);
blockValid = false;
}
if (p->indexOfFirstRangeBin_ < 0 || p->indexOfFirstRangeBin_ > 230)
if (p->indexOfFirstRangeBin_ > 230)
{
logger_->warn("Invalid index of first range bin: {}",
p->indexOfFirstRangeBin_);
blockValid = false;
}
if (p->numberOfRangeBins_ < 0 || p->numberOfRangeBins_ > 1840)
if (p->numberOfRangeBins_ > 1840)
{
logger_->warn("Invalid number of range bins: {}",
p->numberOfRangeBins_);

View file

@ -3,12 +3,22 @@
#include <scwx/util/rangebuf.hpp>
#include <istream>
#include <sstream>
#include <string>
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-copy"
#endif
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/filter/bzip2.hpp>
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
namespace scwx
{
namespace wsr88d

View file

@ -102,11 +102,11 @@ bool VectorArrowDataPacket::Parse(std::istream& is)
// The number of vectors is equal to the size divided by the number of bytes
// in a vector
size_t vectorCount = p->lengthOfBlock_ / 10;
std::size_t vectorCount = p->lengthOfBlock_ / 10;
p->arrow_.resize(vectorCount);
for (int v = 0; v < vectorCount && !is.eof(); v++)
for (std::size_t v = 0; v < vectorCount && !is.eof(); v++)
{
VectorArrow& arrow = p->arrow_[v];

View file

@ -101,11 +101,11 @@ bool WindBarbDataPacket::Parse(std::istream& is)
// The number of vectors is equal to the size divided by the number of bytes
// in a vector
size_t vectorCount = p->lengthOfBlock_ / 10;
std::size_t vectorCount = p->lengthOfBlock_ / 10;
p->windBarb_.resize(vectorCount);
for (int v = 0; v < vectorCount && !is.eof(); v++)
for (std::size_t v = 0; v < vectorCount && !is.eof(); v++)
{
WindBarb& windBarb = p->windBarb_[v];

View file

@ -246,6 +246,10 @@ if (WIN32)
target_link_libraries(wxdata INTERFACE Ws2_32)
endif()
if (NOT MSVC)
target_link_libraries(wxdata PUBLIC date::date-tz)
endif()
set_target_properties(wxdata PROPERTIES CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF)