mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 07:50:04 +00:00
Began working on distance from location code
This commit is contained in:
parent
60aed45450
commit
9f47e0ad72
7 changed files with 198 additions and 4 deletions
|
|
@ -85,7 +85,8 @@ common::Coordinate AlertManager::Impl::CurrentCoordinate(
|
||||||
settings::AudioSettings& audioSettings = settings::AudioSettings::Instance();
|
settings::AudioSettings& audioSettings = settings::AudioSettings::Instance();
|
||||||
common::Coordinate coordinate {};
|
common::Coordinate coordinate {};
|
||||||
|
|
||||||
if (locationMethod == types::LocationMethod::Fixed)
|
if (locationMethod == types::LocationMethod::Fixed ||
|
||||||
|
locationMethod == types::LocationMethod::Radius)
|
||||||
{
|
{
|
||||||
coordinate.latitude_ = audioSettings.alert_latitude().GetValue();
|
coordinate.latitude_ = audioSettings.alert_latitude().GetValue();
|
||||||
coordinate.longitude_ = audioSettings.alert_longitude().GetValue();
|
coordinate.longitude_ = audioSettings.alert_longitude().GetValue();
|
||||||
|
|
@ -153,6 +154,15 @@ void AlertManager::Impl::HandleAlert(const types::TextEventKey& key,
|
||||||
activeAtLocation = util::GeographicLib::AreaContainsPoint(
|
activeAtLocation = util::GeographicLib::AreaContainsPoint(
|
||||||
alertCoordinates, currentCoordinate);
|
alertCoordinates, currentCoordinate);
|
||||||
}
|
}
|
||||||
|
else if (locationMethod == types::LocationMethod::Radius)
|
||||||
|
{
|
||||||
|
auto alertCoordinates = segment->codedLocation_->coordinates();
|
||||||
|
|
||||||
|
activeAtLocation = util::GeographicLib::AreaInRangeOfPoint(
|
||||||
|
alertCoordinates,
|
||||||
|
currentCoordinate,
|
||||||
|
units::length::meters<double>(1e6));
|
||||||
|
}
|
||||||
else if (locationMethod == types::LocationMethod::County)
|
else if (locationMethod == types::LocationMethod::County)
|
||||||
{
|
{
|
||||||
// Determine if the alert contains the current county
|
// Determine if the alert contains the current county
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,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::Radius, "Radius"},
|
||||||
{LocationMethod::Track, "Track"},
|
{LocationMethod::Track, "Track"},
|
||||||
{LocationMethod::County, "County"},
|
{LocationMethod::County, "County"},
|
||||||
{LocationMethod::All, "All"},
|
{LocationMethod::All, "All"},
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ namespace types
|
||||||
enum class LocationMethod
|
enum class LocationMethod
|
||||||
{
|
{
|
||||||
Fixed,
|
Fixed,
|
||||||
|
Radius,
|
||||||
Track,
|
Track,
|
||||||
County,
|
County,
|
||||||
All,
|
All,
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,16 @@
|
||||||
|
|
||||||
#include <GeographicLib/Gnomonic.hpp>
|
#include <GeographicLib/Gnomonic.hpp>
|
||||||
#include <geos/algorithm/PointLocation.h>
|
#include <geos/algorithm/PointLocation.h>
|
||||||
|
#include <geos/algorithm/distance/PointPairDistance.h>
|
||||||
|
#include <geos/algorithm/distance/DistanceToPoint.h>
|
||||||
#include <geos/geom/CoordinateSequence.h>
|
#include <geos/geom/CoordinateSequence.h>
|
||||||
|
#include <geos/geom/GeometryFactory.h>
|
||||||
|
|
||||||
namespace scwx
|
namespace scwx
|
||||||
{
|
{
|
||||||
namespace qt
|
namespace qt
|
||||||
{
|
{
|
||||||
namespace util
|
namespace util {
|
||||||
{
|
|
||||||
namespace GeographicLib
|
namespace GeographicLib
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -82,6 +84,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 +145,93 @@ GetDistance(double lat1, double lon1, double lat2, double lon2)
|
||||||
return units::length::meters<double> {distance};
|
return units::length::meters<double> {distance};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AreaInRangeOfPoint(const std::vector<common::Coordinate>& area,
|
||||||
|
const common::Coordinate& point,
|
||||||
|
const units::length::meters<double> distance)
|
||||||
|
{
|
||||||
|
// Cannot have an area with just two points
|
||||||
|
if (area.size() <= 2 || (area.size() == 3 && area.front() == area.back()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
::GeographicLib::Gnomonic gnomonic =
|
||||||
|
::GeographicLib::Gnomonic(DefaultGeodesic());
|
||||||
|
geos::geom::CoordinateSequence sequence {};
|
||||||
|
double x;
|
||||||
|
double y;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
sequence.add(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a point on the circle with the radius of the range in lat lon.
|
||||||
|
units::angle::degrees<double> angle = units::angle::degrees<double>(0);
|
||||||
|
common::Coordinate radiusPoint = GetCoordinate(point, angle, distance);
|
||||||
|
// get the radius in gnomonic projection
|
||||||
|
gnomonic.Forward(point.latitude_,
|
||||||
|
point.longitude_,
|
||||||
|
radiusPoint.latitude_,
|
||||||
|
radiusPoint.longitude_,
|
||||||
|
x,
|
||||||
|
y);
|
||||||
|
double gnomonicRadius = sqrt(x * x + y * y);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
{
|
||||||
|
if (geos::algorithm::PointLocation::isInRing(zero, &sequence))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the distance the point is from the output
|
||||||
|
geos::algorithm::distance::PointPairDistance distancePair;
|
||||||
|
auto geometryFactory =
|
||||||
|
geos::geom::GeometryFactory::getDefaultInstance();
|
||||||
|
auto linearRing = geometryFactory->createLinearRing(sequence);
|
||||||
|
auto polygon =
|
||||||
|
geometryFactory->createPolygon(std::move(linearRing));
|
||||||
|
geos::algorithm::distance::DistanceToPoint::computeDistance(*polygon,
|
||||||
|
zero,
|
||||||
|
distancePair);
|
||||||
|
if (gnomonicRadius > distancePair.getDistance())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (const std::exception&)
|
||||||
|
{
|
||||||
|
logger_->trace("Invalid area sequence");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace GeographicLib
|
} // namespace GeographicLib
|
||||||
} // namespace util
|
} // namespace util
|
||||||
} // namespace qt
|
} // namespace qt
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,23 @@ 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);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
|
|
||||||
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