mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-11-01 10:50:05 +00:00
Merge pull request #436 from AdenKoperczak/radar_site_line
Radar site line and radar distance and altitude tootlip
This commit is contained in:
commit
6ecb3f6ffb
9 changed files with 224 additions and 40 deletions
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -52,6 +51,7 @@ public:
|
|||
[[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);
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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()");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue