mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 06:40:05 +00:00 
			
		
		
		
	Merge pull request #250 from AdenKoperczak/audio_location_methods
Radar Site Audio Location Method and Radius
This commit is contained in:
		
						commit
						7b41184fbb
					
				
					 23 changed files with 802 additions and 120 deletions
				
			
		|  | @ -1466,6 +1466,7 @@ void MainWindowImpl::UpdateRadarSite() | ||||||
|       timelineManager_->SetRadarSite("?"); |       timelineManager_->SetRadarSite("?"); | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|  |    alertManager_->SetRadarSite(radarSite); | ||||||
|    placefileManager_->SetRadarSite(radarSite); |    placefileManager_->SetRadarSite(radarSite); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,6 +6,8 @@ | ||||||
| #include <scwx/qt/types/location_types.hpp> | #include <scwx/qt/types/location_types.hpp> | ||||||
| #include <scwx/qt/util/geographic_lib.hpp> | #include <scwx/qt/util/geographic_lib.hpp> | ||||||
| #include <scwx/util/logger.hpp> | #include <scwx/util/logger.hpp> | ||||||
|  | #include <scwx/qt/config/radar_site.hpp> | ||||||
|  | #include <scwx/qt/settings/general_settings.hpp> | ||||||
| 
 | 
 | ||||||
| #include <boost/asio/post.hpp> | #include <boost/asio/post.hpp> | ||||||
| #include <boost/asio/thread_pool.hpp> | #include <boost/asio/thread_pool.hpp> | ||||||
|  | @ -74,6 +76,8 @@ public: | ||||||
|       PositionManager::Instance()}; |       PositionManager::Instance()}; | ||||||
|    std::shared_ptr<TextEventManager> textEventManager_ { |    std::shared_ptr<TextEventManager> textEventManager_ { | ||||||
|       TextEventManager::Instance()}; |       TextEventManager::Instance()}; | ||||||
|  | 
 | ||||||
|  |    std::shared_ptr<config::RadarSite> radarSite_ {}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| AlertManager::AlertManager() : p(std::make_unique<Impl>(this)) {} | AlertManager::AlertManager() : p(std::make_unique<Impl>(this)) {} | ||||||
|  | @ -100,6 +104,33 @@ common::Coordinate AlertManager::Impl::CurrentCoordinate( | ||||||
|          coordinate.longitude_            = trackedCoordinate.longitude(); |          coordinate.longitude_            = trackedCoordinate.longitude(); | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
|  |    else if (locationMethod == types::LocationMethod::RadarSite) | ||||||
|  |    { | ||||||
|  |       std::string radarSiteSelection = | ||||||
|  |          audioSettings.alert_radar_site().GetValue(); | ||||||
|  |       std::shared_ptr<config::RadarSite> radarSite; | ||||||
|  |       if (radarSiteSelection == "default") | ||||||
|  |       { | ||||||
|  |          std::string siteId = settings::GeneralSettings::Instance() | ||||||
|  |                                  .default_radar_site() | ||||||
|  |                                  .GetValue(); | ||||||
|  |          radarSite = config::RadarSite::Get(siteId); | ||||||
|  |       } | ||||||
|  |       else if (radarSiteSelection == "follow") | ||||||
|  |       { | ||||||
|  |          radarSite = radarSite_; | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |       { | ||||||
|  |          radarSite = config::RadarSite::Get(radarSiteSelection); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (radarSite != nullptr) | ||||||
|  |       { | ||||||
|  |          coordinate.latitude_  = radarSite->latitude(); | ||||||
|  |          coordinate.longitude_ = radarSite->longitude(); | ||||||
|  |       } | ||||||
|  |    } | ||||||
| 
 | 
 | ||||||
|    return coordinate; |    return coordinate; | ||||||
| } | } | ||||||
|  | @ -118,6 +149,8 @@ void AlertManager::Impl::HandleAlert(const types::TextEventKey& key, | ||||||
|       audioSettings.alert_location_method().GetValue()); |       audioSettings.alert_location_method().GetValue()); | ||||||
|    common::Coordinate currentCoordinate = CurrentCoordinate(locationMethod); |    common::Coordinate currentCoordinate = CurrentCoordinate(locationMethod); | ||||||
|    std::string        alertCounty = audioSettings.alert_county().GetValue(); |    std::string        alertCounty = audioSettings.alert_county().GetValue(); | ||||||
|  |    auto               alertRadius = units::length::kilometers<double>( | ||||||
|  |       audioSettings.alert_radius().GetValue()); | ||||||
| 
 | 
 | ||||||
|    auto message = textEventManager_->message_list(key).at(messageIndex); |    auto message = textEventManager_->message_list(key).at(messageIndex); | ||||||
| 
 | 
 | ||||||
|  | @ -145,13 +178,16 @@ void AlertManager::Impl::HandleAlert(const types::TextEventKey& key, | ||||||
|       bool activeAtLocation = (locationMethod == types::LocationMethod::All); |       bool activeAtLocation = (locationMethod == types::LocationMethod::All); | ||||||
| 
 | 
 | ||||||
|       if (locationMethod == types::LocationMethod::Fixed || |       if (locationMethod == types::LocationMethod::Fixed || | ||||||
|           locationMethod == types::LocationMethod::Track) |           locationMethod == types::LocationMethod::Track || | ||||||
|  |           locationMethod == types::LocationMethod::RadarSite) | ||||||
|       { |       { | ||||||
|          // Determine if the alert is active at the current coordinte
 |          // Determine if the alert is active at the current coordinte
 | ||||||
|          auto alertCoordinates = segment->codedLocation_->coordinates(); |          auto alertCoordinates = segment->codedLocation_->coordinates(); | ||||||
| 
 | 
 | ||||||
|          activeAtLocation = util::GeographicLib::AreaContainsPoint( |          activeAtLocation = util::GeographicLib::AreaInRangeOfPoint( | ||||||
|             alertCoordinates, currentCoordinate); |                alertCoordinates, | ||||||
|  |                currentCoordinate, | ||||||
|  |                alertRadius); | ||||||
|       } |       } | ||||||
|       else if (locationMethod == types::LocationMethod::County) |       else if (locationMethod == types::LocationMethod::County) | ||||||
|       { |       { | ||||||
|  | @ -183,6 +219,27 @@ void AlertManager::Impl::UpdateLocationTracking( | ||||||
|    positionManager_->EnablePositionUpdates(uuid_, locationEnabled); |    positionManager_->EnablePositionUpdates(uuid_, locationEnabled); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void AlertManager::SetRadarSite( | ||||||
|  |    const std::shared_ptr<config::RadarSite>& radarSite) | ||||||
|  | { | ||||||
|  |    if (p->radarSite_ == radarSite) | ||||||
|  |    { | ||||||
|  |       // No action needed
 | ||||||
|  |       return; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (radarSite == nullptr) | ||||||
|  |    { | ||||||
|  |       logger_->debug("SetRadarSite: ?"); | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       logger_->debug("SetRadarSite: {}", radarSite->id()); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    p->radarSite_ = radarSite; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| std::shared_ptr<AlertManager> AlertManager::Instance() | std::shared_ptr<AlertManager> AlertManager::Instance() | ||||||
| { | { | ||||||
|    static std::weak_ptr<AlertManager> alertManagerReference_ {}; |    static std::weak_ptr<AlertManager> alertManagerReference_ {}; | ||||||
|  |  | ||||||
|  | @ -1,5 +1,7 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <scwx/qt/config/radar_site.hpp> | ||||||
|  | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
| 
 | 
 | ||||||
| #include <QObject> | #include <QObject> | ||||||
|  | @ -20,6 +22,7 @@ public: | ||||||
|    explicit AlertManager(); |    explicit AlertManager(); | ||||||
|    ~AlertManager(); |    ~AlertManager(); | ||||||
| 
 | 
 | ||||||
|  |    void SetRadarSite(const std::shared_ptr<config::RadarSite>& radarSite); | ||||||
|    static std::shared_ptr<AlertManager> Instance(); |    static std::shared_ptr<AlertManager> Instance(); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  |  | ||||||
|  | @ -1,13 +1,16 @@ | ||||||
| #include <scwx/qt/model/alert_model.hpp> | #include <scwx/qt/model/alert_model.hpp> | ||||||
| #include <scwx/qt/config/county_database.hpp> | #include <scwx/qt/config/county_database.hpp> | ||||||
| #include <scwx/qt/manager/text_event_manager.hpp> | #include <scwx/qt/manager/text_event_manager.hpp> | ||||||
|  | #include <scwx/qt/settings/unit_settings.hpp> | ||||||
| #include <scwx/qt/types/qt_types.hpp> | #include <scwx/qt/types/qt_types.hpp> | ||||||
|  | #include <scwx/qt/types/unit_types.hpp> | ||||||
| #include <scwx/qt/util/geographic_lib.hpp> | #include <scwx/qt/util/geographic_lib.hpp> | ||||||
| #include <scwx/common/geographic.hpp> | #include <scwx/common/geographic.hpp> | ||||||
| #include <scwx/util/logger.hpp> | #include <scwx/util/logger.hpp> | ||||||
| #include <scwx/util/strings.hpp> | #include <scwx/util/strings.hpp> | ||||||
| #include <scwx/util/time.hpp> | #include <scwx/util/time.hpp> | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| #include <format> | #include <format> | ||||||
| 
 | 
 | ||||||
| #include <QApplication> | #include <QApplication> | ||||||
|  | @ -73,7 +76,6 @@ public: | ||||||
|                       double, |                       double, | ||||||
|                       types::TextEventHash<types::TextEventKey>> |                       types::TextEventHash<types::TextEventKey>> | ||||||
|                               distanceMap_; |                               distanceMap_; | ||||||
|    scwx::common::DistanceType distanceDisplay_; |  | ||||||
|    scwx::common::Coordinate   previousPosition_; |    scwx::common::Coordinate   previousPosition_; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -182,18 +184,19 @@ QVariant AlertModel::data(const QModelIndex& index, int role) const | ||||||
|       case static_cast<int>(Column::Distance): |       case static_cast<int>(Column::Distance): | ||||||
|          if (role == Qt::DisplayRole) |          if (role == Qt::DisplayRole) | ||||||
|          { |          { | ||||||
|             if (p->distanceDisplay_ == scwx::common::DistanceType::Miles) |             const std::string distanceUnitName = | ||||||
|             { |                settings::UnitSettings::Instance().distance_units().GetValue(); | ||||||
|                return QString("%1 mi").arg( |             types::DistanceUnits distanceUnits = | ||||||
|                   static_cast<uint32_t>(p->distanceMap_.at(textEventKey) * |                types::GetDistanceUnitsFromName(distanceUnitName); | ||||||
|                                         scwx::common::kMilesPerMeter)); |             double distanceScale = types::GetDistanceUnitsScale(distanceUnits); | ||||||
|             } |             std::string abbreviation = | ||||||
|             else |                types::GetDistanceUnitsAbbreviation(distanceUnits); | ||||||
|             { | 
 | ||||||
|                return QString("%1 km").arg( |             return QString("%1 %2") | ||||||
|                   static_cast<uint32_t>(p->distanceMap_.at(textEventKey) * |                .arg(static_cast<uint32_t>(p->distanceMap_.at(textEventKey) * | ||||||
|                                         scwx::common::kKilometersPerMeter)); |                                           scwx::common::kKilometersPerMeter * | ||||||
|             } |                                           distanceScale)) | ||||||
|  |                .arg(QString::fromStdString(abbreviation)); | ||||||
|          } |          } | ||||||
|          else |          else | ||||||
|          { |          { | ||||||
|  | @ -419,7 +422,6 @@ AlertModelImpl::AlertModelImpl() : | ||||||
|     textEventKeys_ {}, |     textEventKeys_ {}, | ||||||
|     geodesic_(util::GeographicLib::DefaultGeodesic()), |     geodesic_(util::GeographicLib::DefaultGeodesic()), | ||||||
|     distanceMap_ {}, |     distanceMap_ {}, | ||||||
|     distanceDisplay_ {scwx::common::DistanceType::Miles}, |  | ||||||
|     previousPosition_ {} |     previousPosition_ {} | ||||||
| { | { | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,8 @@ | ||||||
| #include <scwx/qt/model/radar_site_model.hpp> | #include <scwx/qt/model/radar_site_model.hpp> | ||||||
| #include <scwx/qt/config/radar_site.hpp> | #include <scwx/qt/config/radar_site.hpp> | ||||||
|  | #include <scwx/qt/settings/unit_settings.hpp> | ||||||
| #include <scwx/qt/types/qt_types.hpp> | #include <scwx/qt/types/qt_types.hpp> | ||||||
|  | #include <scwx/qt/types/unit_types.hpp> | ||||||
| #include <scwx/qt/util/geographic_lib.hpp> | #include <scwx/qt/util/geographic_lib.hpp> | ||||||
| #include <scwx/qt/util/json.hpp> | #include <scwx/qt/util/json.hpp> | ||||||
| #include <scwx/common/geographic.hpp> | #include <scwx/common/geographic.hpp> | ||||||
|  | @ -36,7 +38,6 @@ public: | ||||||
|        radarSites_ {}, |        radarSites_ {}, | ||||||
|        geodesic_(util::GeographicLib::DefaultGeodesic()), |        geodesic_(util::GeographicLib::DefaultGeodesic()), | ||||||
|        distanceMap_ {}, |        distanceMap_ {}, | ||||||
|        distanceDisplay_ {scwx::common::DistanceType::Miles}, |  | ||||||
|        previousPosition_ {} |        previousPosition_ {} | ||||||
|    { |    { | ||||||
|       // Get all loaded radar sites
 |       // Get all loaded radar sites
 | ||||||
|  | @ -64,7 +65,6 @@ public: | ||||||
|    const GeographicLib::Geodesic& geodesic_; |    const GeographicLib::Geodesic& geodesic_; | ||||||
| 
 | 
 | ||||||
|    std::unordered_map<std::string, double> distanceMap_; |    std::unordered_map<std::string, double> distanceMap_; | ||||||
|    scwx::common::DistanceType              distanceDisplay_; |  | ||||||
|    scwx::common::Coordinate                previousPosition_; |    scwx::common::Coordinate                previousPosition_; | ||||||
| 
 | 
 | ||||||
|    QIcon starIcon_ {":/res/icons/font-awesome-6/star-solid.svg"}; |    QIcon starIcon_ {":/res/icons/font-awesome-6/star-solid.svg"}; | ||||||
|  | @ -213,18 +213,19 @@ QVariant RadarSiteModel::data(const QModelIndex& index, int role) const | ||||||
|       case static_cast<int>(Column::Distance): |       case static_cast<int>(Column::Distance): | ||||||
|          if (role == Qt::DisplayRole) |          if (role == Qt::DisplayRole) | ||||||
|          { |          { | ||||||
|             if (p->distanceDisplay_ == scwx::common::DistanceType::Miles) |             const std::string distanceUnitName = | ||||||
|             { |                settings::UnitSettings::Instance().distance_units().GetValue(); | ||||||
|                return QString("%1 mi").arg( |             types::DistanceUnits distanceUnits = | ||||||
|                   static_cast<uint32_t>(p->distanceMap_.at(site->id()) * |                types::GetDistanceUnitsFromName(distanceUnitName); | ||||||
|                                         scwx::common::kMilesPerMeter)); |             double distanceScale = types::GetDistanceUnitsScale(distanceUnits); | ||||||
|             } |             std::string abbreviation = | ||||||
|             else |                types::GetDistanceUnitsAbbreviation(distanceUnits); | ||||||
|             { | 
 | ||||||
|                return QString("%1 km").arg( |             return QString("%1 %2") | ||||||
|                   static_cast<uint32_t>(p->distanceMap_.at(site->id()) * |                .arg(static_cast<uint32_t>(p->distanceMap_.at(site->id()) * | ||||||
|                                         scwx::common::kKilometersPerMeter)); |                                           scwx::common::kKilometersPerMeter * | ||||||
|             } |                                           distanceScale)) | ||||||
|  |                .arg(QString::fromStdString(abbreviation)); | ||||||
|          } |          } | ||||||
|          else |          else | ||||||
|          { |          { | ||||||
|  |  | ||||||
|  | @ -37,12 +37,17 @@ public: | ||||||
|       alertLocationMethod_.SetDefault(defaultAlertLocationMethodValue); |       alertLocationMethod_.SetDefault(defaultAlertLocationMethodValue); | ||||||
|       alertLatitude_.SetDefault(0.0); |       alertLatitude_.SetDefault(0.0); | ||||||
|       alertLongitude_.SetDefault(0.0); |       alertLongitude_.SetDefault(0.0); | ||||||
|  |       alertRadius_.SetDefault(0.0); | ||||||
|  |       alertRadarSite_.SetDefault("default"); | ||||||
|       ignoreMissingCodecs_.SetDefault(false); |       ignoreMissingCodecs_.SetDefault(false); | ||||||
| 
 | 
 | ||||||
|       alertLatitude_.SetMinimum(-90.0); |       alertLatitude_.SetMinimum(-90.0); | ||||||
|       alertLatitude_.SetMaximum(90.0); |       alertLatitude_.SetMaximum(90.0); | ||||||
|       alertLongitude_.SetMinimum(-180.0); |       alertLongitude_.SetMinimum(-180.0); | ||||||
|       alertLongitude_.SetMaximum(180.0); |       alertLongitude_.SetMaximum(180.0); | ||||||
|  |       alertRadius_.SetMinimum(0.0); | ||||||
|  |       alertRadius_.SetMaximum(9999999999); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|       alertLocationMethod_.SetValidator( |       alertLocationMethod_.SetValidator( | ||||||
|          SCWX_SETTINGS_ENUM_VALIDATOR(types::LocationMethod, |          SCWX_SETTINGS_ENUM_VALIDATOR(types::LocationMethod, | ||||||
|  | @ -86,6 +91,8 @@ public: | ||||||
|    SettingsVariable<std::string> alertLocationMethod_ {"alert_location_method"}; |    SettingsVariable<std::string> alertLocationMethod_ {"alert_location_method"}; | ||||||
|    SettingsVariable<double>      alertLatitude_ {"alert_latitude"}; |    SettingsVariable<double>      alertLatitude_ {"alert_latitude"}; | ||||||
|    SettingsVariable<double>      alertLongitude_ {"alert_longitude"}; |    SettingsVariable<double>      alertLongitude_ {"alert_longitude"}; | ||||||
|  |    SettingsVariable<std::string> alertRadarSite_ {"alert_radar_site"}; | ||||||
|  |    SettingsVariable<double>      alertRadius_ {"alert_radius"}; | ||||||
|    SettingsVariable<std::string> alertCounty_ {"alert_county"}; |    SettingsVariable<std::string> alertCounty_ {"alert_county"}; | ||||||
|    SettingsVariable<bool>        ignoreMissingCodecs_ {"ignore_missing_codecs"}; |    SettingsVariable<bool>        ignoreMissingCodecs_ {"ignore_missing_codecs"}; | ||||||
| 
 | 
 | ||||||
|  | @ -101,6 +108,8 @@ AudioSettings::AudioSettings() : | ||||||
|                       &p->alertLocationMethod_, |                       &p->alertLocationMethod_, | ||||||
|                       &p->alertLatitude_, |                       &p->alertLatitude_, | ||||||
|                       &p->alertLongitude_, |                       &p->alertLongitude_, | ||||||
|  |                       &p->alertRadarSite_, | ||||||
|  |                       &p->alertRadius_, | ||||||
|                       &p->alertCounty_, |                       &p->alertCounty_, | ||||||
|                       &p->ignoreMissingCodecs_}); |                       &p->ignoreMissingCodecs_}); | ||||||
|    RegisterVariables(p->variables_); |    RegisterVariables(p->variables_); | ||||||
|  | @ -133,6 +142,16 @@ SettingsVariable<double>& AudioSettings::alert_longitude() const | ||||||
|    return p->alertLongitude_; |    return p->alertLongitude_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | SettingsVariable<std::string>& AudioSettings::alert_radar_site() const | ||||||
|  | { | ||||||
|  |    return p->alertRadarSite_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SettingsVariable<double>& AudioSettings::alert_radius() const | ||||||
|  | { | ||||||
|  |    return p->alertRadius_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| SettingsVariable<std::string>& AudioSettings::alert_county() const | SettingsVariable<std::string>& AudioSettings::alert_county() const | ||||||
| { | { | ||||||
|    return p->alertCounty_; |    return p->alertCounty_; | ||||||
|  | @ -166,6 +185,8 @@ bool operator==(const AudioSettings& lhs, const AudioSettings& rhs) | ||||||
|            lhs.p->alertLocationMethod_ == rhs.p->alertLocationMethod_ && |            lhs.p->alertLocationMethod_ == rhs.p->alertLocationMethod_ && | ||||||
|            lhs.p->alertLatitude_ == rhs.p->alertLatitude_ && |            lhs.p->alertLatitude_ == rhs.p->alertLatitude_ && | ||||||
|            lhs.p->alertLongitude_ == rhs.p->alertLongitude_ && |            lhs.p->alertLongitude_ == rhs.p->alertLongitude_ && | ||||||
|  |            lhs.p->alertRadarSite_ == rhs.p->alertRadarSite_ && | ||||||
|  |            lhs.p->alertRadius_ == rhs.p->alertRadius_ && | ||||||
|            lhs.p->alertCounty_ == rhs.p->alertCounty_ && |            lhs.p->alertCounty_ == rhs.p->alertCounty_ && | ||||||
|            lhs.p->alertEnabled_ == rhs.p->alertEnabled_); |            lhs.p->alertEnabled_ == rhs.p->alertEnabled_); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -30,6 +30,8 @@ public: | ||||||
|    SettingsVariable<std::string>& alert_location_method() const; |    SettingsVariable<std::string>& alert_location_method() const; | ||||||
|    SettingsVariable<double>&      alert_latitude() const; |    SettingsVariable<double>&      alert_latitude() const; | ||||||
|    SettingsVariable<double>&      alert_longitude() const; |    SettingsVariable<double>&      alert_longitude() const; | ||||||
|  |    SettingsVariable<double>&      alert_radius() const; | ||||||
|  |    SettingsVariable<std::string>& alert_radar_site() const; | ||||||
|    SettingsVariable<std::string>& alert_county() const; |    SettingsVariable<std::string>& alert_county() const; | ||||||
|    SettingsVariable<bool>& alert_enabled(awips::Phenomenon phenomenon) const; |    SettingsVariable<bool>& alert_enabled(awips::Phenomenon phenomenon) const; | ||||||
|    SettingsVariable<bool>& ignore_missing_codecs() const; |    SettingsVariable<bool>& ignore_missing_codecs() const; | ||||||
|  |  | ||||||
|  | @ -40,6 +40,7 @@ public: | ||||||
| 
 | 
 | ||||||
|    void UpdateEditWidget(); |    void UpdateEditWidget(); | ||||||
|    void UpdateResetButton(); |    void UpdateResetButton(); | ||||||
|  |    void UpdateUnitLabel(); | ||||||
| 
 | 
 | ||||||
|    SettingsInterface<T>* self_; |    SettingsInterface<T>* self_; | ||||||
| 
 | 
 | ||||||
|  | @ -49,9 +50,14 @@ public: | ||||||
|    std::unique_ptr<QObject> context_ {std::make_unique<QObject>()}; |    std::unique_ptr<QObject> context_ {std::make_unique<QObject>()}; | ||||||
|    QWidget*                 editWidget_ {nullptr}; |    QWidget*                 editWidget_ {nullptr}; | ||||||
|    QAbstractButton*         resetButton_ {nullptr}; |    QAbstractButton*         resetButton_ {nullptr}; | ||||||
|  |    QLabel*                  unitLabel_ {nullptr}; | ||||||
| 
 | 
 | ||||||
|    std::function<std::string(const T&)> mapFromValue_ {nullptr}; |    std::function<std::string(const T&)> mapFromValue_ {nullptr}; | ||||||
|    std::function<T(const std::string&)> mapToValue_ {nullptr}; |    std::function<T(const std::string&)> mapToValue_ {nullptr}; | ||||||
|  | 
 | ||||||
|  |    double                     unitScale_ {1}; | ||||||
|  |    std::optional<std::string> unitAbbreviation_ {}; | ||||||
|  |    bool                       unitEnabled_ {false}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template<class T> | template<class T> | ||||||
|  | @ -381,6 +387,11 @@ void SettingsInterface<T>::SetEditWidget(QWidget* widget) | ||||||
|             p->context_.get(), |             p->context_.get(), | ||||||
|             [this](double d) |             [this](double d) | ||||||
|             { |             { | ||||||
|  |                if (p->unitEnabled_) | ||||||
|  |                { | ||||||
|  |                   d = d / p->unitScale_; | ||||||
|  |                } | ||||||
|  | 
 | ||||||
|                const T                value  = p->variable_->GetValue(); |                const T                value  = p->variable_->GetValue(); | ||||||
|                const std::optional<T> staged = p->variable_->GetStaged(); |                const std::optional<T> staged = p->variable_->GetStaged(); | ||||||
| 
 | 
 | ||||||
|  | @ -448,6 +459,11 @@ void SettingsInterface<T>::SetResetButton(QAbstractButton* button) | ||||||
|       p->UpdateResetButton(); |       p->UpdateResetButton(); | ||||||
|    } |    } | ||||||
| } | } | ||||||
|  | template<class T> | ||||||
|  | void SettingsInterface<T>::SetUnitLabel(QLabel* label) | ||||||
|  | { | ||||||
|  |    p->unitLabel_ = label; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| template<class T> | template<class T> | ||||||
| void SettingsInterface<T>::SetMapFromValueFunction( | void SettingsInterface<T>::SetMapFromValueFunction( | ||||||
|  | @ -463,6 +479,17 @@ void SettingsInterface<T>::SetMapToValueFunction( | ||||||
|    p->mapToValue_ = function; |    p->mapToValue_ = function; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | template<class T> | ||||||
|  | void SettingsInterface<T>::SetUnit(const double&      scale, | ||||||
|  |                                    const std::string& abbreviation) | ||||||
|  | { | ||||||
|  |    p->unitScale_        = scale; | ||||||
|  |    p->unitAbbreviation_ = abbreviation; | ||||||
|  |    p->unitEnabled_      = true; | ||||||
|  |    p->UpdateEditWidget(); | ||||||
|  |    p->UpdateUnitLabel(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| template<class T> | template<class T> | ||||||
| template<class U> | template<class U> | ||||||
| void SettingsInterface<T>::Impl::SetWidgetText(U* widget, const T& currentValue) | void SettingsInterface<T>::Impl::SetWidgetText(U* widget, const T& currentValue) | ||||||
|  | @ -559,11 +586,27 @@ void SettingsInterface<T>::Impl::UpdateEditWidget() | ||||||
|    { |    { | ||||||
|       if constexpr (std::is_floating_point_v<T>) |       if constexpr (std::is_floating_point_v<T>) | ||||||
|       { |       { | ||||||
|          doubleSpinBox->setValue(static_cast<double>(currentValue)); |          double doubleValue = static_cast<double>(currentValue); | ||||||
|  |          if (unitEnabled_) | ||||||
|  |          { | ||||||
|  |             doubleValue = doubleValue * unitScale_; | ||||||
|  |          } | ||||||
|  |          doubleSpinBox->setValue(doubleValue); | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | template<class T> | ||||||
|  | void SettingsInterface<T>::Impl::UpdateUnitLabel() | ||||||
|  | { | ||||||
|  |    if (unitLabel_ == nullptr || !unitEnabled_) | ||||||
|  |    { | ||||||
|  |       return; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    unitLabel_->setText(QString::fromStdString(unitAbbreviation_.value_or(""))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| template<class T> | template<class T> | ||||||
| void SettingsInterface<T>::Impl::UpdateResetButton() | void SettingsInterface<T>::Impl::UpdateResetButton() | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -7,6 +7,8 @@ | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 | 
 | ||||||
|  | class QLabel; | ||||||
|  | 
 | ||||||
| namespace scwx | namespace scwx | ||||||
| { | { | ||||||
| namespace qt | namespace qt | ||||||
|  | @ -91,6 +93,13 @@ public: | ||||||
|     */ |     */ | ||||||
|    void SetResetButton(QAbstractButton* button) override; |    void SetResetButton(QAbstractButton* button) override; | ||||||
| 
 | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Sets the label for units from the settings dialog. | ||||||
|  |     * | ||||||
|  |     * @param label Unit label | ||||||
|  |     */ | ||||||
|  |    void SetUnitLabel(QLabel* label); | ||||||
|  | 
 | ||||||
|    /**
 |    /**
 | ||||||
|     * If the edit widget displays a different value than what is stored in the |     * If the edit widget displays a different value than what is stored in the | ||||||
|     * settings variable, a mapping function must be provided in order to convert |     * settings variable, a mapping function must be provided in order to convert | ||||||
|  | @ -109,6 +118,14 @@ public: | ||||||
|     */ |     */ | ||||||
|    void SetMapToValueFunction(std::function<T(const std::string&)> function); |    void SetMapToValueFunction(std::function<T(const std::string&)> function); | ||||||
| 
 | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Sets the unit to be used by this setting. | ||||||
|  |     * | ||||||
|  |     * @param scale The radio of the current unit to the base unit | ||||||
|  |     * @param abbreviation The abreviation to be displayed | ||||||
|  |     */ | ||||||
|  |    void SetUnit(const double& scale, const std::string& abbreviation); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|    class Impl; |    class Impl; | ||||||
|    std::unique_ptr<Impl> p; |    std::unique_ptr<Impl> p; | ||||||
|  |  | ||||||
|  | @ -26,16 +26,20 @@ public: | ||||||
|          types::GetOtherUnitsName(types::OtherUnits::Default); |          types::GetOtherUnitsName(types::OtherUnits::Default); | ||||||
|       std::string defaultSpeedUnitsValue = |       std::string defaultSpeedUnitsValue = | ||||||
|          types::GetSpeedUnitsName(types::SpeedUnits::Knots); |          types::GetSpeedUnitsName(types::SpeedUnits::Knots); | ||||||
|  |       std::string defaultDistanceUnitsValue = | ||||||
|  |          types::GetDistanceUnitsName(types::DistanceUnits::Miles); | ||||||
| 
 | 
 | ||||||
|       boost::to_lower(defaultAccumulationUnitsValue); |       boost::to_lower(defaultAccumulationUnitsValue); | ||||||
|       boost::to_lower(defaultEchoTopsUnitsValue); |       boost::to_lower(defaultEchoTopsUnitsValue); | ||||||
|       boost::to_lower(defaultOtherUnitsValue); |       boost::to_lower(defaultOtherUnitsValue); | ||||||
|       boost::to_lower(defaultSpeedUnitsValue); |       boost::to_lower(defaultSpeedUnitsValue); | ||||||
|  |       boost::to_lower(defaultDistanceUnitsValue); | ||||||
| 
 | 
 | ||||||
|       accumulationUnits_.SetDefault(defaultAccumulationUnitsValue); |       accumulationUnits_.SetDefault(defaultAccumulationUnitsValue); | ||||||
|       echoTopsUnits_.SetDefault(defaultEchoTopsUnitsValue); |       echoTopsUnits_.SetDefault(defaultEchoTopsUnitsValue); | ||||||
|       otherUnits_.SetDefault(defaultOtherUnitsValue); |       otherUnits_.SetDefault(defaultOtherUnitsValue); | ||||||
|       speedUnits_.SetDefault(defaultSpeedUnitsValue); |       speedUnits_.SetDefault(defaultSpeedUnitsValue); | ||||||
|  |       distanceUnits_.SetDefault(defaultDistanceUnitsValue); | ||||||
| 
 | 
 | ||||||
|       accumulationUnits_.SetValidator( |       accumulationUnits_.SetValidator( | ||||||
|          SCWX_SETTINGS_ENUM_VALIDATOR(types::AccumulationUnits, |          SCWX_SETTINGS_ENUM_VALIDATOR(types::AccumulationUnits, | ||||||
|  | @ -53,6 +57,10 @@ public: | ||||||
|          SCWX_SETTINGS_ENUM_VALIDATOR(types::SpeedUnits, |          SCWX_SETTINGS_ENUM_VALIDATOR(types::SpeedUnits, | ||||||
|                                       types::SpeedUnitsIterator(), |                                       types::SpeedUnitsIterator(), | ||||||
|                                       types::GetSpeedUnitsName)); |                                       types::GetSpeedUnitsName)); | ||||||
|  |       distanceUnits_.SetValidator( | ||||||
|  |          SCWX_SETTINGS_ENUM_VALIDATOR(types::DistanceUnits, | ||||||
|  |                                       types::DistanceUnitsIterator(), | ||||||
|  |                                       types::GetDistanceUnitsName)); | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    ~Impl() {} |    ~Impl() {} | ||||||
|  | @ -61,6 +69,7 @@ public: | ||||||
|    SettingsVariable<std::string> echoTopsUnits_ {"echo_tops_units"}; |    SettingsVariable<std::string> echoTopsUnits_ {"echo_tops_units"}; | ||||||
|    SettingsVariable<std::string> otherUnits_ {"other_units"}; |    SettingsVariable<std::string> otherUnits_ {"other_units"}; | ||||||
|    SettingsVariable<std::string> speedUnits_ {"speed_units"}; |    SettingsVariable<std::string> speedUnits_ {"speed_units"}; | ||||||
|  |    SettingsVariable<std::string> distanceUnits_ {"distance_units"}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| UnitSettings::UnitSettings() : | UnitSettings::UnitSettings() : | ||||||
|  | @ -69,7 +78,8 @@ UnitSettings::UnitSettings() : | ||||||
|    RegisterVariables({&p->accumulationUnits_, |    RegisterVariables({&p->accumulationUnits_, | ||||||
|                       &p->echoTopsUnits_, |                       &p->echoTopsUnits_, | ||||||
|                       &p->otherUnits_, |                       &p->otherUnits_, | ||||||
|                       &p->speedUnits_}); |                       &p->speedUnits_, | ||||||
|  |                       &p->distanceUnits_}); | ||||||
|    SetDefaults(); |    SetDefaults(); | ||||||
| } | } | ||||||
| UnitSettings::~UnitSettings() = default; | UnitSettings::~UnitSettings() = default; | ||||||
|  | @ -97,6 +107,11 @@ SettingsVariable<std::string>& UnitSettings::speed_units() const | ||||||
|    return p->speedUnits_; |    return p->speedUnits_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | SettingsVariable<std::string>& UnitSettings::distance_units() const | ||||||
|  | { | ||||||
|  |    return p->distanceUnits_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| UnitSettings& UnitSettings::Instance() | UnitSettings& UnitSettings::Instance() | ||||||
| { | { | ||||||
|    static UnitSettings generalSettings_; |    static UnitSettings generalSettings_; | ||||||
|  | @ -108,7 +123,8 @@ bool operator==(const UnitSettings& lhs, const UnitSettings& rhs) | ||||||
|    return (lhs.p->accumulationUnits_ == rhs.p->accumulationUnits_ && |    return (lhs.p->accumulationUnits_ == rhs.p->accumulationUnits_ && | ||||||
|            lhs.p->echoTopsUnits_ == rhs.p->echoTopsUnits_ && |            lhs.p->echoTopsUnits_ == rhs.p->echoTopsUnits_ && | ||||||
|            lhs.p->otherUnits_ == rhs.p->otherUnits_ && |            lhs.p->otherUnits_ == rhs.p->otherUnits_ && | ||||||
|            lhs.p->speedUnits_ == rhs.p->speedUnits_); |            lhs.p->speedUnits_ == rhs.p->speedUnits_ && | ||||||
|  |            lhs.p->distanceUnits_ == rhs.p->distanceUnits_); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace settings
 | } // namespace settings
 | ||||||
|  |  | ||||||
|  | @ -29,6 +29,7 @@ public: | ||||||
|    SettingsVariable<std::string>& echo_tops_units() const; |    SettingsVariable<std::string>& echo_tops_units() const; | ||||||
|    SettingsVariable<std::string>& other_units() const; |    SettingsVariable<std::string>& other_units() const; | ||||||
|    SettingsVariable<std::string>& speed_units() const; |    SettingsVariable<std::string>& speed_units() const; | ||||||
|  |    SettingsVariable<std::string>& distance_units() const; | ||||||
| 
 | 
 | ||||||
|    static UnitSettings& Instance(); |    static UnitSettings& Instance(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ namespace types | ||||||
| static const std::unordered_map<LocationMethod, std::string> | static const std::unordered_map<LocationMethod, std::string> | ||||||
|    locationMethodName_ {{LocationMethod::Fixed, "Fixed"}, |    locationMethodName_ {{LocationMethod::Fixed, "Fixed"}, | ||||||
|                         {LocationMethod::Track, "Track"}, |                         {LocationMethod::Track, "Track"}, | ||||||
|  |                         {LocationMethod::RadarSite, "Radar Site"}, | ||||||
|                         {LocationMethod::County, "County"}, |                         {LocationMethod::County, "County"}, | ||||||
|                         {LocationMethod::All, "All"}, |                         {LocationMethod::All, "All"}, | ||||||
|                         {LocationMethod::Unknown, "?"}}; |                         {LocationMethod::Unknown, "?"}}; | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ enum class LocationMethod | ||||||
| { | { | ||||||
|    Fixed, |    Fixed, | ||||||
|    Track, |    Track, | ||||||
|  |    RadarSite, | ||||||
|    County, |    County, | ||||||
|    All, |    All, | ||||||
|    Unknown |    Unknown | ||||||
|  |  | ||||||
|  | @ -89,12 +89,35 @@ static const std::unordered_map<SpeedUnits, float> speedUnitsScale_ { | ||||||
|    {SpeedUnits::User, 1.0f}, |    {SpeedUnits::User, 1.0f}, | ||||||
|    {SpeedUnits::Unknown, 1.0f}}; |    {SpeedUnits::Unknown, 1.0f}}; | ||||||
| 
 | 
 | ||||||
|  | static const std::unordered_map<DistanceUnits, std::string> | ||||||
|  |    distanceUnitsAbbreviation_ {{DistanceUnits::Kilometers, "km"}, | ||||||
|  |                                {DistanceUnits::Miles, "mi"}, | ||||||
|  |                                {DistanceUnits::User, ""}, | ||||||
|  |                                {DistanceUnits::Unknown, ""}}; | ||||||
|  | 
 | ||||||
|  | static const std::unordered_map<DistanceUnits, std::string> distanceUnitsName_ { | ||||||
|  |    {DistanceUnits::Kilometers, "Kilometers"}, | ||||||
|  |    {DistanceUnits::Miles, "Miles"}, | ||||||
|  |    {DistanceUnits::User, "User-defined"}, | ||||||
|  |    {DistanceUnits::Unknown, "?"}}; | ||||||
|  | 
 | ||||||
|  | static constexpr auto distanceUnitsBase_ = units::kilometers<double> {1.0f}; | ||||||
|  | static const std::unordered_map<DistanceUnits, double> distanceUnitsScale_ { | ||||||
|  |    {DistanceUnits::Kilometers, | ||||||
|  |     (distanceUnitsBase_ / units::kilometers<double> {1.0f})}, | ||||||
|  |    {DistanceUnits::Miles, | ||||||
|  |     (distanceUnitsBase_ / units::miles<double> {1.0f})}, | ||||||
|  |    {DistanceUnits::User, 1.0f}, | ||||||
|  |    {DistanceUnits::Unknown, 1.0f}}; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| SCWX_GET_ENUM(AccumulationUnits, | SCWX_GET_ENUM(AccumulationUnits, | ||||||
|               GetAccumulationUnitsFromName, |               GetAccumulationUnitsFromName, | ||||||
|               accumulationUnitsName_) |               accumulationUnitsName_) | ||||||
| SCWX_GET_ENUM(EchoTopsUnits, GetEchoTopsUnitsFromName, echoTopsUnitsName_) | SCWX_GET_ENUM(EchoTopsUnits, GetEchoTopsUnitsFromName, echoTopsUnitsName_) | ||||||
| SCWX_GET_ENUM(OtherUnits, GetOtherUnitsFromName, otherUnitsName_) | SCWX_GET_ENUM(OtherUnits, GetOtherUnitsFromName, otherUnitsName_) | ||||||
| SCWX_GET_ENUM(SpeedUnits, GetSpeedUnitsFromName, speedUnitsName_) | SCWX_GET_ENUM(SpeedUnits, GetSpeedUnitsFromName, speedUnitsName_) | ||||||
|  | SCWX_GET_ENUM(DistanceUnits, GetDistanceUnitsFromName, distanceUnitsName_) | ||||||
| 
 | 
 | ||||||
| const std::string& GetAccumulationUnitsAbbreviation(AccumulationUnits units) | const std::string& GetAccumulationUnitsAbbreviation(AccumulationUnits units) | ||||||
| { | { | ||||||
|  | @ -146,6 +169,21 @@ float GetSpeedUnitsScale(SpeedUnits units) | ||||||
|    return speedUnitsScale_.at(units); |    return speedUnitsScale_.at(units); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | const std::string& GetDistanceUnitsAbbreviation(DistanceUnits units) | ||||||
|  | { | ||||||
|  |    return distanceUnitsAbbreviation_.at(units); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const std::string& GetDistanceUnitsName(DistanceUnits units) | ||||||
|  | { | ||||||
|  |    return distanceUnitsName_.at(units); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double GetDistanceUnitsScale(DistanceUnits units) | ||||||
|  | { | ||||||
|  |    return distanceUnitsScale_.at(units); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace types
 | } // namespace types
 | ||||||
| } // namespace qt
 | } // namespace qt
 | ||||||
| } // namespace scwx
 | } // namespace scwx
 | ||||||
|  |  | ||||||
|  | @ -56,6 +56,17 @@ typedef scwx::util:: | ||||||
|    Iterator<SpeedUnits, SpeedUnits::KilometersPerHour, SpeedUnits::User> |    Iterator<SpeedUnits, SpeedUnits::KilometersPerHour, SpeedUnits::User> | ||||||
|       SpeedUnitsIterator; |       SpeedUnitsIterator; | ||||||
| 
 | 
 | ||||||
|  | enum class DistanceUnits | ||||||
|  | { | ||||||
|  |    Kilometers, | ||||||
|  |    Miles, | ||||||
|  |    User, | ||||||
|  |    Unknown | ||||||
|  | }; | ||||||
|  | typedef scwx::util:: | ||||||
|  |    Iterator<DistanceUnits, DistanceUnits::Kilometers, DistanceUnits::User> | ||||||
|  |       DistanceUnitsIterator; | ||||||
|  | 
 | ||||||
| const std::string& GetAccumulationUnitsAbbreviation(AccumulationUnits units); | const std::string& GetAccumulationUnitsAbbreviation(AccumulationUnits units); | ||||||
| const std::string& GetAccumulationUnitsName(AccumulationUnits units); | const std::string& GetAccumulationUnitsName(AccumulationUnits units); | ||||||
| AccumulationUnits  GetAccumulationUnitsFromName(const std::string& name); | AccumulationUnits  GetAccumulationUnitsFromName(const std::string& name); | ||||||
|  | @ -74,6 +85,11 @@ const std::string& GetSpeedUnitsName(SpeedUnits units); | ||||||
| SpeedUnits         GetSpeedUnitsFromName(const std::string& name); | SpeedUnits         GetSpeedUnitsFromName(const std::string& name); | ||||||
| float              GetSpeedUnitsScale(SpeedUnits units); | float              GetSpeedUnitsScale(SpeedUnits units); | ||||||
| 
 | 
 | ||||||
|  | const std::string& GetDistanceUnitsAbbreviation(DistanceUnits units); | ||||||
|  | const std::string& GetDistanceUnitsName(DistanceUnits units); | ||||||
|  | DistanceUnits      GetDistanceUnitsFromName(const std::string& name); | ||||||
|  | double             GetDistanceUnitsScale(DistanceUnits units); | ||||||
|  | 
 | ||||||
| } // namespace types
 | } // namespace types
 | ||||||
| } // namespace qt
 | } // namespace qt
 | ||||||
| } // namespace scwx
 | } // namespace scwx
 | ||||||
|  |  | ||||||
|  | @ -102,6 +102,16 @@ public: | ||||||
|                               types::GetSpeedUnitsName); |                               types::GetSpeedUnitsName); | ||||||
|       AddRow(speedUnits_, "Speed", speedComboBox); |       AddRow(speedUnits_, "Speed", speedComboBox); | ||||||
| 
 | 
 | ||||||
|  |       QComboBox* distanceComboBox = new QComboBox(self); | ||||||
|  |       distanceComboBox->setSizePolicy(QSizePolicy::Expanding, | ||||||
|  |                                    QSizePolicy::Preferred); | ||||||
|  |       distanceUnits_.SetSettingsVariable(unitSettings.distance_units()); | ||||||
|  |       SCWX_SETTINGS_COMBO_BOX(distanceUnits_, | ||||||
|  |                               distanceComboBox, | ||||||
|  |                               types::DistanceUnitsIterator(), | ||||||
|  |                               types::GetDistanceUnitsName); | ||||||
|  |       AddRow(distanceUnits_, "Distance", distanceComboBox); | ||||||
|  | 
 | ||||||
|       QComboBox* otherComboBox = new QComboBox(self); |       QComboBox* otherComboBox = new QComboBox(self); | ||||||
|       otherComboBox->setSizePolicy(QSizePolicy::Expanding, |       otherComboBox->setSizePolicy(QSizePolicy::Expanding, | ||||||
|                                    QSizePolicy::Preferred); |                                    QSizePolicy::Preferred); | ||||||
|  | @ -127,6 +137,7 @@ public: | ||||||
|    settings::SettingsInterface<std::string> echoTopsUnits_ {}; |    settings::SettingsInterface<std::string> echoTopsUnits_ {}; | ||||||
|    settings::SettingsInterface<std::string> otherUnits_ {}; |    settings::SettingsInterface<std::string> otherUnits_ {}; | ||||||
|    settings::SettingsInterface<std::string> speedUnits_ {}; |    settings::SettingsInterface<std::string> speedUnits_ {}; | ||||||
|  |    settings::SettingsInterface<std::string> distanceUnits_ {}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| UnitSettingsWidget::UnitSettingsWidget(QWidget* parent) : | UnitSettingsWidget::UnitSettingsWidget(QWidget* parent) : | ||||||
|  |  | ||||||
|  | @ -14,12 +14,14 @@ | ||||||
| #include <scwx/qt/settings/palette_settings.hpp> | #include <scwx/qt/settings/palette_settings.hpp> | ||||||
| #include <scwx/qt/settings/settings_interface.hpp> | #include <scwx/qt/settings/settings_interface.hpp> | ||||||
| #include <scwx/qt/settings/text_settings.hpp> | #include <scwx/qt/settings/text_settings.hpp> | ||||||
|  | #include <scwx/qt/settings/unit_settings.hpp> | ||||||
| #include <scwx/qt/types/alert_types.hpp> | #include <scwx/qt/types/alert_types.hpp> | ||||||
| #include <scwx/qt/types/font_types.hpp> | #include <scwx/qt/types/font_types.hpp> | ||||||
| #include <scwx/qt/types/location_types.hpp> | #include <scwx/qt/types/location_types.hpp> | ||||||
| #include <scwx/qt/types/qt_types.hpp> | #include <scwx/qt/types/qt_types.hpp> | ||||||
| #include <scwx/qt/types/text_types.hpp> | #include <scwx/qt/types/text_types.hpp> | ||||||
| #include <scwx/qt/types/time_types.hpp> | #include <scwx/qt/types/time_types.hpp> | ||||||
|  | #include <scwx/qt/types/unit_types.hpp> | ||||||
| #include <scwx/qt/ui/county_dialog.hpp> | #include <scwx/qt/ui/county_dialog.hpp> | ||||||
| #include <scwx/qt/ui/radar_site_dialog.hpp> | #include <scwx/qt/ui/radar_site_dialog.hpp> | ||||||
| #include <scwx/qt/ui/serial_port_dialog.hpp> | #include <scwx/qt/ui/serial_port_dialog.hpp> | ||||||
|  | @ -103,6 +105,7 @@ public: | ||||||
|    explicit SettingsDialogImpl(SettingsDialog* self) : |    explicit SettingsDialogImpl(SettingsDialog* self) : | ||||||
|        self_ {self}, |        self_ {self}, | ||||||
|        radarSiteDialog_ {new RadarSiteDialog(self)}, |        radarSiteDialog_ {new RadarSiteDialog(self)}, | ||||||
|  |        alertAudioRadarSiteDialog_ {new RadarSiteDialog(self)}, | ||||||
|        gpsSourceDialog_ {new SerialPortDialog(self)}, |        gpsSourceDialog_ {new SerialPortDialog(self)}, | ||||||
|        countyDialog_ {new CountyDialog(self)}, |        countyDialog_ {new CountyDialog(self)}, | ||||||
|        fontDialog_ {new QFontDialog(self)}, |        fontDialog_ {new QFontDialog(self)}, | ||||||
|  | @ -134,6 +137,8 @@ public: | ||||||
|           &alertAudioLocationMethod_, |           &alertAudioLocationMethod_, | ||||||
|           &alertAudioLatitude_, |           &alertAudioLatitude_, | ||||||
|           &alertAudioLongitude_, |           &alertAudioLongitude_, | ||||||
|  |           &alertAudioRadarSite_, | ||||||
|  |           &alertAudioRadius_, | ||||||
|           &alertAudioCounty_, |           &alertAudioCounty_, | ||||||
|           &hoverTextWrap_, |           &hoverTextWrap_, | ||||||
|           &tooltipMethod_, |           &tooltipMethod_, | ||||||
|  | @ -175,6 +180,7 @@ public: | ||||||
| 
 | 
 | ||||||
|    void ShowColorDialog(QLineEdit* lineEdit, QFrame* frame = nullptr); |    void ShowColorDialog(QLineEdit* lineEdit, QFrame* frame = nullptr); | ||||||
|    void UpdateRadarDialogLocation(const std::string& id); |    void UpdateRadarDialogLocation(const std::string& id); | ||||||
|  |    void UpdateAlertRadarDialogLocation(const std::string& id); | ||||||
| 
 | 
 | ||||||
|    QFont GetSelectedFont(); |    QFont GetSelectedFont(); | ||||||
|    void  SelectFontCategory(types::FontCategory fontCategory); |    void  SelectFontCategory(types::FontCategory fontCategory); | ||||||
|  | @ -199,6 +205,7 @@ public: | ||||||
| 
 | 
 | ||||||
|    SettingsDialog*   self_; |    SettingsDialog*   self_; | ||||||
|    RadarSiteDialog*  radarSiteDialog_; |    RadarSiteDialog*  radarSiteDialog_; | ||||||
|  |    RadarSiteDialog*  alertAudioRadarSiteDialog_; | ||||||
|    SerialPortDialog* gpsSourceDialog_; |    SerialPortDialog* gpsSourceDialog_; | ||||||
|    CountyDialog*     countyDialog_; |    CountyDialog*     countyDialog_; | ||||||
|    QFontDialog*      fontDialog_; |    QFontDialog*      fontDialog_; | ||||||
|  | @ -252,6 +259,8 @@ public: | ||||||
|    settings::SettingsInterface<std::string> alertAudioLocationMethod_ {}; |    settings::SettingsInterface<std::string> alertAudioLocationMethod_ {}; | ||||||
|    settings::SettingsInterface<double>      alertAudioLatitude_ {}; |    settings::SettingsInterface<double>      alertAudioLatitude_ {}; | ||||||
|    settings::SettingsInterface<double>      alertAudioLongitude_ {}; |    settings::SettingsInterface<double>      alertAudioLongitude_ {}; | ||||||
|  |    settings::SettingsInterface<std::string> alertAudioRadarSite_ {}; | ||||||
|  |    settings::SettingsInterface<double>      alertAudioRadius_ {}; | ||||||
|    settings::SettingsInterface<std::string> alertAudioCounty_ {}; |    settings::SettingsInterface<std::string> alertAudioCounty_ {}; | ||||||
| 
 | 
 | ||||||
|    std::unordered_map<awips::Phenomenon, settings::SettingsInterface<bool>> |    std::unordered_map<awips::Phenomenon, settings::SettingsInterface<bool>> | ||||||
|  | @ -339,6 +348,30 @@ void SettingsDialogImpl::ConnectSignals() | ||||||
|                        } |                        } | ||||||
|                     }); |                     }); | ||||||
| 
 | 
 | ||||||
|  |    QObject::connect(self_->ui->alertAudioRadarSiteSelectButton, | ||||||
|  |                     &QAbstractButton::clicked, | ||||||
|  |                     self_, | ||||||
|  |                     [this]() { alertAudioRadarSiteDialog_->show(); }); | ||||||
|  | 
 | ||||||
|  |    QObject::connect(alertAudioRadarSiteDialog_, | ||||||
|  |                     &RadarSiteDialog::accepted, | ||||||
|  |                     self_, | ||||||
|  |                     [this]() | ||||||
|  |                     { | ||||||
|  |                        std::string id = | ||||||
|  |                           alertAudioRadarSiteDialog_->radar_site(); | ||||||
|  | 
 | ||||||
|  |                        std::shared_ptr<config::RadarSite> radarSite = | ||||||
|  |                           config::RadarSite::Get(id); | ||||||
|  | 
 | ||||||
|  |                        if (radarSite != nullptr) | ||||||
|  |                        { | ||||||
|  |                           self_->ui->alertAudioRadarSiteComboBox | ||||||
|  |                              ->setCurrentText(QString::fromStdString( | ||||||
|  |                                 RadarSiteLabel(radarSite))); | ||||||
|  |                        } | ||||||
|  |                     }); | ||||||
|  | 
 | ||||||
|    QObject::connect(self_->ui->gpsSourceSelectButton, |    QObject::connect(self_->ui->gpsSourceSelectButton, | ||||||
|                     &QAbstractButton::clicked, |                     &QAbstractButton::clicked, | ||||||
|                     self_, |                     self_, | ||||||
|  | @ -504,11 +537,18 @@ void SettingsDialogImpl::SetupGeneralTab() | ||||||
|                 const std::shared_ptr<config::RadarSite>& b) |                 const std::shared_ptr<config::RadarSite>& b) | ||||||
|              { return a->id() < b->id(); }); |              { return a->id() < b->id(); }); | ||||||
| 
 | 
 | ||||||
|  |    // Add default and follow options to radar sites
 | ||||||
|  |    self_->ui->alertAudioRadarSiteComboBox->addItem( | ||||||
|  |       QString::fromStdString("default")); | ||||||
|  |    self_->ui->alertAudioRadarSiteComboBox->addItem( | ||||||
|  |       QString::fromStdString("follow")); | ||||||
|  | 
 | ||||||
|    // Add sorted radar sites
 |    // Add sorted radar sites
 | ||||||
|    for (std::shared_ptr<config::RadarSite>& radarSite : radarSites) |    for (std::shared_ptr<config::RadarSite>& radarSite : radarSites) | ||||||
|    { |    { | ||||||
|       QString text = QString::fromStdString(RadarSiteLabel(radarSite)); |       QString text = QString::fromStdString(RadarSiteLabel(radarSite)); | ||||||
|       self_->ui->radarSiteComboBox->addItem(text); |       self_->ui->radarSiteComboBox->addItem(text); | ||||||
|  |       self_->ui->alertAudioRadarSiteComboBox->addItem(text); | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    defaultRadarSite_.SetSettingsVariable(generalSettings.default_radar_site()); |    defaultRadarSite_.SetSettingsVariable(generalSettings.default_radar_site()); | ||||||
|  | @ -900,6 +940,12 @@ void SettingsDialogImpl::SetupAudioTab() | ||||||
| 
 | 
 | ||||||
|          bool coordinateEntryEnabled = |          bool coordinateEntryEnabled = | ||||||
|             locationMethod == types::LocationMethod::Fixed; |             locationMethod == types::LocationMethod::Fixed; | ||||||
|  |          bool radarSiteEntryEnable = | ||||||
|  |             locationMethod == types::LocationMethod::RadarSite; | ||||||
|  |          bool radiusEntryEnable = | ||||||
|  |             locationMethod == types::LocationMethod::Fixed || | ||||||
|  |             locationMethod == types::LocationMethod::Track || | ||||||
|  |             locationMethod == types::LocationMethod::RadarSite; | ||||||
|          bool countyEntryEnabled = |          bool countyEntryEnabled = | ||||||
|             locationMethod == types::LocationMethod::County; |             locationMethod == types::LocationMethod::County; | ||||||
| 
 | 
 | ||||||
|  | @ -912,6 +958,18 @@ void SettingsDialogImpl::SetupAudioTab() | ||||||
|          self_->ui->resetAlertAudioLongitudeButton->setEnabled( |          self_->ui->resetAlertAudioLongitudeButton->setEnabled( | ||||||
|             coordinateEntryEnabled); |             coordinateEntryEnabled); | ||||||
| 
 | 
 | ||||||
|  |          self_->ui->alertAudioRadarSiteComboBox->setEnabled( | ||||||
|  |             radarSiteEntryEnable); | ||||||
|  |          self_->ui->alertAudioRadarSiteSelectButton->setEnabled( | ||||||
|  |             radarSiteEntryEnable); | ||||||
|  |          self_->ui->resetAlertAudioRadarSiteButton->setEnabled( | ||||||
|  |             radarSiteEntryEnable); | ||||||
|  | 
 | ||||||
|  |          self_->ui->alertAudioRadiusSpinBox->setEnabled( | ||||||
|  |             radiusEntryEnable); | ||||||
|  |          self_->ui->resetAlertAudioRadiusButton->setEnabled( | ||||||
|  |             radiusEntryEnable); | ||||||
|  | 
 | ||||||
|          self_->ui->alertAudioCountyLineEdit->setEnabled(countyEntryEnabled); |          self_->ui->alertAudioCountyLineEdit->setEnabled(countyEntryEnabled); | ||||||
|          self_->ui->alertAudioCountySelectButton->setEnabled( |          self_->ui->alertAudioCountySelectButton->setEnabled( | ||||||
|             countyEntryEnabled); |             countyEntryEnabled); | ||||||
|  | @ -983,6 +1041,64 @@ void SettingsDialogImpl::SetupAudioTab() | ||||||
|    alertAudioLongitude_.SetResetButton( |    alertAudioLongitude_.SetResetButton( | ||||||
|       self_->ui->resetAlertAudioLongitudeButton); |       self_->ui->resetAlertAudioLongitudeButton); | ||||||
| 
 | 
 | ||||||
|  |    alertAudioRadarSite_.SetSettingsVariable(audioSettings.alert_radar_site()); | ||||||
|  |    alertAudioRadarSite_.SetMapFromValueFunction( | ||||||
|  |       [](const std::string& id) -> std::string | ||||||
|  |       { | ||||||
|  |          // Get the radar site associated with the ID
 | ||||||
|  |          std::shared_ptr<config::RadarSite> radarSite = | ||||||
|  |             config::RadarSite::Get(id); | ||||||
|  | 
 | ||||||
|  |          if (radarSite == nullptr) | ||||||
|  |          { | ||||||
|  |             // No radar site found, just return the ID
 | ||||||
|  |             return id; | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          // Add location details to the radar site
 | ||||||
|  |          return RadarSiteLabel(radarSite); | ||||||
|  |       }); | ||||||
|  |    alertAudioRadarSite_.SetMapToValueFunction( | ||||||
|  |       [](const std::string& text) -> std::string | ||||||
|  |       { | ||||||
|  |          // Find the position of location details
 | ||||||
|  |          size_t pos = text.rfind(" ("); | ||||||
|  | 
 | ||||||
|  |          if (pos == std::string::npos) | ||||||
|  |          { | ||||||
|  |             // No location details found, just return the text
 | ||||||
|  |             return text; | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          // Remove location details from the radar site
 | ||||||
|  |          return text.substr(0, pos); | ||||||
|  |       }); | ||||||
|  |    alertAudioRadarSite_.SetEditWidget(self_->ui->alertAudioRadarSiteComboBox); | ||||||
|  |    alertAudioRadarSite_.SetResetButton( | ||||||
|  |       self_->ui->resetAlertAudioRadarSiteButton); | ||||||
|  |    UpdateAlertRadarDialogLocation(audioSettings.alert_radar_site().GetValue()); | ||||||
|  | 
 | ||||||
|  |    alertAudioRadius_.SetSettingsVariable(audioSettings.alert_radius()); | ||||||
|  |    alertAudioRadius_.SetEditWidget(self_->ui->alertAudioRadiusSpinBox); | ||||||
|  |    alertAudioRadius_.SetResetButton( | ||||||
|  |       self_->ui->resetAlertAudioRadiusButton); | ||||||
|  |    alertAudioRadius_.SetUnitLabel(self_->ui->alertAudioRadiusUnitsLabel); | ||||||
|  |    auto alertAudioRadiusUpdateUnits = [this](const std::string& newValue) | ||||||
|  |    { | ||||||
|  |       types::DistanceUnits radiusUnits = | ||||||
|  |          types::GetDistanceUnitsFromName(newValue); | ||||||
|  |       double      radiusScale = types::GetDistanceUnitsScale(radiusUnits); | ||||||
|  |       std::string abbreviation = | ||||||
|  |          types::GetDistanceUnitsAbbreviation(radiusUnits); | ||||||
|  | 
 | ||||||
|  |       alertAudioRadius_.SetUnit(radiusScale, abbreviation); | ||||||
|  |    }; | ||||||
|  |    settings::UnitSettings::Instance() | ||||||
|  |       .distance_units() | ||||||
|  |       .RegisterValueStagedCallback(alertAudioRadiusUpdateUnits); | ||||||
|  |    alertAudioRadiusUpdateUnits( | ||||||
|  |       settings::UnitSettings::Instance().distance_units().GetValue()); | ||||||
|  | 
 | ||||||
|    auto& alertAudioPhenomena = types::GetAlertAudioPhenomena(); |    auto& alertAudioPhenomena = types::GetAlertAudioPhenomena(); | ||||||
|    auto  alertAudioLayout = |    auto  alertAudioLayout = | ||||||
|       static_cast<QGridLayout*>(self_->ui->alertAudioGroupBox->layout()); |       static_cast<QGridLayout*>(self_->ui->alertAudioGroupBox->layout()); | ||||||
|  | @ -1268,6 +1384,19 @@ void SettingsDialogImpl::UpdateRadarDialogLocation(const std::string& id) | ||||||
|    } |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void SettingsDialogImpl::UpdateAlertRadarDialogLocation(const std::string& id) | ||||||
|  | { | ||||||
|  |    std::shared_ptr<config::RadarSite> radarSite = config::RadarSite::Get(id); | ||||||
|  | 
 | ||||||
|  |    if (radarSite != nullptr) | ||||||
|  |    { | ||||||
|  |       alertAudioRadarSiteDialog_->HandleMapUpdate(radarSite->latitude(), | ||||||
|  |                                                   radarSite->longitude()); | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| QFont SettingsDialogImpl::GetSelectedFont() | QFont SettingsDialogImpl::GetSelectedFont() | ||||||
| { | { | ||||||
|    std::string fontFamily = fontFamilies_.at(selectedFontCategory_) |    std::string fontFamily = fontFamilies_.at(selectedFontCategory_) | ||||||
|  |  | ||||||
|  | @ -122,7 +122,7 @@ | ||||||
|           </sizepolicy> |           </sizepolicy> | ||||||
|          </property> |          </property> | ||||||
|          <property name="currentIndex"> |          <property name="currentIndex"> | ||||||
|           <number>0</number> |           <number>3</number> | ||||||
|          </property> |          </property> | ||||||
|          <widget class="QWidget" name="general"> |          <widget class="QWidget" name="general"> | ||||||
|           <layout class="QVBoxLayout" name="verticalLayout_2"> |           <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||||
|  | @ -135,9 +135,9 @@ | ||||||
|               <property name="geometry"> |               <property name="geometry"> | ||||||
|                <rect> |                <rect> | ||||||
|                 <x>0</x> |                 <x>0</x> | ||||||
|                 <y>-113</y> |                 <y>0</y> | ||||||
|                 <width>511</width> |                 <width>511</width> | ||||||
|                 <height>669</height> |                 <height>647</height> | ||||||
|                </rect> |                </rect> | ||||||
|               </property> |               </property> | ||||||
|               <layout class="QVBoxLayout" name="verticalLayout"> |               <layout class="QVBoxLayout" name="verticalLayout"> | ||||||
|  | @ -610,8 +610,8 @@ | ||||||
|                    <rect> |                    <rect> | ||||||
|                     <x>0</x> |                     <x>0</x> | ||||||
|                     <y>0</y> |                     <y>0</y> | ||||||
|                     <width>98</width> |                     <width>66</width> | ||||||
|                     <height>28</height> |                     <height>18</height> | ||||||
|                    </rect> |                    </rect> | ||||||
|                   </property> |                   </property> | ||||||
|                   <layout class="QGridLayout" name="gridLayout_3"> |                   <layout class="QGridLayout" name="gridLayout_3"> | ||||||
|  | @ -691,45 +691,6 @@ | ||||||
|               <string>Alerts</string> |               <string>Alerts</string> | ||||||
|              </property> |              </property> | ||||||
|              <layout class="QGridLayout" name="gridLayout_10"> |              <layout class="QGridLayout" name="gridLayout_10"> | ||||||
|               <item row="2" column="0"> |  | ||||||
|                <widget class="QLabel" name="label_14"> |  | ||||||
|                 <property name="text"> |  | ||||||
|                  <string>Latitude</string> |  | ||||||
|                 </property> |  | ||||||
|                </widget> |  | ||||||
|               </item> |  | ||||||
|               <item row="1" column="6"> |  | ||||||
|                <widget class="QToolButton" name="resetAlertAudioLocationMethodButton"> |  | ||||||
|                 <property name="text"> |  | ||||||
|                  <string>...</string> |  | ||||||
|                 </property> |  | ||||||
|                 <property name="icon"> |  | ||||||
|                  <iconset resource="../../../../scwx-qt.qrc"> |  | ||||||
|                   <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> |  | ||||||
|                 </property> |  | ||||||
|                </widget> |  | ||||||
|               </item> |  | ||||||
|               <item row="3" column="0"> |  | ||||||
|                <widget class="QLabel" name="label_16"> |  | ||||||
|                 <property name="text"> |  | ||||||
|                  <string>Longitude</string> |  | ||||||
|                 </property> |  | ||||||
|                </widget> |  | ||||||
|               </item> |  | ||||||
|               <item row="0" column="3"> |  | ||||||
|                <widget class="QToolButton" name="alertAudioSoundSelectButton"> |  | ||||||
|                 <property name="text"> |  | ||||||
|                  <string>...</string> |  | ||||||
|                 </property> |  | ||||||
|                </widget> |  | ||||||
|               </item> |  | ||||||
|               <item row="1" column="0"> |  | ||||||
|                <widget class="QLabel" name="label_12"> |  | ||||||
|                 <property name="text"> |  | ||||||
|                  <string>Location Method</string> |  | ||||||
|                 </property> |  | ||||||
|                </widget> |  | ||||||
|               </item> |  | ||||||
|               <item row="3" column="6"> |               <item row="3" column="6"> | ||||||
|                <widget class="QToolButton" name="resetAlertAudioLongitudeButton"> |                <widget class="QToolButton" name="resetAlertAudioLongitudeButton"> | ||||||
|                 <property name="text"> |                 <property name="text"> | ||||||
|  | @ -741,11 +702,19 @@ | ||||||
|                 </property> |                 </property> | ||||||
|                </widget> |                </widget> | ||||||
|               </item> |               </item> | ||||||
|               <item row="0" column="5"> |               <item row="5" column="1" colspan="2"> | ||||||
|                <widget class="QToolButton" name="alertAudioSoundStopButton"> |                <widget class="QDoubleSpinBox" name="alertAudioRadiusSpinBox"> | ||||||
|                 <property name="icon"> |                 <property name="decimals"> | ||||||
|                  <iconset resource="../../../../scwx-qt.qrc"> |                  <number>2</number> | ||||||
|                   <normaloff>:/res/icons/font-awesome-6/stop-solid.svg</normaloff>:/res/icons/font-awesome-6/stop-solid.svg</iconset> |                 </property> | ||||||
|  |                 <property name="minimum"> | ||||||
|  |                  <double>0.000000000000000</double> | ||||||
|  |                 </property> | ||||||
|  |                 <property name="maximum"> | ||||||
|  |                  <double>9999999999999.000000000000000</double> | ||||||
|  |                 </property> | ||||||
|  |                 <property name="singleStep"> | ||||||
|  |                  <double>0.010000000000000</double> | ||||||
|                 </property> |                 </property> | ||||||
|                </widget> |                </widget> | ||||||
|               </item> |               </item> | ||||||
|  | @ -760,6 +729,23 @@ | ||||||
|                 </property> |                 </property> | ||||||
|                </widget> |                </widget> | ||||||
|               </item> |               </item> | ||||||
|  |               <item row="1" column="1" colspan="2"> | ||||||
|  |                <widget class="QComboBox" name="alertAudioLocationMethodComboBox"> | ||||||
|  |                 <property name="sizePolicy"> | ||||||
|  |                  <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> | ||||||
|  |                   <horstretch>0</horstretch> | ||||||
|  |                   <verstretch>0</verstretch> | ||||||
|  |                  </sizepolicy> | ||||||
|  |                 </property> | ||||||
|  |                </widget> | ||||||
|  |               </item> | ||||||
|  |               <item row="7" column="0"> | ||||||
|  |                <widget class="QLabel" name="label_19"> | ||||||
|  |                 <property name="text"> | ||||||
|  |                  <string>County</string> | ||||||
|  |                 </property> | ||||||
|  |                </widget> | ||||||
|  |               </item> | ||||||
|               <item row="0" column="6"> |               <item row="0" column="6"> | ||||||
|                <widget class="QToolButton" name="resetAlertAudioSoundButton"> |                <widget class="QToolButton" name="resetAlertAudioSoundButton"> | ||||||
|                 <property name="text"> |                 <property name="text"> | ||||||
|  | @ -771,25 +757,10 @@ | ||||||
|                 </property> |                 </property> | ||||||
|                </widget> |                </widget> | ||||||
|               </item> |               </item> | ||||||
|               <item row="0" column="4"> |               <item row="5" column="3"> | ||||||
|                <widget class="QToolButton" name="alertAudioSoundTestButton"> |                <widget class="QLabel" name="alertAudioRadiusUnitsLabel"> | ||||||
|                 <property name="icon"> |  | ||||||
|                  <iconset resource="../../../../scwx-qt.qrc"> |  | ||||||
|                   <normaloff>:/res/icons/font-awesome-6/play-solid.svg</normaloff>:/res/icons/font-awesome-6/play-solid.svg</iconset> |  | ||||||
|                 </property> |  | ||||||
|                </widget> |  | ||||||
|               </item> |  | ||||||
|               <item row="0" column="0"> |  | ||||||
|                <widget class="QLabel" name="label_17"> |  | ||||||
|                 <property name="text"> |                 <property name="text"> | ||||||
|                  <string>Sound</string> |                  <string/> | ||||||
|                 </property> |  | ||||||
|                </widget> |  | ||||||
|               </item> |  | ||||||
|               <item row="4" column="0"> |  | ||||||
|                <widget class="QLabel" name="label_19"> |  | ||||||
|                 <property name="text"> |  | ||||||
|                  <string>County</string> |  | ||||||
|                 </property> |                 </property> | ||||||
|                </widget> |                </widget> | ||||||
|               </item> |               </item> | ||||||
|  | @ -809,6 +780,34 @@ | ||||||
|                 </property> |                 </property> | ||||||
|                </widget> |                </widget> | ||||||
|               </item> |               </item> | ||||||
|  |               <item row="1" column="6"> | ||||||
|  |                <widget class="QToolButton" name="resetAlertAudioLocationMethodButton"> | ||||||
|  |                 <property name="text"> | ||||||
|  |                  <string>...</string> | ||||||
|  |                 </property> | ||||||
|  |                 <property name="icon"> | ||||||
|  |                  <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |                   <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||||
|  |                 </property> | ||||||
|  |                </widget> | ||||||
|  |               </item> | ||||||
|  |               <item row="0" column="3"> | ||||||
|  |                <widget class="QToolButton" name="alertAudioSoundSelectButton"> | ||||||
|  |                 <property name="text"> | ||||||
|  |                  <string>...</string> | ||||||
|  |                 </property> | ||||||
|  |                </widget> | ||||||
|  |               </item> | ||||||
|  |               <item row="7" column="3"> | ||||||
|  |                <widget class="QToolButton" name="alertAudioCountySelectButton"> | ||||||
|  |                 <property name="text"> | ||||||
|  |                  <string>...</string> | ||||||
|  |                 </property> | ||||||
|  |                </widget> | ||||||
|  |               </item> | ||||||
|  |               <item row="0" column="1" colspan="2"> | ||||||
|  |                <widget class="QLineEdit" name="alertAudioSoundLineEdit"/> | ||||||
|  |               </item> | ||||||
|               <item row="2" column="1" colspan="2"> |               <item row="2" column="1" colspan="2"> | ||||||
|                <widget class="QDoubleSpinBox" name="alertAudioLatitudeSpinBox"> |                <widget class="QDoubleSpinBox" name="alertAudioLatitudeSpinBox"> | ||||||
|                 <property name="decimals"> |                 <property name="decimals"> | ||||||
|  | @ -825,21 +824,16 @@ | ||||||
|                 </property> |                 </property> | ||||||
|                </widget> |                </widget> | ||||||
|               </item> |               </item> | ||||||
|               <item row="1" column="1" colspan="2"> |               <item row="0" column="5"> | ||||||
|                <widget class="QComboBox" name="alertAudioLocationMethodComboBox"> |                <widget class="QToolButton" name="alertAudioSoundStopButton"> | ||||||
|                 <property name="sizePolicy"> |                 <property name="icon"> | ||||||
|                  <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> |                  <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|                   <horstretch>0</horstretch> |                   <normaloff>:/res/icons/font-awesome-6/stop-solid.svg</normaloff>:/res/icons/font-awesome-6/stop-solid.svg</iconset> | ||||||
|                   <verstretch>0</verstretch> |  | ||||||
|                  </sizepolicy> |  | ||||||
|                 </property> |                 </property> | ||||||
|                </widget> |                </widget> | ||||||
|               </item> |               </item> | ||||||
|               <item row="0" column="1" colspan="2"> |               <item row="5" column="6"> | ||||||
|                <widget class="QLineEdit" name="alertAudioSoundLineEdit"/> |                <widget class="QToolButton" name="resetAlertAudioRadiusButton"> | ||||||
|               </item> |  | ||||||
|               <item row="4" column="6"> |  | ||||||
|                <widget class="QToolButton" name="resetAlertAudioCountyButton"> |  | ||||||
|                 <property name="text"> |                 <property name="text"> | ||||||
|                  <string>...</string> |                  <string>...</string> | ||||||
|                 </property> |                 </property> | ||||||
|  | @ -849,14 +843,21 @@ | ||||||
|                 </property> |                 </property> | ||||||
|                </widget> |                </widget> | ||||||
|               </item> |               </item> | ||||||
|               <item row="4" column="3"> |               <item row="2" column="0"> | ||||||
|                <widget class="QToolButton" name="alertAudioCountySelectButton"> |                <widget class="QLabel" name="label_14"> | ||||||
|                 <property name="text"> |                 <property name="text"> | ||||||
|                  <string>...</string> |                  <string>Latitude</string> | ||||||
|                 </property> |                 </property> | ||||||
|                </widget> |                </widget> | ||||||
|               </item> |               </item> | ||||||
|               <item row="4" column="2"> |               <item row="0" column="0"> | ||||||
|  |                <widget class="QLabel" name="label_17"> | ||||||
|  |                 <property name="text"> | ||||||
|  |                  <string>Sound</string> | ||||||
|  |                 </property> | ||||||
|  |                </widget> | ||||||
|  |               </item> | ||||||
|  |               <item row="7" column="2"> | ||||||
|                <widget class="QLabel" name="alertAudioCountyLabel"> |                <widget class="QLabel" name="alertAudioCountyLabel"> | ||||||
|                 <property name="sizePolicy"> |                 <property name="sizePolicy"> | ||||||
|                  <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> |                  <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> | ||||||
|  | @ -869,7 +870,15 @@ | ||||||
|                 </property> |                 </property> | ||||||
|                </widget> |                </widget> | ||||||
|               </item> |               </item> | ||||||
|               <item row="4" column="1"> |               <item row="0" column="4"> | ||||||
|  |                <widget class="QToolButton" name="alertAudioSoundTestButton"> | ||||||
|  |                 <property name="icon"> | ||||||
|  |                  <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |                   <normaloff>:/res/icons/font-awesome-6/play-solid.svg</normaloff>:/res/icons/font-awesome-6/play-solid.svg</iconset> | ||||||
|  |                 </property> | ||||||
|  |                </widget> | ||||||
|  |               </item> | ||||||
|  |               <item row="7" column="1"> | ||||||
|                <widget class="QLineEdit" name="alertAudioCountyLineEdit"> |                <widget class="QLineEdit" name="alertAudioCountyLineEdit"> | ||||||
|                 <property name="sizePolicy"> |                 <property name="sizePolicy"> | ||||||
|                  <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> |                  <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> | ||||||
|  | @ -882,6 +891,66 @@ | ||||||
|                 </property> |                 </property> | ||||||
|                </widget> |                </widget> | ||||||
|               </item> |               </item> | ||||||
|  |               <item row="1" column="0"> | ||||||
|  |                <widget class="QLabel" name="label_12"> | ||||||
|  |                 <property name="text"> | ||||||
|  |                  <string>Location Method</string> | ||||||
|  |                 </property> | ||||||
|  |                </widget> | ||||||
|  |               </item> | ||||||
|  |               <item row="7" column="6"> | ||||||
|  |                <widget class="QToolButton" name="resetAlertAudioCountyButton"> | ||||||
|  |                 <property name="text"> | ||||||
|  |                  <string>...</string> | ||||||
|  |                 </property> | ||||||
|  |                 <property name="icon"> | ||||||
|  |                  <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |                   <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||||
|  |                 </property> | ||||||
|  |                </widget> | ||||||
|  |               </item> | ||||||
|  |               <item row="5" column="0"> | ||||||
|  |                <widget class="QLabel" name="alertAudioRadiusLabel"> | ||||||
|  |                 <property name="text"> | ||||||
|  |                  <string>Radius</string> | ||||||
|  |                 </property> | ||||||
|  |                </widget> | ||||||
|  |               </item> | ||||||
|  |               <item row="3" column="0"> | ||||||
|  |                <widget class="QLabel" name="label_16"> | ||||||
|  |                 <property name="text"> | ||||||
|  |                  <string>Longitude</string> | ||||||
|  |                 </property> | ||||||
|  |                </widget> | ||||||
|  |               </item> | ||||||
|  |               <item row="4" column="0"> | ||||||
|  |                <widget class="QLabel" name="label_28"> | ||||||
|  |                 <property name="text"> | ||||||
|  |                  <string>Radar Site</string> | ||||||
|  |                 </property> | ||||||
|  |                </widget> | ||||||
|  |               </item> | ||||||
|  |               <item row="4" column="3"> | ||||||
|  |                <widget class="QToolButton" name="alertAudioRadarSiteSelectButton"> | ||||||
|  |                 <property name="text"> | ||||||
|  |                  <string>...</string> | ||||||
|  |                 </property> | ||||||
|  |                </widget> | ||||||
|  |               </item> | ||||||
|  |               <item row="4" column="6"> | ||||||
|  |                <widget class="QToolButton" name="resetAlertAudioRadarSiteButton"> | ||||||
|  |                 <property name="text"> | ||||||
|  |                  <string>...</string> | ||||||
|  |                 </property> | ||||||
|  |                 <property name="icon"> | ||||||
|  |                  <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |                   <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||||
|  |                 </property> | ||||||
|  |                </widget> | ||||||
|  |               </item> | ||||||
|  |               <item row="4" column="1" colspan="2"> | ||||||
|  |                <widget class="QComboBox" name="alertAudioRadarSiteComboBox"/> | ||||||
|  |               </item> | ||||||
|              </layout> |              </layout> | ||||||
|             </widget> |             </widget> | ||||||
|            </item> |            </item> | ||||||
|  |  | ||||||
|  | @ -5,7 +5,9 @@ | ||||||
| 
 | 
 | ||||||
| #include <GeographicLib/Gnomonic.hpp> | #include <GeographicLib/Gnomonic.hpp> | ||||||
| #include <geos/algorithm/PointLocation.h> | #include <geos/algorithm/PointLocation.h> | ||||||
|  | #include <geos/operation/distance/DistanceOp.h> | ||||||
| #include <geos/geom/CoordinateSequence.h> | #include <geos/geom/CoordinateSequence.h> | ||||||
|  | #include <geos/geom/GeometryFactory.h> | ||||||
| 
 | 
 | ||||||
| namespace scwx | namespace scwx | ||||||
| { | { | ||||||
|  | @ -28,6 +30,40 @@ const ::GeographicLib::Geodesic& DefaultGeodesic() | ||||||
|    return geodesic_; |    return geodesic_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool GnomonicAreaContainsCenter(geos::geom::CoordinateSequence sequence) | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |    // Cannot have an area with just two points
 | ||||||
|  |    if (sequence.size() <= 2 || | ||||||
|  |        (sequence.size() == 3 && sequence.front() == sequence.back())) | ||||||
|  |    { | ||||||
|  |       return false; | ||||||
|  |    } | ||||||
|  |    bool areaContainsPoint = false; | ||||||
|  |    geos::geom::CoordinateXY zero {}; | ||||||
|  |    // If the sequence is not a ring, add the first point again for closure
 | ||||||
|  |    if (!sequence.isRing()) | ||||||
|  |    { | ||||||
|  |       sequence.add(sequence.front(), false); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // The sequence should be a ring at this point, but make sure
 | ||||||
|  |    if (sequence.isRing()) | ||||||
|  |    { | ||||||
|  |       try | ||||||
|  |       { | ||||||
|  |          areaContainsPoint = | ||||||
|  |             geos::algorithm::PointLocation::isInRing(zero, &sequence); | ||||||
|  |       } | ||||||
|  |       catch (const std::exception& ex) | ||||||
|  |       { | ||||||
|  |          logger_->warn("Invalid area sequence. {}", ex.what()); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return areaContainsPoint; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool AreaContainsPoint(const std::vector<common::Coordinate>& area, | bool AreaContainsPoint(const std::vector<common::Coordinate>& area, | ||||||
|                        const common::Coordinate&              point) |                        const common::Coordinate&              point) | ||||||
| { | { | ||||||
|  | @ -82,6 +118,12 @@ bool AreaContainsPoint(const std::vector<common::Coordinate>& area, | ||||||
|    return areaContainsPoint; |    return areaContainsPoint; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| units::angle::degrees<double> | units::angle::degrees<double> | ||||||
| GetAngle(double lat1, double lon1, double lat2, double lon2) | GetAngle(double lat1, double lon1, double lat2, double lon2) | ||||||
| { | { | ||||||
|  | @ -137,6 +179,116 @@ GetDistance(double lat1, double lon1, double lat2, double lon2) | ||||||
|    return units::length::meters<double> {distance}; |    return units::length::meters<double> {distance}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * Uses the gnomonic projection to determine if the area is in the radius. | ||||||
|  |  * | ||||||
|  |  * The basic algorithm is as follows: | ||||||
|  |  *    - Get a gnomonic projection centered on the point of the area | ||||||
|  |  *    - Find the point on the area which is closest to the center | ||||||
|  |  *    - Convert the closest point back to latitude and longitude. | ||||||
|  |  *    - Find the distance form the closest point to the point. | ||||||
|  |  * | ||||||
|  |  * The first property needed to make this work is that great circles become | ||||||
|  |  * lines in the projection, which allows the area to be converted to strait | ||||||
|  |  * lines. This is generally true for gnomic projections. | ||||||
|  |  * | ||||||
|  |  * The second property needed to make this work is that a point further away | ||||||
|  |  * on the geodesic must be further away on the projection. This means that | ||||||
|  |  * the closes point on the projection is also the closest point on the geodesic. | ||||||
|  |  * This holds for spherical geodesics and is an approximation non spherical | ||||||
|  |  * geodesics. | ||||||
|  |  * | ||||||
|  |  * This algorithm only works if the area is fully on the hemisphere centered | ||||||
|  |  * on the point. Otherwise, this falls back to centroid based distances. | ||||||
|  |  * | ||||||
|  |  * If the point is inside the area, 0 is always returned. | ||||||
|  |  */ | ||||||
|  | units::length::meters<double> | ||||||
|  | GetDistanceAreaPoint(const std::vector<common::Coordinate>& area, | ||||||
|  |                      const common::Coordinate&              point) | ||||||
|  | { | ||||||
|  |    // Ensure that the same geodesic is used here as is for the distance
 | ||||||
|  |    // calculation
 | ||||||
|  |    ::GeographicLib::Gnomonic gnomonic = | ||||||
|  |       ::GeographicLib::Gnomonic(DefaultGeodesic()); | ||||||
|  |    geos::geom::CoordinateSequence sequence {}; | ||||||
|  |    double                         x; | ||||||
|  |    double                         y; | ||||||
|  |    bool                           useCentroid = false; | ||||||
|  | 
 | ||||||
|  |    // Using a gnomonic projection with the test point as the center
 | ||||||
|  |    // latitude/longitude, the projected test point will be at (0, 0)
 | ||||||
|  |    geos::geom::CoordinateXY zero {}; | ||||||
|  | 
 | ||||||
|  |    // Create the area coordinate sequence using a gnomonic projection
 | ||||||
|  |    for (auto& areaCoordinate : area) | ||||||
|  |    { | ||||||
|  |       gnomonic.Forward(point.latitude_, | ||||||
|  |                        point.longitude_, | ||||||
|  |                        areaCoordinate.latitude_, | ||||||
|  |                        areaCoordinate.longitude_, | ||||||
|  |                        x, | ||||||
|  |                        y); | ||||||
|  |       // Check if the current point is in the hemisphere centered on the point
 | ||||||
|  |       // if not, fall back to using centroid.
 | ||||||
|  |       if (std::isnan(x) || std::isnan(y)) | ||||||
|  |       { | ||||||
|  |          useCentroid = true; | ||||||
|  |       } | ||||||
|  |       sequence.add(x, y); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    units::length::meters<double> distance; | ||||||
|  | 
 | ||||||
|  |    if (useCentroid) | ||||||
|  |    { | ||||||
|  |       common::Coordinate centroid = common::GetCentroid(area); | ||||||
|  |       distance = GetDistance(point.latitude_, | ||||||
|  |                              point.longitude_, | ||||||
|  |                              centroid.latitude_, | ||||||
|  |                              centroid.longitude_); | ||||||
|  |    } | ||||||
|  |    else if (GnomonicAreaContainsCenter(sequence)) | ||||||
|  |    { | ||||||
|  |       distance = units::length::meters<double>(0); | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       // Get the closes point on the geometry
 | ||||||
|  |       auto geometryFactory = geos::geom::GeometryFactory::getDefaultInstance(); | ||||||
|  |       auto lineString      = geometryFactory->createLineString(sequence); | ||||||
|  |       auto zeroPoint       = geometryFactory->createPoint(zero); | ||||||
|  | 
 | ||||||
|  |       std::unique_ptr<geos::geom::CoordinateSequence> closestPoints = | ||||||
|  |          geos::operation::distance::DistanceOp::nearestPoints(lineString.get(), | ||||||
|  |                                                               zeroPoint.get()); | ||||||
|  | 
 | ||||||
|  |       double closestLat; | ||||||
|  |       double closestLon; | ||||||
|  | 
 | ||||||
|  |       gnomonic.Reverse(point.latitude_, | ||||||
|  |                        point.longitude_, | ||||||
|  |                        closestPoints->getX(0), | ||||||
|  |                        closestPoints->getY(0), | ||||||
|  |                        closestLat, | ||||||
|  |                        closestLon); | ||||||
|  | 
 | ||||||
|  |       distance = GetDistance(point.latitude_, | ||||||
|  |                              point.longitude_, | ||||||
|  |                              closestLat, | ||||||
|  |                              closestLon); | ||||||
|  | 
 | ||||||
|  |    } | ||||||
|  |    return distance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool AreaInRangeOfPoint(const std::vector<common::Coordinate>& area, | ||||||
|  |                         const common::Coordinate&              point, | ||||||
|  |                         const units::length::meters<double>    distance) | ||||||
|  | { | ||||||
|  |     return GetDistanceAreaPoint(area, point) <= distance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace GeographicLib
 | } // namespace GeographicLib
 | ||||||
| } // namespace util
 | } // namespace util
 | ||||||
| } // namespace qt
 | } // namespace qt
 | ||||||
|  |  | ||||||
|  | @ -90,6 +90,37 @@ common::Coordinate GetCoordinate(const common::Coordinate& center, | ||||||
| units::length::meters<double> | units::length::meters<double> | ||||||
| GetDistance(double lat1, double lon1, double lat2, double lon2); | GetDistance(double lat1, double lon1, double lat2, double lon2); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get the distance from an area to a point. If the area is less than a quarter | ||||||
|  |  * radius of the Earth away, this is the closest distance between the area and | ||||||
|  |  * the point. Otherwise it is the distance from the centroid of the area to the | ||||||
|  |  * point. Finally, if the point is in the area, it is always 0. | ||||||
|  |  * | ||||||
|  |  * @param [in] area A vector of Coordinates representing the area | ||||||
|  |  * @param [in] point The point to check against the area | ||||||
|  |  * | ||||||
|  |  * @return true if area is inside the radius of the point | ||||||
|  |  */ | ||||||
|  | units::length::meters<double> | ||||||
|  | GetDistanceAreaPoint(const std::vector<common::Coordinate>& area, | ||||||
|  |                      const common::Coordinate&              point); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Determine if an area/ring, oriented in either direction, is within a | ||||||
|  |  * distance of a point. A point lying on the area boundary is considered to be | ||||||
|  |  * inside the area, and thus always in range. Any part of the area being inside | ||||||
|  |  * the radius counts as inside. Uses GetDistanceAreaPoint to get the distance. | ||||||
|  |  * | ||||||
|  |  * @param [in] area A vector of Coordinates representing the area | ||||||
|  |  * @param [in] point The point to check against the area | ||||||
|  |  * @param [in] distance The max distance in meters | ||||||
|  |  * | ||||||
|  |  * @return true if area is inside the radius of the point | ||||||
|  |  */ | ||||||
|  | bool AreaInRangeOfPoint(const std::vector<common::Coordinate>& area, | ||||||
|  |                         const common::Coordinate&              point, | ||||||
|  |                         const units::length::meters<double>    distance); | ||||||
|  | 
 | ||||||
| } // namespace GeographicLib
 | } // namespace GeographicLib
 | ||||||
| } // namespace util
 | } // namespace util
 | ||||||
| } // namespace qt
 | } // namespace qt
 | ||||||
|  |  | ||||||
|  | @ -1 +1 @@ | ||||||
| Subproject commit 0ea32947d6a6e39a69d931b2827056e7bc58cbba | Subproject commit 5115993596cfe6528b7a07c0b16dbd1ae16ce4df | ||||||
							
								
								
									
										69
									
								
								test/source/scwx/qt/util/geographic_lib.test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								test/source/scwx/qt/util/geographic_lib.test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,69 @@ | ||||||
|  | #include <scwx/qt/util/geographic_lib.hpp> | ||||||
|  | 
 | ||||||
|  | #include <gtest/gtest.h> | ||||||
|  | #include <boost/iostreams/copy.hpp> | ||||||
|  | #include <boost/iostreams/filtering_streambuf.hpp> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace util | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | std::vector<common::Coordinate> area = { | ||||||
|  |    common::Coordinate(37.0193692, -91.8778413), | ||||||
|  |    common::Coordinate(36.9719180, -91.3006973), | ||||||
|  |    common::Coordinate(36.7270831, -91.6815753), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | TEST(geographic_lib, area_in_range_inside) | ||||||
|  | { | ||||||
|  |    auto inside = common::Coordinate(36.9241584, -91.6425933); | ||||||
|  |    bool value; | ||||||
|  | 
 | ||||||
|  |    // inside is always true
 | ||||||
|  |    value = scwx::qt::util::GeographicLib::AreaInRangeOfPoint( | ||||||
|  |          area, inside, units::length::meters<double>(0)); | ||||||
|  |    EXPECT_EQ(value, true); | ||||||
|  |    value = scwx::qt::util::GeographicLib::AreaInRangeOfPoint( | ||||||
|  |          area, inside, units::length::meters<double>(1e6)); | ||||||
|  |    EXPECT_EQ(value, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(geographic_lib, area_in_range_near) | ||||||
|  | { | ||||||
|  |    auto near = common::Coordinate(36.8009181, -91.3922700); | ||||||
|  |    bool value; | ||||||
|  | 
 | ||||||
|  |    value = scwx::qt::util::GeographicLib::AreaInRangeOfPoint( | ||||||
|  |          area, near, units::length::meters<double>(9000)); | ||||||
|  |    EXPECT_EQ(value, false); | ||||||
|  |    value = scwx::qt::util::GeographicLib::AreaInRangeOfPoint( | ||||||
|  |          area, near, units::length::meters<double>(10100)); | ||||||
|  |    EXPECT_EQ(value, true); | ||||||
|  |    value = scwx::qt::util::GeographicLib::AreaInRangeOfPoint( | ||||||
|  |          area, near, units::length::meters<double>(1e6)); | ||||||
|  |    EXPECT_EQ(value, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(geographic_lib, area_in_range_far) | ||||||
|  | { | ||||||
|  |    auto far = common::Coordinate(37.6481966, -94.2163834); | ||||||
|  |    bool value; | ||||||
|  | 
 | ||||||
|  |    value = scwx::qt::util::GeographicLib::AreaInRangeOfPoint( | ||||||
|  |          area, far, units::length::meters<double>(9000)); | ||||||
|  |    EXPECT_EQ(value, false); | ||||||
|  |    value = scwx::qt::util::GeographicLib::AreaInRangeOfPoint( | ||||||
|  |          area, far, units::length::meters<double>(10100)); | ||||||
|  |    EXPECT_EQ(value, false); | ||||||
|  |    value = scwx::qt::util::GeographicLib::AreaInRangeOfPoint( | ||||||
|  |          area, far, units::length::meters<double>(100e3)); | ||||||
|  |    EXPECT_EQ(value, false); | ||||||
|  |    value = scwx::qt::util::GeographicLib::AreaInRangeOfPoint( | ||||||
|  |          area, far, units::length::meters<double>(300e3)); | ||||||
|  |    EXPECT_EQ(value, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } // namespace util
 | ||||||
|  | } // namespace scwx
 | ||||||
|  | @ -28,7 +28,8 @@ set(SRC_QT_MAP_TESTS source/scwx/qt/map/map_provider.test.cpp) | ||||||
| set(SRC_QT_MODEL_TESTS source/scwx/qt/model/imgui_context_model.test.cpp) | set(SRC_QT_MODEL_TESTS source/scwx/qt/model/imgui_context_model.test.cpp) | ||||||
| set(SRC_QT_SETTINGS_TESTS source/scwx/qt/settings/settings_container.test.cpp | set(SRC_QT_SETTINGS_TESTS source/scwx/qt/settings/settings_container.test.cpp | ||||||
|                           source/scwx/qt/settings/settings_variable.test.cpp) |                           source/scwx/qt/settings/settings_variable.test.cpp) | ||||||
| set(SRC_QT_UTIL_TESTS source/scwx/qt/util/q_file_input_stream.test.cpp) | set(SRC_QT_UTIL_TESTS source/scwx/qt/util/q_file_input_stream.test.cpp | ||||||
|  |                       source/scwx/qt/util/geographic_lib.test.cpp) | ||||||
| set(SRC_UTIL_TESTS source/scwx/util/float.test.cpp | set(SRC_UTIL_TESTS source/scwx/util/float.test.cpp | ||||||
|                    source/scwx/util/rangebuf.test.cpp |                    source/scwx/util/rangebuf.test.cpp | ||||||
|                    source/scwx/util/streams.test.cpp |                    source/scwx/util/streams.test.cpp | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat