Merge pull request #436 from AdenKoperczak/radar_site_line

Radar site line and radar distance and altitude tootlip
This commit is contained in:
Dan Paulat 2025-05-04 11:49:46 -05:00 committed by GitHub
commit 6ecb3f6ffb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 224 additions and 40 deletions

View file

@ -51,6 +51,7 @@ public:
std::string state_ {};
std::string place_ {};
std::string tzName_ {};
double altitude_ {0.0};
const scwx::util::time_zone* timeZone_ {nullptr};
};
@ -142,6 +143,11 @@ const scwx::util::time_zone* RadarSite::time_zone() const
return p->timeZone_;
}
units::length::feet<double> RadarSite::altitude() const
{
return units::length::feet<double>(p->altitude_);
}
std::shared_ptr<RadarSite> RadarSite::Get(const std::string& id)
{
std::shared_lock lock(siteMutex_);
@ -268,6 +274,8 @@ size_t RadarSite::ReadConfig(const std::string& path)
site->p->state_ = boost::json::value_to<std::string>(o.at("state"));
site->p->place_ = boost::json::value_to<std::string>(o.at("place"));
site->p->tzName_ = boost::json::value_to<std::string>(o.at("tz"));
site->p->altitude_ =
boost::json::value_to<double>(o.at("elevation"));
try
{

View file

@ -6,12 +6,9 @@
#include <optional>
#include <string>
#include <vector>
#include <units/length.h>
namespace scwx
{
namespace qt
{
namespace config
namespace scwx::qt::config
{
class RadarSiteImpl;
@ -28,18 +25,19 @@ public:
RadarSite(RadarSite&&) noexcept;
RadarSite& operator=(RadarSite&&) noexcept;
std::string type() const;
std::string type_name() const;
std::string id() const;
double latitude() const;
double longitude() const;
std::string country() const;
std::string state() const;
std::string place() const;
std::string location_name() const;
std::string tz_name() const;
[[nodiscard]] std::string type() const;
[[nodiscard]] std::string type_name() const;
[[nodiscard]] std::string id() const;
[[nodiscard]] double latitude() const;
[[nodiscard]] double longitude() const;
[[nodiscard]] std::string country() const;
[[nodiscard]] std::string state() const;
[[nodiscard]] std::string place() const;
[[nodiscard]] std::string location_name() const;
[[nodiscard]] std::string tz_name() const;
[[nodiscard]] units::length::feet<double> altitude() const;
const scwx::util::time_zone* time_zone() const;
[[nodiscard]] const scwx::util::time_zone* time_zone() const;
static std::shared_ptr<RadarSite> Get(const std::string& id);
static std::vector<std::shared_ptr<RadarSite>> GetAll();
@ -67,6 +65,4 @@ private:
std::string GetRadarIdFromSiteId(const std::string& siteId);
} // namespace config
} // namespace qt
} // namespace scwx
} // namespace scwx::qt::config

View file

@ -27,6 +27,7 @@ public:
common::RadarProductGroup::Unknown};
std::string radarProduct_ {"???"};
int16_t radarProductCode_ {0};
std::shared_ptr<config::RadarSite> radarSite_ {nullptr};
MapProvider mapProvider_ {MapProvider::Unknown};
std::string mapCopyrights_ {};
@ -106,6 +107,11 @@ std::string MapContext::radar_product() const
return p->radarProduct_;
}
std::shared_ptr<config::RadarSite> MapContext::radar_site() const
{
return p->radarSite_;
}
int16_t MapContext::radar_product_code() const
{
return p->radarProductCode_;
@ -174,6 +180,11 @@ void MapContext::set_radar_product_code(int16_t radarProductCode)
p->radarProductCode_ = radarProductCode;
}
void MapContext::set_radar_site(const std::shared_ptr<config::RadarSite>& site)
{
p->radarSite_ = site;
}
void MapContext::set_widget(QWidget* widget)
{
p->widget_ = widget;

View file

@ -4,13 +4,12 @@
#include <scwx/qt/map/map_provider.hpp>
#include <scwx/common/geographic.hpp>
#include <scwx/common/products.hpp>
#include <scwx/qt/config/radar_site.hpp>
#include <qmaplibre.hpp>
#include <QMargins>
namespace scwx
{
namespace qt
namespace scwx::qt
{
namespace view
{
@ -30,7 +29,7 @@ class MapContext : public gl::GlContext
public:
explicit MapContext(
std::shared_ptr<view::RadarProductView> radarProductView = nullptr);
~MapContext();
~MapContext() override;
MapContext(const MapContext&) = delete;
MapContext& operator=(const MapContext&) = delete;
@ -48,11 +47,12 @@ public:
[[nodiscard]] std::shared_ptr<view::OverlayProductView>
overlay_product_view() const;
[[nodiscard]] std::shared_ptr<view::RadarProductView>
radar_product_view() const;
[[nodiscard]] common::RadarProductGroup radar_product_group() const;
[[nodiscard]] std::string radar_product() const;
[[nodiscard]] int16_t radar_product_code() const;
[[nodiscard]] QWidget* widget() const;
radar_product_view() const;
[[nodiscard]] common::RadarProductGroup radar_product_group() const;
[[nodiscard]] std::string radar_product() const;
[[nodiscard]] int16_t radar_product_code() const;
[[nodiscard]] std::shared_ptr<config::RadarSite> radar_site() const;
[[nodiscard]] QWidget* widget() const;
void set_map(const std::shared_ptr<QMapLibre::Map>& map);
void set_map_copyrights(const std::string& copyrights);
@ -67,6 +67,7 @@ public:
void set_radar_product_group(common::RadarProductGroup radarProductGroup);
void set_radar_product(const std::string& radarProduct);
void set_radar_product_code(int16_t radarProductCode);
void set_radar_site(const std::shared_ptr<config::RadarSite>& site);
void set_widget(QWidget* widget);
private:
@ -76,5 +77,4 @@ private:
};
} // namespace map
} // namespace qt
} // namespace scwx
} // namespace scwx::qt

View file

@ -2008,6 +2008,9 @@ void MapWidgetImpl::SelectNearestRadarSite(double latitude,
void MapWidgetImpl::SetRadarSite(const std::string& radarSite,
bool checkProductAvailability)
{
// Set the radar site in the context
context_->set_radar_site(config::RadarSite::Get(radarSite));
// Check if radar site has changed
if (radarProductManager_ == nullptr ||
radarSite != radarProductManager_->radar_site()->id())

View file

@ -1,6 +1,9 @@
#include <scwx/qt/map/radar_product_layer.hpp>
#include <scwx/qt/map/map_settings.hpp>
#include <scwx/qt/gl/shader_program.hpp>
#include <scwx/qt/settings/unit_settings.hpp>
#include <scwx/qt/types/unit_types.hpp>
#include <scwx/qt/util/geographic_lib.hpp>
#include <scwx/qt/util/maplibre.hpp>
#include <scwx/qt/util/tooltip.hpp>
#include <scwx/qt/view/radar_product_view.hpp>
@ -353,11 +356,66 @@ bool RadarProductLayer::RunMousePicking(
std::shared_ptr<view::RadarProductView> radarProductView =
context()->radar_product_view();
if (radarProductView == nullptr)
if (context()->radar_site() == nullptr)
{
return itemPicked;
}
// Get distance and altitude of point
const double radarLatitude = context()->radar_site()->latitude();
const double radarLongitude = context()->radar_site()->longitude();
const auto distanceMeters =
util::GeographicLib::GetDistance(mouseGeoCoords.latitude_,
mouseGeoCoords.longitude_,
radarLatitude,
radarLongitude);
const std::string distanceUnitName =
settings::UnitSettings::Instance().distance_units().GetValue();
const types::DistanceUnits distanceUnits =
types::GetDistanceUnitsFromName(distanceUnitName);
const double distanceScale = types::GetDistanceUnitsScale(distanceUnits);
const std::string distanceAbbrev =
types::GetDistanceUnitsAbbreviation(distanceUnits);
const double distance = distanceMeters.value() *
scwx::common::kKilometersPerMeter * distanceScale;
std::string distanceHeightStr =
fmt::format("{:.2f} {}", distance, distanceAbbrev);
if (radarProductView == nullptr)
{
util::tooltip::Show(distanceHeightStr, mouseGlobalPos);
itemPicked = true;
return itemPicked;
}
std::optional<float> elevation = radarProductView->elevation();
if (elevation.has_value())
{
const auto altitudeMeters =
util::GeographicLib::GetRadarBeamAltititude(
distanceMeters,
units::angle::degrees<double>(*elevation),
context()->radar_site()->altitude());
const std::string heightUnitName =
settings::UnitSettings::Instance().echo_tops_units().GetValue();
const types::EchoTopsUnits heightUnits =
types::GetEchoTopsUnitsFromName(heightUnitName);
const double heightScale = types::GetEchoTopsUnitsScale(heightUnits);
const std::string heightAbbrev =
types::GetEchoTopsUnitsAbbreviation(heightUnits);
const double altitude = altitudeMeters.value() *
scwx::common::kKilometersPerMeter *
heightScale;
distanceHeightStr = fmt::format(
"{}\n{:.2f} {}", distanceHeightStr, altitude, heightAbbrev);
}
std::optional<std::uint16_t> binLevel =
radarProductView->GetBinLevel(mouseGeoCoords);
@ -383,12 +441,13 @@ bool RadarProductLayer::RunMousePicking(
if (codeName != codeShortName && !codeShortName.empty())
{
// There is a unique long and short name for the code
hoverText = fmt::format("{}: {}", codeShortName, codeName);
hoverText = fmt::format(
"{}: {}\n{}", codeShortName, codeName, distanceHeightStr);
}
else
{
// Otherwise, only use the long name (always present)
hoverText = codeName;
hoverText = fmt::format("{}\n{}", codeName, distanceHeightStr);
}
// Show the tooltip
@ -439,17 +498,20 @@ bool RadarProductLayer::RunMousePicking(
{
// Don't display a units value that wasn't intended to be
// displayed
hoverText = fmt::format("{}{}", f, suffix);
hoverText =
fmt::format("{}{}\n{}", f, suffix, distanceHeightStr);
}
else if (std::isalpha(static_cast<unsigned char>(units.at(0))))
{
// dBZ, Kts, etc.
hoverText = fmt::format("{} {}{}", f, units, suffix);
hoverText = fmt::format(
"{} {}{}\n{}", f, units, suffix, distanceHeightStr);
}
else
{
// %, etc.
hoverText = fmt::format("{}{}{}", f, units, suffix);
hoverText = fmt::format(
"{}{}{}\n{}", f, units, suffix, distanceHeightStr);
}
// Show the tooltip
@ -458,6 +520,12 @@ bool RadarProductLayer::RunMousePicking(
itemPicked = true;
}
}
else
{
// Always show tooltip for distance and altitude
util::tooltip::Show(distanceHeightStr, mouseGlobalPos);
itemPicked = true;
}
}
return itemPicked;

View file

@ -1,5 +1,6 @@
#include <scwx/qt/map/radar_site_layer.hpp>
#include <scwx/qt/config/radar_site.hpp>
#include <scwx/qt/gl/draw/geo_lines.hpp>
#include <scwx/qt/settings/general_settings.hpp>
#include <scwx/qt/settings/text_settings.hpp>
#include <scwx/qt/util/maplibre.hpp>
@ -10,6 +11,8 @@
#include <imgui.h>
#include <mbgl/util/constants.hpp>
#include <QGuiApplication>
namespace scwx
{
namespace qt
@ -23,11 +26,15 @@ static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
class RadarSiteLayer::Impl
{
public:
explicit Impl(RadarSiteLayer* self) : self_ {self} {}
explicit Impl(RadarSiteLayer* self, std::shared_ptr<MapContext>& context) :
self_ {self}, geoLines_ {std::make_shared<gl::draw::GeoLines>(context)}
{
}
~Impl() = default;
void RenderRadarSite(const QMapLibre::CustomLayerRenderParameters& params,
std::shared_ptr<config::RadarSite>& radarSite);
void RenderRadarLine();
RadarSiteLayer* self_;
@ -41,10 +48,15 @@ public:
float halfHeight_ {};
std::string hoverText_ {};
std::shared_ptr<gl::draw::GeoLines> geoLines_;
std::array<std::shared_ptr<gl::draw::GeoLineDrawItem>, 2> radarSiteLines_ {
nullptr, nullptr};
};
RadarSiteLayer::RadarSiteLayer(std::shared_ptr<MapContext> context) :
DrawLayer(context, "RadarSiteLayer"), p(std::make_unique<Impl>(this))
DrawLayer(context, "RadarSiteLayer"),
p(std::make_unique<Impl>(this, context))
{
}
@ -56,7 +68,24 @@ void RadarSiteLayer::Initialize()
p->radarSites_ = config::RadarSite::GetAll();
ImGuiInitialize();
p->geoLines_->StartLines();
p->radarSiteLines_[0] = p->geoLines_->AddLine();
p->radarSiteLines_[1] = p->geoLines_->AddLine();
p->geoLines_->FinishLines();
static const boost::gil::rgba32f_pixel_t color0 {0.0f, 0.0f, 0.0f, 1.0f};
static const boost::gil::rgba32f_pixel_t color1 {1.0f, 1.0f, 1.0f, 1.0f};
static const float width = 1;
p->geoLines_->SetLineModulate(p->radarSiteLines_[0], color0);
p->geoLines_->SetLineWidth(p->radarSiteLines_[0], width + 2);
p->geoLines_->SetLineModulate(p->radarSiteLines_[1], color1);
p->geoLines_->SetLineWidth(p->radarSiteLines_[1], width);
AddDrawItem(p->geoLines_);
p->geoLines_->set_thresholded(false);
DrawLayer::Initialize();
}
void RadarSiteLayer::Render(
@ -96,8 +125,12 @@ void RadarSiteLayer::Render(
}
ImGui::PopStyleVar();
ImGuiFrameEnd();
p->RenderRadarLine();
DrawLayer::RenderWithoutImGui(params);
ImGuiFrameEnd();
SCWX_GL_CHECK_ERROR();
}
@ -163,6 +196,37 @@ void RadarSiteLayer::Impl::RenderRadarSite(
}
}
void RadarSiteLayer::Impl::RenderRadarLine()
{
if ((QGuiApplication::keyboardModifiers() &
Qt::KeyboardModifier::ShiftModifier) &&
self_->context()->radar_site() != nullptr)
{
const auto& mouseCoord = self_->context()->mouse_coordinate();
const double radarLatitude = self_->context()->radar_site()->latitude();
const double radarLongitude = self_->context()->radar_site()->longitude();
geoLines_->SetLineLocation(radarSiteLines_[0],
static_cast<float>(mouseCoord.latitude_),
static_cast<float>(mouseCoord.longitude_),
static_cast<float>(radarLatitude),
static_cast<float>(radarLongitude));
geoLines_->SetLineVisible(radarSiteLines_[0], true);
geoLines_->SetLineLocation(radarSiteLines_[1],
static_cast<float>(mouseCoord.latitude_),
static_cast<float>(mouseCoord.longitude_),
static_cast<float>(radarLatitude),
static_cast<float>(radarLongitude));
geoLines_->SetLineVisible(radarSiteLines_[1], true);
}
else
{
geoLines_->SetLineVisible(radarSiteLines_[0], false);
geoLines_->SetLineVisible(radarSiteLines_[1], false);
}
}
void RadarSiteLayer::Deinitialize()
{
logger_->debug("Deinitialize()");

View file

@ -2,6 +2,7 @@
#include <scwx/util/logger.hpp>
#include <numbers>
#include <cmath>
#include <GeographicLib/Gnomonic.hpp>
#include <geos/algorithm/PointLocation.h>
@ -289,6 +290,25 @@ bool AreaInRangeOfPoint(const std::vector<common::Coordinate>& area,
return GetDistanceAreaPoint(area, point) <= distance;
}
units::length::meters<double>
GetRadarBeamAltititude(units::length::meters<double> range,
units::angle::degrees<double> elevation,
units::length::meters<double> height)
{
static const units::length::meters<double> earthRadius {6367444 * 4 / 3};
height += earthRadius;
const double elevationRadians =
units::angle::radians<double>(elevation).value();
const auto altitudeSquared =
(range * range + height * height +
2 * range * height * std::sin(elevationRadians));
return units::length::meters<double>(std::sqrt(altitudeSquared.value())) -
earthRadius;
}
} // namespace GeographicLib
} // namespace util
} // namespace qt

View file

@ -121,6 +121,20 @@ bool AreaInRangeOfPoint(const std::vector<common::Coordinate>& area,
const common::Coordinate& point,
const units::length::meters<double> distance);
/**
* Get the altitude of the radar beam at a given distance, elevation and height
*
* @param [in] range The range to the radar site
* @param [in] elevation The elevation of the radar site
* @param [in] height The height of the radar site
*
* @return The altitude of the radar at that range
*/
units::length::meters<double>
GetRadarBeamAltititude(units::length::meters<double> range,
units::angle::degrees<double> elevation,
units::length::meters<double> height);
} // namespace GeographicLib
} // namespace util
} // namespace qt