/** * \file signtest.cpp * \brief Test treatment of +/-0 and +/-180 * * Copyright (c) Charles Karney (2022) and licensed * under the MIT/X11 License. For more information, see * https://geographiclib.sourceforge.io/ **********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include // On Centos 7, remquo(810.0, 90.0 &q) returns 90.0 with q=8. Rather than // lousing up Math.cpp with this problem we just skip the failing tests. #if defined(__GNUG__) && __GNUG__ < 11 # define BUGGY_REMQUO 1 #else # define BUGGY_REMQUO 0 #endif // Visual Studio C++ does not implement round to even, see // https://developercommunity.visualstudio.com/t/stdfixed-output-does-not-implement-round-to-even/1671088 // reported on 2022-02-20. Problem can't be reproduced at Microsoft, probably // this is some issue with the runtime library. // // Broken on my Windows desktop system vc14 thru vc16 // Fixed on my Windows laptop system vc16 and vc17 // Broken on build-open machines, vc14 and vc15+win32 // Fixed on build-open machines, vc15+x64 and vc16 // Let's assume that it's OK for vc16 and later #if defined(_MSC_VER) && _MSC_VER < 1920 # define BUGGY_ROUNDING 1 #else # define BUGGY_ROUNDING 0 #endif // use "do { } while (false)" idiom so it can be punctuated like a statement. #define REMQUO_CHECK(CMD) do { if (!BUGGY_REMQUO) { CMD; } } while (false) #define ROUNDING_CHECK(CMD) do { if (!BUGGY_ROUNDING) { CMD; } } while (false) using namespace std; using namespace GeographicLib; typedef Math::real T; static int equiv(T x, T y) { using std::isnan; // Needed for Centos 7, ubuntu 14 return ( (isnan(x) && isnan(y)) || (x == y && signbit(x) == signbit(y)) ) ? 0 : 1; } static int checkEquals(T x, T y, T d) { if (fabs(x - y) <= d) return 0; cout << "checkEquals fails: " << x << " != " << y << " +/- " << d << "\n"; return 1; } #define check(expr, r) do { \ T s = T(r), t = expr; \ if (equiv(s, t)) { \ cout << "Line " << __LINE__ << ": " << #expr \ << " != " << #r << " (" << t << ")\n"; \ ++n; \ } \ } while (false) #define checksincosd(x, s, c) do { \ T sx, cx; \ Math::sincosd(x, sx, cx); \ if (equiv(s, sx)) { \ cout << "Line " << __LINE__ << ": sin(" << x \ << ") != " << s << " (" << sx << ")\n"; \ ++n; \ } \ if (equiv(c, cx)) { \ cout << "Line " << __LINE__ << ": cos(" << x \ << ") != " << c << " (" << cx << ")\n"; \ ++n; \ } \ } while (false) #define strcheck(expr, r) do { \ string s = string(r), ss = expr; \ if (!(s == ss)) { \ cout << "Line " << __LINE__ << ": " << #expr \ << " != " << #r << " (" << ss << ")\n"; \ ++n; \ } \ } while (false) int main() { T inf = Math::infinity(), nan = Math::NaN(), eps = numeric_limits::epsilon(), ovf = 1 / Math::sq(eps), e; int n = 0; check( Math::AngRound(-eps/32), -eps/32); check( Math::AngRound(-eps/64), -0.0 ); check( Math::AngRound(- T(0)), -0.0 ); check( Math::AngRound( T(0)), +0.0 ); check( Math::AngRound( eps/64), +0.0 ); check( Math::AngRound( eps/32), +eps/32); check( Math::AngRound((1-2*eps)/64), (1-2*eps)/64); check( Math::AngRound((1-eps )/64), T(1) /64); check( Math::AngRound((1-eps/2)/64), T(1) /64); check( Math::AngRound((1-eps/4)/64), T(1) /64); check( Math::AngRound( T(1) /64), T(1) /64); check( Math::AngRound((1+eps/2)/64), T(1) /64); check( Math::AngRound((1+eps )/64), T(1) /64); check( Math::AngRound((1+2*eps)/64), (1+2*eps)/64); check( Math::AngRound((1-eps )/32), (1-eps )/32); check( Math::AngRound((1-eps/2)/32), T(1) /32); check( Math::AngRound((1-eps/4)/32), T(1) /32); check( Math::AngRound( T(1) /32), T(1) /32); check( Math::AngRound((1+eps/2)/32), T(1) /32); check( Math::AngRound((1+eps )/32), (1+eps )/32); check( Math::AngRound((1-eps )/16), (1-eps )/16); check( Math::AngRound((1-eps/2)/16), (1-eps/2)/16); check( Math::AngRound((1-eps/4)/16), T(1) /16); check( Math::AngRound( T(1) /16), T(1) /16); check( Math::AngRound((1+eps/4)/16), T(1) /16); check( Math::AngRound((1+eps/2)/16), T(1) /16); check( Math::AngRound((1+eps )/16), (1+eps )/16); check( Math::AngRound((1-eps )/ 8), (1-eps )/ 8); check( Math::AngRound((1-eps/2)/ 8), (1-eps/2)/ 8); check( Math::AngRound((1-eps/4)/ 8), T(1) / 8); check( Math::AngRound((1+eps/2)/ 8), T(1) / 8); check( Math::AngRound((1+eps )/ 8), (1+eps )/ 8); check( Math::AngRound( 1-eps ), 1-eps ); check( Math::AngRound( 1-eps/2 ), 1-eps/2 ); check( Math::AngRound( 1-eps/4 ), 1 ); check( Math::AngRound( T(1) ), 1 ); check( Math::AngRound( 1+eps/4 ), 1 ); check( Math::AngRound( 1+eps/2 ), 1 ); check( Math::AngRound( 1+eps ), 1+ eps ); check( Math::AngRound(T(90)-64*eps), 90-64*eps ); check( Math::AngRound(T(90)-32*eps), 90 ); check( Math::AngRound(T(90) ), 90 ); checksincosd(- inf , nan, nan); REMQUO_CHECK( checksincosd(-T(810), -1.0, +0.0) ); checksincosd(-T(720), -0.0, +1.0); checksincosd(-T(630), +1.0, +0.0); checksincosd(-T(540), -0.0, -1.0); checksincosd(-T(450), -1.0, +0.0); checksincosd(-T(360), -0.0, +1.0); checksincosd(-T(270), +1.0, +0.0); checksincosd(-T(180), -0.0, -1.0); checksincosd(-T( 90), -1.0, +0.0); checksincosd(-T( 0), -0.0, +1.0); checksincosd(+T( 0), +0.0, +1.0); checksincosd(+T( 90), +1.0, +0.0); checksincosd(+T(180), +0.0, -1.0); checksincosd(+T(270), -1.0, +0.0); checksincosd(+T(360), +0.0, +1.0); checksincosd(+T(450), +1.0, +0.0); checksincosd(+T(540), +0.0, -1.0); checksincosd(+T(630), -1.0, +0.0); checksincosd(+T(720), +0.0, +1.0); REMQUO_CHECK( checksincosd(+T(810), +1.0, +0.0) ); checksincosd(+ inf , nan, nan); checksincosd( nan , nan, nan); { T s1, c1, s2, c2, s3, c3; Math::sincosd(T( 9), s1, c1); Math::sincosd(T( 81), s2, c2); Math::sincosd(T(-123456789), s3, c3); int j = equiv(s1, c2) + equiv(c1, s2); REMQUO_CHECK( j += equiv(s1, s3) + equiv(c1, -c3) ); if (j) { cout << "Line " << __LINE__ << " : sincos accuracy fail\n"; ++n; } } check( Math::sind(- inf ), nan); check( Math::sind(-T(720)), -0.0); check( Math::sind(-T(540)), -0.0); check( Math::sind(-T(360)), -0.0); check( Math::sind(-T(180)), -0.0); check( Math::sind(-T( 0)), -0.0); check( Math::sind(+T( 0)), +0.0); check( Math::sind(+T(180)), +0.0); check( Math::sind(+T(360)), +0.0); check( Math::sind(+T(540)), +0.0); check( Math::sind(+T(720)), +0.0); check( Math::sind(+ inf ), nan); check( Math::cosd(- inf ), nan); REMQUO_CHECK( check( Math::cosd(-T(810)), +0.0) ); check( Math::cosd(-T(630)), +0.0); check( Math::cosd(-T(450)), +0.0); check( Math::cosd(-T(270)), +0.0); check( Math::cosd(-T( 90)), +0.0); check( Math::cosd(+T( 90)), +0.0); check( Math::cosd(+T(270)), +0.0); check( Math::cosd(+T(450)), +0.0); check( Math::cosd(+T(630)), +0.0); REMQUO_CHECK( check( Math::cosd(+T(810)), +0.0) ); check( Math::cosd(+ inf ), nan); #if !(defined(_MSC_VER) && _MSC_VER <= 1900) check( Math::tand(- inf ), nan); #endif REMQUO_CHECK( check( Math::tand(-T(810)), -ovf) ); check( Math::tand(-T(720)), -0.0); check( Math::tand(-T(630)), +ovf); check( Math::tand(-T(540)), +0.0); check( Math::tand(-T(450)), -ovf); check( Math::tand(-T(360)), -0.0); check( Math::tand(-T(270)), +ovf); check( Math::tand(-T(180)), +0.0); check( Math::tand(-T( 90)), -ovf); check( Math::tand(-T( 0)), -0.0); check( Math::tand(+T( 0)), +0.0); check( Math::tand(+T( 90)), +ovf); check( Math::tand(+T(180)), -0.0); check( Math::tand(+T(270)), -ovf); check( Math::tand(+T(360)), +0.0); check( Math::tand(+T(450)), +ovf); check( Math::tand(+T(540)), -0.0); check( Math::tand(+T(630)), -ovf); check( Math::tand(+T(720)), +0.0); REMQUO_CHECK( check( Math::tand(+T(810)), +ovf) ); #if !(defined(_MSC_VER) && _MSC_VER <= 1900) check( Math::tand(+ inf ), nan); #endif check( Math::atan2d(+T(0), -T(0)), +180 ); check( Math::atan2d(-T(0), -T(0)), -180 ); check( Math::atan2d(+T(0), +T(0)), +0.0 ); check( Math::atan2d(-T(0), +T(0)), -0.0 ); check( Math::atan2d(+T(0), -T(1)), +180 ); check( Math::atan2d(-T(0), -T(1)), -180 ); check( Math::atan2d(+T(0), +T(1)), +0.0 ); check( Math::atan2d(-T(0), +T(1)), -0.0 ); check( Math::atan2d(-T(1), +T(0)), -90 ); check( Math::atan2d(-T(1), -T(0)), -90 ); check( Math::atan2d(+T(1), +T(0)), +90 ); check( Math::atan2d(+T(1), -T(0)), +90 ); check( Math::atan2d(+T(1), -inf), +180 ); check( Math::atan2d(-T(1), -inf), -180 ); check( Math::atan2d(+T(1), +inf), +0.0 ); check( Math::atan2d(-T(1), +inf), -0.0 ); check( Math::atan2d( +inf, +T(1)), +90 ); check( Math::atan2d( +inf, -T(1)), +90 ); check( Math::atan2d( -inf, +T(1)), -90 ); check( Math::atan2d( -inf, -T(1)), -90 ); check( Math::atan2d( +inf, -inf), +135 ); check( Math::atan2d( -inf, -inf), -135 ); check( Math::atan2d( +inf, +inf), +45 ); check( Math::atan2d( -inf, +inf), -45 ); check( Math::atan2d( nan, +T(1)), nan ); check( Math::atan2d(+T(1), nan), nan ); { T s = 7e-16; if ( equiv( Math::atan2d(s, -T(1)), 180 - Math::atan2d(s, T(1)) ) ) { cout << "Line " << __LINE__ << " : atan2d accuracy fail\n"; ++n; } } check( Math::sum(+T(9), -T(9), e), +0.0 ); check( Math::sum(-T(9), +T(9), e), +0.0 ); check( Math::sum(-T(0), +T(0), e), +0.0 ); check( Math::sum(+T(0), -T(0), e), +0.0 ); check( Math::sum(-T(0), -T(0), e), -0.0 ); check( Math::sum(+T(0), +T(0), e), +0.0 ); check( Math::AngNormalize(-T(900)), -180 ); check( Math::AngNormalize(-T(720)), -0.0 ); check( Math::AngNormalize(-T(540)), -180 ); check( Math::AngNormalize(-T(360)), -0.0 ); check( Math::AngNormalize(-T(180)), -180 ); check( Math::AngNormalize( -T(0)), -0.0 ); check( Math::AngNormalize( +T(0)), +0.0 ); check( Math::AngNormalize( T(180)), +180 ); check( Math::AngNormalize( T(360)), +0.0 ); check( Math::AngNormalize( T(540)), +180 ); check( Math::AngNormalize( T(720)), +0.0 ); check( Math::AngNormalize( T(900)), +180 ); check( Math::AngDiff(+T( 0), +T( 0), e), +0.0 ); check( Math::AngDiff(+T( 0), -T( 0), e), -0.0 ); check( Math::AngDiff(-T( 0), +T( 0), e), +0.0 ); check( Math::AngDiff(-T( 0), -T( 0), e), +0.0 ); check( Math::AngDiff(+T( 5), +T(365), e), +0.0 ); check( Math::AngDiff(+T(365), +T( 5), e), -0.0 ); check( Math::AngDiff(+T( 5), +T(185), e), +180.0 ); check( Math::AngDiff(+T(185), +T( 5), e), -180.0 ); check( Math::AngDiff( +eps , +T(180), e), +180.0 ); check( Math::AngDiff( -eps , +T(180), e), -180.0 ); check( Math::AngDiff( +eps , -T(180), e), +180.0 ); check( Math::AngDiff( -eps , -T(180), e), -180.0 ); { T x = 138 + 128 * eps, y = -164; if ( equiv( Math::AngDiff(x, y), 58 - 128 * eps ) ) { cout << "Line " << __LINE__ << " : AngDiff accuracy fail\n"; ++n; } } check( Utility::val("+0"), +0.0 ); check( Utility::val("-0"), -0.0 ); check( Utility::val("nan"), nan ); check( Utility::val("+inf"), +inf ); check( Utility::val( "inf"), +inf ); check( Utility::val("-inf"), -inf ); strcheck( Utility::str( nan, 0), "nan" ); strcheck( Utility::str(-inf, 0), "-inf" ); strcheck( Utility::str(+inf, 0), "inf" ); strcheck( Utility::str(-T(3.5), 0), "-4" ); ROUNDING_CHECK( strcheck( Utility::str(-T(2.5), 0), "-2" ) ); strcheck( Utility::str(-T(1.5), 0), "-2" ); ROUNDING_CHECK( strcheck( Utility::str(-T(0.5), 0), "-0" ) ); strcheck( Utility::str(-T(0 ), 0), "-0" ); strcheck( Utility::str(+T(0 ), 0), "0" ); ROUNDING_CHECK( strcheck( Utility::str(+T(0.5), 0), "0" ) ); strcheck( Utility::str(+T(1.5), 0), "2" ); ROUNDING_CHECK( strcheck( Utility::str(+T(2.5), 0), "2" ) ); strcheck( Utility::str(+T(3.5), 0), "4" ); strcheck( Utility::str(-T(1.75), 1), "-1.8"); ROUNDING_CHECK( strcheck( Utility::str(-T(1.25), 1), "-1.2") ); strcheck( Utility::str(-T(0.75), 1), "-0.8"); ROUNDING_CHECK( strcheck( Utility::str(-T(0.25), 1), "-0.2") ); strcheck( Utility::str(-T(0 ), 1), "-0.0"); strcheck( Utility::str(+T(0 ), 1), "0.0"); ROUNDING_CHECK( strcheck( Utility::str(+T(0.25), 1), "0.2") ); strcheck( Utility::str(+T(0.75), 1), "0.8"); ROUNDING_CHECK( strcheck( Utility::str(+T(1.25), 1), "1.2") ); strcheck( Utility::str(+T(1.75), 1), "1.8"); strcheck( DMS::Encode( nan, DMS::DEGREE, 0), "nan" ); strcheck( DMS::Encode(-inf, DMS::DEGREE, 0), "-inf" ); strcheck( DMS::Encode(+inf, DMS::DEGREE, 0), "inf" ); strcheck( DMS::Encode(-T(3.5), DMS::DEGREE, 0), "-4" ); ROUNDING_CHECK( strcheck( DMS::Encode(-T(2.5), DMS::DEGREE, 0), "-2" ) ); strcheck( DMS::Encode(-T(1.5), DMS::DEGREE, 0), "-2" ); ROUNDING_CHECK( strcheck( DMS::Encode(-T(0.5), DMS::DEGREE, 0), "-0" ) ); strcheck( DMS::Encode(-T(0 ), DMS::DEGREE, 0), "-0" ); strcheck( DMS::Encode(+T(0 ), DMS::DEGREE, 0), "0" ); ROUNDING_CHECK( strcheck( DMS::Encode(+T(0.5), DMS::DEGREE, 0), "0" ) ); strcheck( DMS::Encode(+T(1.5), DMS::DEGREE, 0), "2" ); ROUNDING_CHECK( strcheck( DMS::Encode(+T(2.5), DMS::DEGREE, 0), "2" ) ); strcheck( DMS::Encode(+T(3.5), DMS::DEGREE, 0), "4" ); strcheck( DMS::Encode(-T(1.75), DMS::DEGREE, 1), "-1.8"); ROUNDING_CHECK( strcheck( DMS::Encode(-T(1.25), DMS::DEGREE, 1), "-1.2") ); strcheck( DMS::Encode(-T(0.75), DMS::DEGREE, 1), "-0.8"); ROUNDING_CHECK( strcheck( DMS::Encode(-T(0.25), DMS::DEGREE, 1), "-0.2") ); strcheck( DMS::Encode(-T(0 ), DMS::DEGREE, 1), "-0.0"); strcheck( DMS::Encode(+T(0 ), DMS::DEGREE, 1), "0.0"); ROUNDING_CHECK( strcheck( DMS::Encode(+T(0.25), DMS::DEGREE, 1), "0.2") ); strcheck( DMS::Encode(+T(0.75), DMS::DEGREE, 1), "0.8"); ROUNDING_CHECK( strcheck( DMS::Encode(+T(1.25), DMS::DEGREE, 1), "1.2") ); strcheck( DMS::Encode(+T(1.75), DMS::DEGREE, 1), "1.8"); strcheck( DMS::Encode( T(1e20), DMS::DEGREE, 0), "100000000000000000000"); strcheck( DMS::Encode( T(1e21), DMS::DEGREE, 0), "1000000000000000000000"); { T t = -(1 + 2/T(60) + T(2.99)/3600); strcheck( DMS::Encode( t,DMS::DEGREE,0,DMS::NONE ), "-1" ); strcheck( DMS::Encode( t,DMS::DEGREE,0,DMS::LATITUDE ), "01S" ); strcheck( DMS::Encode( t,DMS::DEGREE,0,DMS::LONGITUDE),"001W" ); strcheck( DMS::Encode(-t,DMS::DEGREE,0,DMS::AZIMUTH ),"001" ); strcheck( DMS::Encode( t,DMS::DEGREE,1,DMS::NONE ), "-1.0" ); strcheck( DMS::Encode( t,DMS::DEGREE,1,DMS::LATITUDE ), "01.0S" ); strcheck( DMS::Encode( t,DMS::DEGREE,1,DMS::LONGITUDE),"001.0W" ); strcheck( DMS::Encode(-t,DMS::DEGREE,1,DMS::AZIMUTH ),"001.0" ); strcheck( DMS::Encode( t,DMS::MINUTE,0,DMS::NONE ), "-1d02'" ); strcheck( DMS::Encode( t,DMS::MINUTE,0,DMS::LATITUDE ), "01d02'S" ); strcheck( DMS::Encode( t,DMS::MINUTE,0,DMS::LONGITUDE),"001d02'W" ); strcheck( DMS::Encode(-t,DMS::MINUTE,0,DMS::AZIMUTH ),"001d02'" ); strcheck( DMS::Encode( t,DMS::MINUTE,1,DMS::NONE ), "-1d02.0'" ); strcheck( DMS::Encode( t,DMS::MINUTE,1,DMS::LATITUDE ), "01d02.0'S" ); strcheck( DMS::Encode( t,DMS::MINUTE,1,DMS::LONGITUDE),"001d02.0'W" ); strcheck( DMS::Encode(-t,DMS::MINUTE,1,DMS::AZIMUTH ),"001d02.0'" ); strcheck( DMS::Encode( t,DMS::SECOND,0,DMS::NONE ), "-1d02'03\"" ); strcheck( DMS::Encode( t,DMS::SECOND,0,DMS::LATITUDE ), "01d02'03\"S" ); strcheck( DMS::Encode( t,DMS::SECOND,0,DMS::LONGITUDE),"001d02'03\"W" ); strcheck( DMS::Encode(-t,DMS::SECOND,0,DMS::AZIMUTH ),"001d02'03\"" ); strcheck( DMS::Encode( t,DMS::SECOND,1,DMS::NONE ), "-1d02'03.0\""); strcheck( DMS::Encode( t,DMS::SECOND,1,DMS::LATITUDE ), "01d02'03.0\"S"); strcheck( DMS::Encode( t,DMS::SECOND,1,DMS::LONGITUDE),"001d02'03.0\"W"); strcheck( DMS::Encode(-t,DMS::SECOND,1,DMS::AZIMUTH ),"001d02'03.0\"" ); } DMS::flag ind; check( DMS::Decode(" +0 ", ind), +0.0 ); check( DMS::Decode("-0 ", ind), -0.0 ); check( DMS::Decode(" nan", ind), nan ); check( DMS::Decode("+inf", ind), +inf ); check( DMS::Decode(" inf", ind), +inf ); check( DMS::Decode("-inf", ind), -inf ); check( DMS::Decode(" +0N", ind), +0.0 ); check( DMS::Decode("-0N ", ind), -0.0 ); check( DMS::Decode("+0S ", ind), -0.0 ); check( DMS::Decode(" -0S", ind), +0.0 ); { // azimuth of geodesic line with points on equator determined by signs of // latitude // lat1 lat2 azi1/2 T C[2][3] = { { +T(0), -T(0), 180 }, { -T(0), +T(0), 0 } }; const Geodesic& g = Geodesic::WGS84(); const GeodesicExact& ge = GeodesicExact::WGS84(); T azi1, azi2; int i = 0; for (int k = 0; k < 2; ++k) { g.Inverse(C[k][0], T(0), C[k][1], T(0), azi1, azi2); if ( equiv(azi1, C[k][2]) + equiv(azi2, C[k][2]) ) ++i; ge.Inverse(C[k][0], T(0), C[k][1], T(0), azi1, azi2); if ( equiv(azi1, C[k][2]) + equiv(azi2, C[k][2]) ) ++i; } if (i) { cout << "Line " << __LINE__ << ": Geodesic::Inverse coincident points on equator fail\n"; ++n; } } { // Does the nearly antipodal equatorial solution go north or south? // lat1 lat2 azi1 azi2 T C[2][4] = { { +T(0), +T(0), 56, 124}, { -T(0), -T(0), 124, 56} }; const Geodesic& g = Geodesic::WGS84(); const GeodesicExact& ge = GeodesicExact::WGS84(); T azi1, azi2; int i = 0; for (int k = 0; k < 2; ++k) { g.Inverse(C[k][0], T(0), C[k][1], T(179.5), azi1, azi2); i += checkEquals(azi1, C[k][2], 1) + checkEquals(azi2, C[k][3], 1); ge.Inverse(C[k][0], T(0), C[k][1], T(179.5), azi1, azi2); i += checkEquals(azi1, C[k][2], 1) + checkEquals(azi2, C[k][3], 1); } if (i) { cout << "Line " << __LINE__ << ": Geodesic::Inverse nearly antipodal points on equator fail\n"; ++n; } } { // How does the exact antipodal equatorial path go N/S + E/W // lat1 lat2 lon2 azi1 azi2 T C[4][5] = { { +T(0), +T(0), +180, +T(0), +180}, { -T(0), -T(0), +180, +180, +T(0)}, { +T(0), +T(0), -180, -T(0), -180}, { -T(0), -T(0), -180, -180, -T(0)} }; const Geodesic& g = Geodesic::WGS84(); const GeodesicExact& ge = GeodesicExact::WGS84(); T azi1, azi2; int i = 0; for (int k = 0; k < 4; ++k) { g.Inverse(C[k][0], T(0), C[k][1], C[k][2], azi1, azi2); if ( equiv(azi1, C[k][3]) + equiv(azi2, C[k][4]) ) ++i; ge.Inverse(C[k][0], T(0), C[k][1], C[k][2], azi1, azi2); if ( equiv(azi1, C[k][3]) + equiv(azi2, C[k][4]) ) ++i; } if (i) { cout << "Line " << __LINE__ << ": Geodesic::Inverse antipodal points on equator fail\n"; ++n; } } { // Antipodal points on the equator with prolate ellipsoid // lon2 azi1/2 T C[2][2] = { { +180, +90 }, { -180, -90 } }; const Geodesic g(T(6.4e6), -1/T(300)); const GeodesicExact ge(T(6.4e6), -1/T(300)); T azi1, azi2; int i = 0; for (int k = 0; k < 2; ++k) { g.Inverse(T(0), T(0), T(0), C[k][0], azi1, azi2); if ( equiv(azi1, C[k][1]) + equiv(azi2, C[k][1]) ) ++i; ge.Inverse(T(0), T(0), T(0), C[k][0], azi1, azi2); if ( equiv(azi1, C[k][1]) + equiv(azi2, C[k][1]) ) ++i; } if (i) { cout << "Line " << __LINE__ << ": Geodesic::Inverse antipodal points on equator, prolate, fail\n"; ++n; } } { // azimuths = +/-0 and +/-180 for the direct problem // azi1, lon2, azi2 T C[4][3] = { { +T(0), +180, +180 }, { -T(0), -180, -180 }, { +180 , +180, +T(0) }, { -180 , -180, -T(0) } }; const Geodesic& g = Geodesic::WGS84(); const GeodesicExact& ge = GeodesicExact::WGS84(); T lon2, azi2; int i = 0; for (int k = 0; k < 4; ++k) { T t; g.GenDirect(T(0), T(0), C[k][0], false, T(15e6), Geodesic::LONGITUDE | Geodesic::AZIMUTH | Geodesic::LONG_UNROLL, t, lon2, azi2, t, t, t, t, t); if ( equiv(lon2, C[k][1]) + equiv(azi2, C[k][2]) ) ++i; ge.GenDirect(T(0), T(0), C[k][0], false, T(15e6), Geodesic::LONGITUDE | Geodesic::AZIMUTH | Geodesic::LONG_UNROLL, t, lon2, azi2, t, t, t, t, t); if ( equiv(lon2, C[k][1]) + equiv(azi2, C[k][2]) ) ++i; } if (i) { cout << "Line " << __LINE__ << ": Geodesic::Direct azi1 = +/-0 +/-180, fail\n"; ++n; } } { // lat = +/-0 in UTMUPS::Forward // lat y northp T C[2][3] = { { +T(0), T(0), 1 }, { -T(0), 10e6, 0 } }; int i = 0; bool northp; int zone; T x, y; string mgrs; for (int k = 0; k < 2; ++k) { UTMUPS::Forward(C[k][0], T(3), zone, northp, x, y); if ( equiv(y, C[k][1]) + (northp == (C[k][2] > 0) ? 0 : 1) ) ++i; MGRS::Forward(zone, northp, x, y, 2, mgrs); if (!( mgrs == (k == 0 ? "31NEA0000" : "31MEV0099") )) ++i; MGRS::Forward(zone, northp, x, y, +T(0), 2, mgrs); if (!( mgrs == (k == 0 ? "31NEA0000" : "31MEV0099") )) ++i; MGRS::Forward(zone, northp, x, y, -T(0), 2, mgrs); if (!( mgrs == (k == 0 ? "31NEA0000" : "31MEV0099") )) ++i; } if (i) { cout << "Line " << __LINE__ << ": UTMUPS/MGRS::Forward lat = +/-0, fail\n"; ++n; } } { // Implement check on AddEdge bug: AddPoint(0,0) + // AddEdge(90, 1000) + AddEdge(0, 1000) + AddEdge(-90, 0). The area // should be 1e6. Prior to the fix it was 1e6 - A/2, where A = ellipsoid // area. // add_test (NAME Planimeter29 COMMAND Planimeter ...) const Geodesic& g = Geodesic::WGS84(); PolygonArea polygon(g); polygon.AddPoint(0, 0); polygon.AddEdge( 90, 1000); polygon.AddEdge( 0, 1000); polygon.AddEdge(-90, 1000); T perim, area; // The area should be 1e6. Prior to the fix it was 1e6 - A/2, where // A = ellipsoid area. polygon.Compute(false, true, perim, area); int i = checkEquals(area, 1000000, 0.01); if (i) { cout << "Line " << __LINE__ << ": Planimeter29 fail\n"; ++n; } } if (n) { cout << n << " failure" << (n > 1 ? "s" : "") << "\n"; return 1; } }