From 5784abc1175e275b14ed6e0e091229e1b6a743e1 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sat, 15 Oct 2022 09:43:47 -0500 Subject: [PATCH] Centroid calculation for alert distance --- scwx-qt/source/scwx/qt/model/alert_model.cpp | 60 +++++++++++++------- wxdata/include/scwx/common/geographic.hpp | 11 ++++ wxdata/source/scwx/common/geographic.cpp | 39 +++++++++++++ 3 files changed, 91 insertions(+), 19 deletions(-) diff --git a/scwx-qt/source/scwx/qt/model/alert_model.cpp b/scwx-qt/source/scwx/qt/model/alert_model.cpp index 4c23d4a0..23b83af8 100644 --- a/scwx-qt/source/scwx/qt/model/alert_model.cpp +++ b/scwx-qt/source/scwx/qt/model/alert_model.cpp @@ -48,6 +48,10 @@ public: GeographicLib::Geodesic geodesic_; + std::unordered_map> + centroidMap_; std::unordered_map> @@ -173,30 +177,43 @@ void AlertModel::HandleAlert(const types::TextEventKey& alertKey) double distanceInMeters; - if (!p->textEventKeys_.contains(alertKey)) - { - beginInsertRows(QModelIndex(), 0, 0); + // Get the most recent segment for the event + auto alertMessages = + manager::TextEventManager::Instance().message_list(alertKey); + std::shared_ptr alertSegment = + alertMessages.back()->segments().back(); - p->textEventKeys_.push_back(alertKey); + if (alertSegment->codedLocation_.has_value()) + { + // Update centroid and distance + common::Coordinate centroid = + common::GetCentroid(alertSegment->codedLocation_->coordinates()); p->geodesic_.Inverse(p->previousPosition_.latitude_, p->previousPosition_.longitude_, - 0.0, // TODO: textEvent->latitude(), - 0.0, // TODO: textEvent->longitude(), + centroid.latitude_, + centroid.longitude_, distanceInMeters); - p->distanceMap_[alertKey] = distanceInMeters; + p->centroidMap_.insert_or_assign(alertKey, centroid); + p->distanceMap_.insert_or_assign(alertKey, distanceInMeters); + } + else if (!p->centroidMap_.contains(alertKey)) + { + // The alert has no location, so provide a default + p->centroidMap_.insert_or_assign(alertKey, common::Coordinate {0.0, 0.0}); + p->distanceMap_.insert_or_assign(alertKey, 0.0); + } + // Update row + if (!p->textEventKeys_.contains(alertKey)) + { + beginInsertRows(QModelIndex(), 0, 0); + p->textEventKeys_.push_back(alertKey); endInsertRows(); } else { - p->geodesic_.Inverse(p->previousPosition_.latitude_, - p->previousPosition_.longitude_, - 0.0, // TODO: textEvent->latitude(), - 0.0, // TODO: textEvent->longitude(), - distanceInMeters); - const int row = p->textEventKeys_.indexOf(alertKey); QModelIndex topLeft = createIndex(row, kFirstColumn); QModelIndex bottomRight = createIndex(row, kLastColumn); @@ -213,12 +230,17 @@ void AlertModel::HandleMapUpdate(double latitude, double longitude) for (const auto& textEvent : p->textEventKeys_) { - p->geodesic_.Inverse(latitude, - longitude, - 0.0, // TODO: textEvent->latitude(), - 0.0, // TODO: textEvent->longitude(), - distanceInMeters); - p->distanceMap_[textEvent] = distanceInMeters; + auto& centroid = p->centroidMap_.at(textEvent); + + if (centroid != common::Coordinate {0.0, 0.0}) + { + p->geodesic_.Inverse(latitude, + longitude, + centroid.latitude_, + centroid.longitude_, + distanceInMeters); + p->distanceMap_.insert_or_assign(textEvent, distanceInMeters); + } } p->previousPosition_ = {latitude, longitude}; diff --git a/wxdata/include/scwx/common/geographic.hpp b/wxdata/include/scwx/common/geographic.hpp index adf0a7f0..1318f43b 100644 --- a/wxdata/include/scwx/common/geographic.hpp +++ b/wxdata/include/scwx/common/geographic.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace scwx { @@ -43,6 +44,16 @@ enum class DistanceType Miles }; +/** + * Calculate the geographic midpoint of a set of coordinates. Uses Method A + * described at http://www.geomidpoint.com/calculation.html. + * + * @param coordinates Set of unique coordinates + * + * @return Centroid + */ +Coordinate GetCentroid(const std::vector& coordinates); + std::string GetLatitudeString(double latitude, DegreeStringType type = DegreeStringType::Decimal); diff --git a/wxdata/source/scwx/common/geographic.cpp b/wxdata/source/scwx/common/geographic.cpp index b1981df1..651cae13 100644 --- a/wxdata/source/scwx/common/geographic.cpp +++ b/wxdata/source/scwx/common/geographic.cpp @@ -2,6 +2,7 @@ #include #include +#include namespace scwx { @@ -12,6 +13,44 @@ static std::string GetDegreeString(double degrees, DegreeStringType type, const std::string& suffix); +Coordinate GetCentroid(const std::vector& coordinates) +{ + double x = 0.0; + double y = 0.0; + double z = 0.0; + + for (const Coordinate& c : coordinates) + { + // Convert latitude and longitude to radians + double latitudeRadians = c.latitude_ * std::numbers::pi / 180.0; + double longitudeRadians = c.longitude_ * std::numbers::pi / 180.0; + + // Convert latitude and longitude to Cartesian coordinates + double x1 = std::cos(latitudeRadians) * std::cos(longitudeRadians); + double y1 = std::cos(latitudeRadians) * std::sin(longitudeRadians); + double z1 = std::sin(latitudeRadians); + + // Combine with accumulators + x += x1; + y += y1; + z += z1; + } + + // Compute averages + x /= coordinates.size(); + y /= coordinates.size(); + z /= coordinates.size(); + + // Convert Cartesian coordinates back to latitude and longitude + double hyp = std::sqrt(x * x + y * y); + double latitudeRadians = std::atan2(z, hyp); + double longitudeRadians = std::atan2(y, x); + + // Return latitude and longitude in degrees + return {latitudeRadians * 180.0 / std::numbers::pi, + longitudeRadians * 180.0 / std::numbers::pi}; +} + std::string GetLatitudeString(double latitude, DegreeStringType type) { std::string suffix {};