diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index 1e3735de..9cab7c89 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -74,8 +74,10 @@ set(SRC_MAP source/scwx/qt/map/map_widget.cpp source/scwx/qt/map/radar_product_layer.cpp source/scwx/qt/map/radar_range_layer.cpp source/scwx/qt/map/triangle_layer.cpp) -set(HDR_SETTINGS source/scwx/qt/settings/general_settings.hpp) -set(SRC_SETTINGS source/scwx/qt/settings/general_settings.cpp) +set(HDR_SETTINGS source/scwx/qt/settings/general_settings.hpp + source/scwx/qt/settings/palette_settings.hpp) +set(SRC_SETTINGS source/scwx/qt/settings/general_settings.cpp + source/scwx/qt/settings/palette_settings.cpp) set(HDR_UI source/scwx/qt/ui/flow_layout.hpp) set(SRC_UI source/scwx/qt/ui/flow_layout.cpp) set(HDR_UTIL source/scwx/qt/util/font.hpp diff --git a/scwx-qt/source/scwx/qt/manager/settings_manager.cpp b/scwx-qt/source/scwx/qt/manager/settings_manager.cpp index 7280c19d..de4e7842 100644 --- a/scwx-qt/source/scwx/qt/manager/settings_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/settings_manager.cpp @@ -21,6 +21,7 @@ namespace SettingsManager static const std::string logPrefix_ = "[scwx::qt::manager::settings_manager] "; static std::shared_ptr generalSettings_ = nullptr; +static std::shared_ptr paletteSettings_ = nullptr; static boost::json::value ConvertSettingsToJson(); static void GenerateDefaultSettings(); @@ -79,11 +80,17 @@ std::shared_ptr general_settings() return generalSettings_; } +std::shared_ptr palette_settings() +{ + return paletteSettings_; +} + static boost::json::value ConvertSettingsToJson() { boost::json::object settingsJson; settingsJson["general"] = generalSettings_->ToJson(); + settingsJson["palette"] = paletteSettings_->ToJson(); return settingsJson; } @@ -93,6 +100,7 @@ static void GenerateDefaultSettings() BOOST_LOG_TRIVIAL(info) << logPrefix_ << "Generating default settings"; generalSettings_ = settings::GeneralSettings::Create(); + paletteSettings_ = settings::PaletteSettings::Create(); } static bool LoadSettings(const boost::json::object& settingsJson) @@ -103,6 +111,8 @@ static bool LoadSettings(const boost::json::object& settingsJson) generalSettings_ = settings::GeneralSettings::Load( settingsJson.if_contains("general"), jsonDirty); + paletteSettings_ = settings::PaletteSettings::Load( + settingsJson.if_contains("palette"), jsonDirty); return jsonDirty; } diff --git a/scwx-qt/source/scwx/qt/manager/settings_manager.hpp b/scwx-qt/source/scwx/qt/manager/settings_manager.hpp index a2b10209..9fb555b9 100644 --- a/scwx-qt/source/scwx/qt/manager/settings_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/settings_manager.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace scwx { @@ -15,6 +16,7 @@ void Initialize(); void ReadSettings(const std::string& settingsPath); std::shared_ptr general_settings(); +std::shared_ptr palette_settings(); } // namespace SettingsManager } // namespace manager diff --git a/scwx-qt/source/scwx/qt/map/map_widget.cpp b/scwx-qt/source/scwx/qt/map/map_widget.cpp index 4f8f3109..d316a66e 100644 --- a/scwx-qt/source/scwx/qt/map/map_widget.cpp +++ b/scwx-qt/source/scwx/qt/map/map_widget.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -99,11 +100,13 @@ void MapWidget::SelectRadarProduct(common::Level2Product product) p->radarProductView_->Initialize(); - QString colorTableFile = qgetenv("COLOR_TABLE"); - if (!colorTableFile.isEmpty()) + std::string colorTableFile = + manager::SettingsManager::palette_settings()->palette( + common::GetLevel2Palette(product)); + if (!colorTableFile.empty()) { std::shared_ptr colorTable = - common::ColorTable::Load(colorTableFile.toUtf8().constData()); + common::ColorTable::Load(colorTableFile); p->radarProductView_->LoadColorTable(colorTable); } diff --git a/scwx-qt/source/scwx/qt/settings/general_settings.cpp b/scwx-qt/source/scwx/qt/settings/general_settings.cpp index c7f14a7a..746d206f 100644 --- a/scwx-qt/source/scwx/qt/settings/general_settings.cpp +++ b/scwx-qt/source/scwx/qt/settings/general_settings.cpp @@ -12,7 +12,7 @@ namespace settings static const std::string logPrefix_ = "[scwx::qt::settings::general_settings] "; -static const std::string& DEFAULT_DEFAULT_RADAR_SITE = "KLSX"; +static const std::string DEFAULT_DEFAULT_RADAR_SITE = "KLSX"; class GeneralSettingsImpl { @@ -35,12 +35,12 @@ GeneralSettings::GeneralSettings(GeneralSettings&&) noexcept = default; GeneralSettings& GeneralSettings::operator=(GeneralSettings&&) noexcept = default; -const std::string& GeneralSettings::default_radar_site() +const std::string& GeneralSettings::default_radar_site() const { return p->defaultRadarSite_; } -boost::json::value GeneralSettings::ToJson() +boost::json::value GeneralSettings::ToJson() const { boost::json::object json; diff --git a/scwx-qt/source/scwx/qt/settings/general_settings.hpp b/scwx-qt/source/scwx/qt/settings/general_settings.hpp index 1ae9ef65..6f7391a5 100644 --- a/scwx-qt/source/scwx/qt/settings/general_settings.hpp +++ b/scwx-qt/source/scwx/qt/settings/general_settings.hpp @@ -26,9 +26,9 @@ public: GeneralSettings(GeneralSettings&&) noexcept; GeneralSettings& operator=(GeneralSettings&&) noexcept; - const std::string& default_radar_site(); + const std::string& default_radar_site() const; - boost::json::value ToJson(); + boost::json::value ToJson() const; static std::shared_ptr Create(); static std::shared_ptr Load(const boost::json::value* json, diff --git a/scwx-qt/source/scwx/qt/settings/palette_settings.cpp b/scwx-qt/source/scwx/qt/settings/palette_settings.cpp new file mode 100644 index 00000000..b3c25b37 --- /dev/null +++ b/scwx-qt/source/scwx/qt/settings/palette_settings.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace settings +{ + +static const std::string logPrefix_ = "[scwx::qt::settings::palette_settings] "; + +static const std::vector paletteNames_ = { + "BR", "BV", "SW", "ZDR", "PHI", "CC", "???"}; + +static const std::string DEFAULT_KEY = "Default"; +static const std::string DEFAULT_PALETTE = ""; + +class PaletteSettingsImpl +{ +public: + explicit PaletteSettingsImpl() {} + + ~PaletteSettingsImpl() {} + + void SetDefaults() + { + std::for_each( + paletteNames_.cbegin(), + paletteNames_.cend(), + [&](const std::string& name) { palette_[name] = DEFAULT_PALETTE; }); + } + + std::unordered_map palette_; +}; + +PaletteSettings::PaletteSettings() : p(std::make_unique()) +{ +} +PaletteSettings::~PaletteSettings() = default; + +PaletteSettings::PaletteSettings(PaletteSettings&&) noexcept = default; +PaletteSettings& +PaletteSettings::operator=(PaletteSettings&&) noexcept = default; + +const std::string& PaletteSettings::palette(const std::string& name) const +{ + auto palette = p->palette_.find(name); + + if (palette == p->palette_.cend()) + { + palette = p->palette_.find("Default"); + } + + if (palette == p->palette_.cend()) + { + return DEFAULT_PALETTE; + } + + return palette->second; +} + +boost::json::value PaletteSettings::ToJson() const +{ + boost::json::object json; + + std::for_each( + paletteNames_.cbegin(), + paletteNames_.cend(), + [&](const std::string& name) { json[name] = p->palette_[name]; }); + + return json; +} + +std::shared_ptr PaletteSettings::Create() +{ + std::shared_ptr generalSettings = + std::make_shared(); + + generalSettings->p->SetDefaults(); + + return generalSettings; +} + +std::shared_ptr +PaletteSettings::Load(const boost::json::value* json, bool& jsonDirty) +{ + std::shared_ptr generalSettings = + std::make_shared(); + + if (json != nullptr && json->is_object()) + { + std::for_each(paletteNames_.cbegin(), + paletteNames_.cend(), + [&](const std::string& name) { + jsonDirty |= !util::json::FromJsonString( + json->as_object(), + name, + generalSettings->p->palette_[name], + DEFAULT_PALETTE); + }); + } + else + { + if (json == nullptr) + { + BOOST_LOG_TRIVIAL(warning) + << logPrefix_ << "Key is not present, resetting to defaults"; + } + else if (!json->is_object()) + { + BOOST_LOG_TRIVIAL(warning) + << logPrefix_ << "Invalid json, resetting to defaults"; + } + + generalSettings->p->SetDefaults(); + jsonDirty = true; + } + + return generalSettings; +} + +bool operator==(const PaletteSettings& lhs, const PaletteSettings& rhs) +{ + return lhs.p->palette_ == rhs.p->palette_; +} + +} // namespace settings +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/settings/palette_settings.hpp b/scwx-qt/source/scwx/qt/settings/palette_settings.hpp new file mode 100644 index 00000000..d032a380 --- /dev/null +++ b/scwx-qt/source/scwx/qt/settings/palette_settings.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace settings +{ + +class PaletteSettingsImpl; + +class PaletteSettings +{ +public: + explicit PaletteSettings(); + ~PaletteSettings(); + + PaletteSettings(const PaletteSettings&) = delete; + PaletteSettings& operator=(const PaletteSettings&) = delete; + + PaletteSettings(PaletteSettings&&) noexcept; + PaletteSettings& operator=(PaletteSettings&&) noexcept; + + const std::string& palette(const std::string& name) const; + + boost::json::value ToJson() const; + + static std::shared_ptr Create(); + static std::shared_ptr Load(const boost::json::value* json, + bool& jsonDirty); + + friend bool operator==(const PaletteSettings& lhs, + const PaletteSettings& rhs); + +private: + std::unique_ptr p; +}; + +} // namespace settings +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/view/level2_product_view.cpp b/scwx-qt/source/scwx/qt/view/level2_product_view.cpp index 13db55d3..37e61f8a 100644 --- a/scwx-qt/source/scwx/qt/view/level2_product_view.cpp +++ b/scwx-qt/source/scwx/qt/view/level2_product_view.cpp @@ -156,14 +156,15 @@ void Level2ProductView::LoadColorTable( void Level2ProductView::UpdateColorTable() { if (p->momentDataBlock0_ == nullptr || // - p->colorTable_ == nullptr) + p->colorTable_ == nullptr || // + !p->colorTable_->IsValid()) { // Nothing to update return; } - const float offset = p->momentDataBlock0_->offset(); - const float scale = p->momentDataBlock0_->scale(); + float offset = p->momentDataBlock0_->offset(); + float scale = p->momentDataBlock0_->scale(); if (p->savedColorTable_ == p->colorTable_ && // p->savedOffset_ == offset && // @@ -173,10 +174,41 @@ void Level2ProductView::UpdateColorTable() return; } - std::vector& lut = p->colorTableLut_; - lut.resize(254); + uint16_t rangeMin; + uint16_t rangeMax; - auto dataRange = boost::irange(2, 255); + switch (p->product_) + { + case common::Level2Product::Reflectivity: + case common::Level2Product::Velocity: + case common::Level2Product::SpectrumWidth: + case common::Level2Product::CorrelationCoefficient: + default: + rangeMin = 2; + rangeMax = 255; + break; + + case common::Level2Product::DifferentialReflectivity: + rangeMin = 2; + rangeMax = 1058; + break; + + case common::Level2Product::DifferentialPhase: + rangeMin = 2; + rangeMax = 1023; + break; + + case common::Level2Product::ClutterFilterPowerRemoved: + rangeMin = 8; + rangeMax = 81; + break; + } + + boost::integer_range dataRange = + boost::irange(rangeMin, rangeMax); + + std::vector& lut = p->colorTableLut_; + lut.resize(rangeMax - rangeMin + 1); std::for_each(std::execution::par_unseq, dataRange.begin(), diff --git a/wxdata/include/scwx/common/color_table.hpp b/wxdata/include/scwx/common/color_table.hpp index 6d8654bc..7b1c9eb6 100644 --- a/wxdata/include/scwx/common/color_table.hpp +++ b/wxdata/include/scwx/common/color_table.hpp @@ -35,6 +35,7 @@ public: ColorTable& operator=(ColorTable&&) noexcept; boost::gil::rgba8_pixel_t Color(float value) const; + bool IsValid() const; static std::shared_ptr Load(const std::string& filename); diff --git a/wxdata/include/scwx/common/products.hpp b/wxdata/include/scwx/common/products.hpp index 10b94dea..6e733b77 100644 --- a/wxdata/include/scwx/common/products.hpp +++ b/wxdata/include/scwx/common/products.hpp @@ -29,6 +29,7 @@ typedef util::Iterator + +namespace scwx +{ +namespace util +{ + +std::istream& getline(std::istream& is, std::string& t); + +} // namespace util +} // namespace scwx diff --git a/wxdata/source/scwx/common/color_table.cpp b/wxdata/source/scwx/common/color_table.cpp index 5ac6098a..df709910 100644 --- a/wxdata/source/scwx/common/color_table.cpp +++ b/wxdata/source/scwx/common/color_table.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -122,6 +123,11 @@ boost::gil::rgba8_pixel_t ColorTable::Color(float value) const return color; } +bool ColorTable::IsValid() const +{ + return p->colorMap_.size() > 0; +} + std::shared_ptr ColorTable::Load(const std::string& filename) { BOOST_LOG_TRIVIAL(debug) @@ -132,7 +138,7 @@ std::shared_ptr ColorTable::Load(const std::string& filename) std::ifstream f(filename, std::ios_base::in); std::string line; - while (std::getline(f, line)) + while (scwx::util::getline(f, line)) { std::string token; std::istringstream tokens(line); diff --git a/wxdata/source/scwx/common/products.cpp b/wxdata/source/scwx/common/products.cpp index 94cb9318..d475a603 100644 --- a/wxdata/source/scwx/common/products.cpp +++ b/wxdata/source/scwx/common/products.cpp @@ -27,6 +27,16 @@ static const std::unordered_map level2Description_ { {Level2Product::ClutterFilterPowerRemoved, "Clutter Filter Power Removed"}, {Level2Product::Unknown, "?"}}; +static const std::unordered_map level2Palette_ { + {Level2Product::Reflectivity, "BR"}, + {Level2Product::Velocity, "BV"}, + {Level2Product::SpectrumWidth, "SW"}, + {Level2Product::DifferentialReflectivity, "ZDR"}, + {Level2Product::DifferentialPhase, "PHI"}, + {Level2Product::CorrelationCoefficient, "CC"}, + {Level2Product::ClutterFilterPowerRemoved, "???"}, + {Level2Product::Unknown, "???"}}; + const std::string& GetLevel2Name(Level2Product product) { return level2Name_.at(product); @@ -37,6 +47,11 @@ const std::string& GetLevel2Description(Level2Product product) return level2Description_.at(product); } +const std::string& GetLevel2Palette(Level2Product product) +{ + return level2Palette_.at(product); +} + const Level2Product GetLevel2Product(const std::string& name) { auto result = std::find_if( diff --git a/wxdata/source/scwx/util/streams.cpp b/wxdata/source/scwx/util/streams.cpp new file mode 100644 index 00000000..51bb9de0 --- /dev/null +++ b/wxdata/source/scwx/util/streams.cpp @@ -0,0 +1,42 @@ +#include + +namespace scwx +{ +namespace util +{ + +std::istream& getline(std::istream& is, std::string& t) +{ + t.clear(); + + std::istream::sentry sentry(is, true); + std::streambuf* sb = is.rdbuf(); + + while (true) + { + int c = sb->sbumpc(); + switch (c) + { + case '\n': return is; + + case '\r': + if (sb->sgetc() == '\n') + { + sb->sbumpc(); + } + return is; + + case std::streambuf::traits_type::eof(): + if (t.empty()) + { + is.setstate(std::ios::eofbit); + } + return is; + + default: t += static_cast(c); + } + } +} + +} // namespace util +} // namespace scwx diff --git a/wxdata/wxdata.cmake b/wxdata/wxdata.cmake index bfeff763..a02ec4bc 100644 --- a/wxdata/wxdata.cmake +++ b/wxdata/wxdata.cmake @@ -10,8 +10,10 @@ set(SRC_COMMON source/scwx/common/color_table.cpp source/scwx/common/products.cpp) set(HDR_UTIL include/scwx/util/iterator.hpp include/scwx/util/rangebuf.hpp + include/scwx/util/streams.hpp include/scwx/util/vectorbuf.hpp) set(SRC_UTIL source/scwx/util/rangebuf.cpp + source/scwx/util/streams.cpp source/scwx/util/vectorbuf.cpp) set(HDR_WSR88D include/scwx/wsr88d/ar2v_file.hpp) set(SRC_WSR88D source/scwx/wsr88d/ar2v_file.cpp)