mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-30 23:40:06 +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; | ||||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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
	
	 Dan Paulat
						Dan Paulat