From d7a413d5e74703640aa6a6f74f220d8dd50f239b Mon Sep 17 00:00:00 2001 From: James Turner Date: Fri, 29 Jun 2018 13:28:02 +0100 Subject: [PATCH] Lat-lon parsing: allow lon,lat order, and detect order Flightplan/route-manager defaults to lon,lat order. Allow the parser to detect the correct order when NSEW suffixes are provided and consistent. --- simgear/misc/strutils.cxx | 37 ++++++++++++++++++++++++++++------ simgear/misc/strutils.hxx | 13 +++++++++++- simgear/misc/strutils_test.cxx | 22 ++++++++++++++++++++ 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/simgear/misc/strutils.cxx b/simgear/misc/strutils.cxx index 8c0ff464..1de78be5 100644 --- a/simgear/misc/strutils.cxx +++ b/simgear/misc/strutils.cxx @@ -1179,7 +1179,21 @@ bool parseStringAsLatLonValue(const std::string& s, double& degrees) return true; } -bool parseStringAsGeod(const std::string& s, SGGeod* result) +namespace { + bool isLatString(const std::string &s) + { + const char lastChar = ::toupper(s.back()); + return (lastChar == 'N') || (lastChar == 'S'); + } + + bool isLonString(const std::string &s) + { + const char lastChar = ::toupper(s.back()); + return (lastChar == 'E') || (lastChar == 'W'); + } +} // of anonymous namespace + +bool parseStringAsGeod(const std::string& s, SGGeod* result, bool assumeLonLatOrder) { if (s.empty()) return false; @@ -1189,16 +1203,27 @@ bool parseStringAsGeod(const std::string& s, SGGeod* result) return false; } - double lat, lon; - if (!parseStringAsLatLonValue(s.substr(0, commaPos), lat) || - !parseStringAsLatLonValue(s.substr(commaPos+1), lon)) - { + auto termA = simplify(s.substr(0, commaPos)), + termB = simplify(s.substr(commaPos+1)); + double valueA, valueB; + if (!parseStringAsLatLonValue(termA, valueA) || !parseStringAsLatLonValue(termB, valueB)) { return false; } if (result) { - *result = SGGeod::fromDeg(lon, lat); + // explicit ordering + if (isLatString(termA) && isLonString(termB)) { + *result = SGGeod::fromDeg(valueB, valueA); + } else if (isLonString(termA) && isLatString(termB)) { + *result = SGGeod::fromDeg(valueA, valueB); + } else { + // implicit ordering + // SGGeod wants longitude, latitude + *result = assumeLonLatOrder ? SGGeod::fromDeg(valueA, valueB) + : SGGeod::fromDeg(valueB, valueA); + } } + return true; } diff --git a/simgear/misc/strutils.hxx b/simgear/misc/strutils.hxx index cc735982..f4b14558 100644 --- a/simgear/misc/strutils.hxx +++ b/simgear/misc/strutils.hxx @@ -367,8 +367,19 @@ namespace simgear { * * Supported formats: * , + * [NS],[EW] + * *'[NS],*'[EW] + * + * Latitude and longitude are parsed seperately so the formats for each + * do not need to agree. Latitude is assumed to precede longitude + * unless assumeLonLatOrder = true + * + * When NSEW characters are used, the order can be swapped and will be + * fixed correctly (longitude then latitude). */ - bool parseStringAsGeod(const std::string& string, SGGeod* result = nullptr); + bool parseStringAsGeod(const std::string& string, + SGGeod* result = nullptr, + bool assumeLonLatOrder = false); // enum values here correspond to existing lon-lat format codes inside // FlightGear (property: /sim/lon-lat-format ) diff --git a/simgear/misc/strutils_test.cxx b/simgear/misc/strutils_test.cxx index e321f13b..07ff8c1c 100644 --- a/simgear/misc/strutils_test.cxx +++ b/simgear/misc/strutils_test.cxx @@ -654,6 +654,28 @@ void test_parseGeod() SG_VERIFY(strutils::parseStringAsGeod("") == false); SG_VERIFY(strutils::parseStringAsGeod("aaaaaaaa") == false); + + // ordering tests + + // normal default order, but explicitly pass as lon,lat + // (should work) + SG_VERIFY(strutils::parseStringAsGeod("3.12345678w, 56.12345678s", &a)); + SG_CHECK_EQUAL_EP(a.getLongitudeDeg(), -3.12345678); + SG_CHECK_EQUAL_EP(a.getLatitudeDeg(), -56.12345678); + + + // different default order + // also some embedded whitespace for fun + SG_VERIFY(strutils::parseStringAsGeod(" -12 34.56,\n-45 27.89 ", &a, true)); + SG_CHECK_EQUAL_EP(a.getLongitudeDeg(), -12.576); + SG_CHECK_EQUAL_EP(a.getLatitudeDeg(), -45.464833333); + + + // differnet default order, but still set explicitly so should + // use the lat,lon order + SG_VERIFY(strutils::parseStringAsGeod("\t40 30'50\"S, 12 34'56\"W ", &a, true)); + SG_CHECK_EQUAL_EP(a.getLongitudeDeg(), -12.58222222); + SG_CHECK_EQUAL_EP(a.getLatitudeDeg(), -40.5138888); } void test_formatGeod()