From ce79b9ffa4062cc3976838cb9a5fedd31f2f3614 Mon Sep 17 00:00:00 2001 From: Patrick Valsecchi Date: Mon, 23 Feb 2015 13:38:41 +0100 Subject: [PATCH] Add support for more GPS coordinate formats. As requested in the user forum and in the mailing list, now support: - 46.473881 6.784696 (format used in XML files) - 48 51.491n 2 17.677e I was not able to handle the XML format in a generic way without making the code too ugly. So I've added an exception. Signed-off-by: Patrick Valsecchi Signed-off-by: Dirk Hohndel --- qthelper.cpp | 68 ++++++++++++++++++++++++++++------------- tests/testgpscoords.cpp | 23 ++++++++++++++ tests/testgpscoords.h | 4 +++ 3 files changed, 73 insertions(+), 22 deletions(-) diff --git a/qthelper.cpp b/qthelper.cpp index b26bdf467..34aacaf70 100644 --- a/qthelper.cpp +++ b/qthelper.cpp @@ -71,6 +71,9 @@ extern "C" const char *printGPSCoords(int lat, int lon) return strdup(result.toUtf8().data()); } +/** +* Try to parse in a generic manner a coordinate. +*/ static bool parseCoord(const QString& txt, int& pos, const QString& positives, const QString& negatives, const QString& others, double& value) @@ -92,7 +95,7 @@ static bool parseCoord(const QString& txt, int& pos, const QString& positives, numberDefined = true; posBeforeNumber = pos; pos += numberRe.cap(1).size() - 1; - } else if (positives.indexOf(txt[pos].toUpper()) >= 0) { + } else if (positives.indexOf(txt[pos]) >= 0) { if (sign != 0) return false; sign = 1; @@ -102,7 +105,7 @@ static bool parseCoord(const QString& txt, int& pos, const QString& positives, ++pos; break; } - } else if (negatives.indexOf(txt[pos].toUpper()) >= 0) { + } else if (negatives.indexOf(txt[pos]) >= 0) { if (sign != 0) { if (others.indexOf(txt[pos]) >= 0) //special case for the '-' sign => next coordinate @@ -116,10 +119,11 @@ static bool parseCoord(const QString& txt, int& pos, const QString& positives, ++pos; break; } - } else if (others.indexOf(txt[pos].toUpper()) >= 0) { + } else if (others.indexOf(txt[pos]) >= 0) { //we are at the next coordinate. break; - } else if (DEGREE_SIGNS.indexOf(txt[pos]) >= 0) { + } else if (DEGREE_SIGNS.indexOf(txt[pos]) >= 0 || + (txt[pos].isSpace() && !degreesDefined && numberDefined)) { if (!numberDefined) return false; if (degreesDefined) { @@ -131,20 +135,18 @@ static bool parseCoord(const QString& txt, int& pos, const QString& positives, value += number; numberDefined = false; degreesDefined = true; - } else if (txt[pos] == '\'') { + } else if (txt[pos] == '\'' || (txt[pos].isSpace() && !minutesDefined && numberDefined)) { if (!numberDefined || minutesDefined) return false; value += number / 60.0; numberDefined = false; minutesDefined = true; - } else if (txt[pos] == '"') { + } else if (txt[pos] == '"' || (txt[pos].isSpace() && !secondsDefined && numberDefined)) { if (!numberDefined || secondsDefined) return false; value += number / 3600.0; numberDefined = false; secondsDefined = true; - } else if (txt[pos] == ' ' || txt[pos] == '\t') { - //ignore spaces } else { return false; } @@ -152,31 +154,53 @@ static bool parseCoord(const QString& txt, int& pos, const QString& positives, } if (!degreesDefined && numberDefined) { value = number; //just a single number => degrees - numberDefined = false; - degreesDefined = true; - } - if (!degreesDefined || numberDefined) + } else if (!minutesDefined && numberDefined) { + value += number / 60.0; + } else if (!secondsDefined && numberDefined) { + value += number / 3600.0; + } else if (numberDefined) { return false; + } if (sign == -1) value *= -1.0; return true; } -bool parseGpsText(const QString &gps_text, double *latitude, double *longitude) -{ - const QString trimmed = gps_text.trimmed(); - if (trimmed.isEmpty()) { - *latitude = 0.0; - *longitude = 0.0; +/** +* Parse special coordinate formats that cannot be handled by parseCoord. +*/ +static bool parseSpecialCoords(const QString& txt, double& latitude, double& longitude) { + QRegExp xmlFormat("(-?\\d+(?:\\.\\d+)?)\\s+(-?\\d+(?:\\.\\d+)?)"); + if (xmlFormat.exactMatch(txt)) { + latitude = xmlFormat.cap(1).toDouble(); + longitude = xmlFormat.cap(2).toDouble(); return true; } - int pos = 0; + return false; +} + +bool parseGpsText(const QString &gps_text, double *latitude, double *longitude) +{ static const QString POS_LAT = QString("+N") + translate("gettextFromC", "N"); static const QString NEG_LAT = QString("-S") + translate("gettextFromC", "S"); static const QString POS_LON = QString("+E") + translate("gettextFromC", "E"); static const QString NEG_LON = QString("-W") + translate("gettextFromC", "W"); - return parseCoord(gps_text, pos, POS_LAT, NEG_LAT, POS_LON + NEG_LON, *latitude) && - parseCoord(gps_text, pos, POS_LON, NEG_LON, "", *longitude) && - pos == gps_text.size(); + + //remove the useless spaces (but keep the ones separating numbers) + static const QRegExp SPACE_CLEANER("\\s*([" + POS_LAT + NEG_LAT + POS_LON + + NEG_LON + DEGREE_SIGNS + "'\"\\s])\\s*"); + const QString normalized = gps_text.trimmed().toUpper().replace(SPACE_CLEANER, "\\1"); + + if (normalized.isEmpty()) { + *latitude = 0.0; + *longitude = 0.0; + return true; + } + if (parseSpecialCoords(normalized, *latitude, *longitude)) + return true; + int pos = 0; + return parseCoord(normalized, pos, POS_LAT, NEG_LAT, POS_LON + NEG_LON, *latitude) && + parseCoord(normalized, pos, POS_LON, NEG_LON, "", *longitude) && + pos == normalized.size(); } #if 0 // we'll need something like this for the dive site management, eventually diff --git a/tests/testgpscoords.cpp b/tests/testgpscoords.cpp index a38dcc807..c5a4d22a4 100644 --- a/tests/testgpscoords.cpp +++ b/tests/testgpscoords.cpp @@ -77,6 +77,29 @@ void TestGpsCoords::testSpaceDecimalParse() coord2double(52.83), coord2double(1.61)); } +void TestGpsCoords::testXmlFormatParse() +{ + testParseOK("46.473881 6.784696", + coord2double(46.473881), coord2double(6.784696)); +} + +void TestGpsCoords::testNegativeXmlFormatParse() +{ + testParseOK("46.473881 -6.784696", + coord2double(46.473881), -coord2double(6.784696)); +} + +void TestGpsCoords::testNoUnitParse() +{ + testParseOK("48 51.491n 2 17.677e", + coord2double(48, 51.491), coord2double(2, 17.677)); +} + +void TestGpsCoords::testPrefixNoUnitParse() +{ + testParseOK("n48 51.491 w2 17.677", + coord2double(48, 51.491), -coord2double(2, 17.677)); +} void TestGpsCoords::testParseOK(const QString &txt, double expectedLat, double expectedLon) diff --git a/tests/testgpscoords.h b/tests/testgpscoords.h index 5add3da93..784bc302e 100644 --- a/tests/testgpscoords.h +++ b/tests/testgpscoords.h @@ -18,6 +18,10 @@ private slots: void testDecimalParse(); void testSpaceDecimalParse(); void testDecimalInversedParse(); + void testXmlFormatParse(); + void testNoUnitParse(); + void testNegativeXmlFormatParse(); + void testPrefixNoUnitParse(); private: static void testParseOK(const QString &txt, double expectedLat,