254 lines
8.5 KiB
C++
254 lines
8.5 KiB
C++
#include <CesiumGeospatial/Cartographic.h>
|
|
#include <CesiumGeospatial/Ellipsoid.h>
|
|
#include <CesiumGeospatial/SimplePlanarEllipsoidCurve.h>
|
|
#include <CesiumUtility/Math.h>
|
|
|
|
#include <doctest/doctest.h>
|
|
#include <glm/common.hpp>
|
|
#include <glm/ext/vector_double3.hpp>
|
|
#include <glm/geometric.hpp>
|
|
|
|
#include <optional>
|
|
|
|
using namespace CesiumGeospatial;
|
|
using namespace CesiumUtility;
|
|
|
|
const glm::dvec3
|
|
philadelphiaEcef(1253264.69280105, -4732469.91065521, 4075112.40412297);
|
|
const glm::dvec3
|
|
tokyoEcef(-3960158.65587452, 3352568.87555906, 3697235.23506459);
|
|
// The antipodal position from the philadelphia coordinates above
|
|
const glm::dvec3 philadelphiaAntipodeEcef =
|
|
glm::dvec3(-1253369.920224856, 4732412.7444064, -4075146.2160252854);
|
|
|
|
// Equivalent to philadelphiaEcef in Longitude, Latitude, Height
|
|
const Cartographic philadelphiaLlh(
|
|
-1.3119164210487293,
|
|
0.6974930673711344,
|
|
373.64791900173714);
|
|
// Equivalent to tokyoEcef
|
|
const Cartographic
|
|
tokyoLlh(2.4390907007049445, 0.6222806863437318, 283.242432000711);
|
|
|
|
// A point above New York City
|
|
const glm::dvec3
|
|
newYorkCityEcef(1329752.6826922249, -4657851.870887691, 4140135.1399898543);
|
|
|
|
// Times Square in NYC
|
|
// This point is -10 meters below the surface of the ellipsoid (negative height)
|
|
const glm::dvec3
|
|
timesSquareEcef(1334771.9227395034, -4650343.070699833, 4142168.965635141);
|
|
|
|
TEST_CASE("SimplePlanarEllipsoidCurve::getPosition") {
|
|
SUBCASE("positions at start and end of curve are identical to input "
|
|
"coordinates") {
|
|
std::optional<SimplePlanarEllipsoidCurve> curve =
|
|
SimplePlanarEllipsoidCurve::fromEarthCenteredEarthFixedCoordinates(
|
|
Ellipsoid::WGS84,
|
|
philadelphiaEcef,
|
|
tokyoEcef);
|
|
|
|
CHECK(curve.has_value());
|
|
CHECK(Math::equalsEpsilon(
|
|
curve.value().getPosition(0.0),
|
|
philadelphiaEcef,
|
|
Math::Epsilon6));
|
|
CHECK(Math::equalsEpsilon(
|
|
curve.value().getPosition(1.0),
|
|
tokyoEcef,
|
|
Math::Epsilon6));
|
|
}
|
|
|
|
SUBCASE("all points should be coplanar") {
|
|
std::optional<SimplePlanarEllipsoidCurve> curve =
|
|
SimplePlanarEllipsoidCurve::fromEarthCenteredEarthFixedCoordinates(
|
|
Ellipsoid::WGS84,
|
|
philadelphiaEcef,
|
|
tokyoEcef);
|
|
|
|
CHECK(curve.has_value());
|
|
// needs three points to form a plane - get midpoint to make third point
|
|
glm::dvec3 midpoint = curve.value().getPosition(0.5);
|
|
glm::dvec3 planeNormal = glm::normalize(
|
|
glm::cross(philadelphiaEcef - midpoint, tokyoEcef - midpoint));
|
|
|
|
int steps = 100;
|
|
|
|
for (int i = 0; i <= steps; i++) {
|
|
double time = 1.0 / (double)steps;
|
|
double dot =
|
|
glm::abs(glm::dot(curve.value().getPosition(time), planeNormal));
|
|
// If the dot product of the point and normal are 0, they're coplanar
|
|
CHECK(Math::equalsEpsilon(dot, 0, Math::Epsilon5));
|
|
}
|
|
}
|
|
|
|
SUBCASE("should always stay above the earth") {
|
|
std::optional<SimplePlanarEllipsoidCurve> curve =
|
|
SimplePlanarEllipsoidCurve::fromEarthCenteredEarthFixedCoordinates(
|
|
Ellipsoid::WGS84,
|
|
philadelphiaEcef,
|
|
philadelphiaAntipodeEcef);
|
|
|
|
CHECK(curve.has_value());
|
|
|
|
int steps = 100;
|
|
for (int i = 0; i <= steps; i++) {
|
|
double time = 1.0 / (double)steps;
|
|
glm::dvec3 actualResult = curve.value().getPosition(time);
|
|
|
|
Cartographic cartographicResult =
|
|
Ellipsoid::WGS84.cartesianToCartographic(actualResult)
|
|
.value_or(Cartographic(0, 0, 0));
|
|
|
|
CHECK(cartographicResult.height > 0);
|
|
}
|
|
}
|
|
|
|
SUBCASE("midpoint of reverse path should be identical") {
|
|
std::optional<SimplePlanarEllipsoidCurve> forwardCurve =
|
|
SimplePlanarEllipsoidCurve::fromEarthCenteredEarthFixedCoordinates(
|
|
Ellipsoid::WGS84,
|
|
philadelphiaEcef,
|
|
tokyoEcef);
|
|
|
|
CHECK(forwardCurve.has_value());
|
|
glm::dvec3 forwardResult = forwardCurve.value().getPosition(0.5);
|
|
|
|
std::optional<SimplePlanarEllipsoidCurve> reverseCurve =
|
|
SimplePlanarEllipsoidCurve::fromEarthCenteredEarthFixedCoordinates(
|
|
Ellipsoid::WGS84,
|
|
tokyoEcef,
|
|
philadelphiaEcef);
|
|
|
|
CHECK(reverseCurve.has_value());
|
|
glm::dvec3 reverseResult = reverseCurve.value().getPosition(0.5);
|
|
|
|
CHECK(Math::equalsEpsilon(forwardResult, reverseResult, Math::Epsilon6));
|
|
}
|
|
|
|
SUBCASE("curve with same start and end point shouldn't change positions") {
|
|
std::optional<SimplePlanarEllipsoidCurve> curve =
|
|
SimplePlanarEllipsoidCurve::fromEarthCenteredEarthFixedCoordinates(
|
|
Ellipsoid::WGS84,
|
|
philadelphiaEcef,
|
|
philadelphiaEcef);
|
|
|
|
CHECK(curve.has_value());
|
|
|
|
// check a whole bunch of points along the curve to make sure it stays the
|
|
// same
|
|
const int steps = 25;
|
|
for (int i = 0; i <= steps; i++) {
|
|
glm::dvec3 result = curve.value().getPosition(i / (double)steps);
|
|
CHECK(Math::equalsEpsilon(result, philadelphiaEcef, Math::Epsilon6));
|
|
}
|
|
}
|
|
|
|
SUBCASE("should correctly interpolate height") {
|
|
double startHeight = 100.0;
|
|
double endHeight = 25.0;
|
|
|
|
std::optional<SimplePlanarEllipsoidCurve> flightPath =
|
|
SimplePlanarEllipsoidCurve::fromLongitudeLatitudeHeight(
|
|
Ellipsoid::WGS84,
|
|
Cartographic(25.0, 100.0, startHeight),
|
|
Cartographic(25.0, 100.0, endHeight));
|
|
|
|
CHECK(flightPath.has_value());
|
|
|
|
std::optional<Cartographic> position25Percent =
|
|
Ellipsoid::WGS84.cartesianToCartographic(
|
|
flightPath.value().getPosition(0.25));
|
|
std::optional<Cartographic> position50Percent =
|
|
Ellipsoid::WGS84.cartesianToCartographic(
|
|
flightPath.value().getPosition(0.5));
|
|
std::optional<Cartographic> position75Percent =
|
|
Ellipsoid::WGS84.cartesianToCartographic(
|
|
flightPath.value().getPosition(0.75));
|
|
|
|
CHECK(position25Percent.has_value());
|
|
CHECK(Math::equalsEpsilon(
|
|
position25Percent.value().height,
|
|
(endHeight - startHeight) * 0.25 + startHeight,
|
|
Math::Epsilon6));
|
|
CHECK(position50Percent.has_value());
|
|
CHECK(Math::equalsEpsilon(
|
|
position50Percent.value().height,
|
|
(endHeight - startHeight) * 0.5 + startHeight,
|
|
Math::Epsilon6));
|
|
CHECK(position75Percent.has_value());
|
|
CHECK(Math::equalsEpsilon(
|
|
position75Percent.value().height,
|
|
(endHeight - startHeight) * 0.75 + startHeight,
|
|
Math::Epsilon6));
|
|
}
|
|
|
|
// Testing a bug in SimplePlanarEllipsoidCurve where a path from a point with
|
|
// negative height to one with positive height would give results on the other
|
|
// side of the earth
|
|
SUBCASE("should correctly handle points with negative height") {
|
|
std::optional<SimplePlanarEllipsoidCurve> curve =
|
|
SimplePlanarEllipsoidCurve::fromEarthCenteredEarthFixedCoordinates(
|
|
Ellipsoid::WGS84,
|
|
timesSquareEcef,
|
|
newYorkCityEcef);
|
|
|
|
CHECK(curve.has_value());
|
|
const double expectedDistance =
|
|
glm::distance(timesSquareEcef, newYorkCityEcef);
|
|
const glm::dvec3 midpoint = curve->getPosition(0.5);
|
|
const double totalActualDistance =
|
|
glm::distance(timesSquareEcef, midpoint) +
|
|
glm::distance(newYorkCityEcef, midpoint);
|
|
|
|
CHECK(Math::equalsEpsilon(
|
|
expectedDistance,
|
|
totalActualDistance,
|
|
Math::Epsilon4));
|
|
}
|
|
}
|
|
|
|
TEST_CASE(
|
|
"SimplePlanarEllipsoidCurve::fromEarthCenteredEarthFixedCoordinates") {
|
|
SUBCASE("should fail on coordinates (0, 0, 0)") {
|
|
std::optional<SimplePlanarEllipsoidCurve> curve =
|
|
SimplePlanarEllipsoidCurve::fromEarthCenteredEarthFixedCoordinates(
|
|
Ellipsoid::WGS84,
|
|
philadelphiaEcef,
|
|
glm::dvec3(0, 0, 0));
|
|
|
|
CHECK(!curve.has_value());
|
|
}
|
|
}
|
|
|
|
TEST_CASE("SimplePlanarEllipsoidCurve::fromLongitudeLatitudeHeight") {
|
|
SUBCASE("should match results of curve created from equivalent ECEF "
|
|
"coordinates") {
|
|
std::optional<SimplePlanarEllipsoidCurve> llhCurve =
|
|
SimplePlanarEllipsoidCurve::fromLongitudeLatitudeHeight(
|
|
Ellipsoid::WGS84,
|
|
philadelphiaLlh,
|
|
tokyoLlh);
|
|
|
|
std::optional<SimplePlanarEllipsoidCurve> ecefCurve =
|
|
SimplePlanarEllipsoidCurve::fromEarthCenteredEarthFixedCoordinates(
|
|
Ellipsoid::WGS84,
|
|
philadelphiaEcef,
|
|
tokyoEcef);
|
|
|
|
CHECK(llhCurve.has_value());
|
|
CHECK(ecefCurve.has_value());
|
|
|
|
int steps = 100;
|
|
|
|
for (int i = 0; i <= steps; i++) {
|
|
double n = 1.0 / (double)steps;
|
|
CHECK(Math::equalsEpsilon(
|
|
ecefCurve.value().getPosition(n),
|
|
llhCurve.value().getPosition(n),
|
|
Math::Epsilon6));
|
|
}
|
|
}
|
|
}
|