Hi,

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.

This patch is tested against the v4.4-branch. The cmake build is too broken for the moment in master (tests are not built with qmake). It applies OK on master with "git am --3way".

CU
>From a103fca5ade177ad4ea6632c37fa0b011905660a Mon Sep 17 00:00:00 2001
From: Patrick Valsecchi <[email protected]>
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 <[email protected]>
---
 qthelper.cpp            | 66 +++++++++++++++++++++++++++++++++----------------
 tests/testgpscoords.cpp | 23 +++++++++++++++++
 tests/testgpscoords.h   |  4 +++
 3 files changed, 72 insertions(+), 21 deletions(-)

diff --git a/qthelper.cpp b/qthelper.cpp
index acb1e10..51712da 100644
--- a/qthelper.cpp
+++ b/qthelper.cpp
@@ -57,6 +57,9 @@ QString printGPSCoords(int lat, int lon)
 	return result;
 }
 
+/**
+* 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)
@@ -78,7 +81,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;
@@ -88,7 +91,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
@@ -102,10 +105,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) {
@@ -117,20 +121,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;
 		}
@@ -138,31 +140,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;
 }
 
+/**
+* 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;
+	}
+	return false;
+}
+
 bool parseGpsText(const QString &gps_text, double *latitude, double *longitude)
 {
-	const QString trimmed = gps_text.trimmed();
-	if (trimmed.isEmpty()) {
+	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");
+
+	//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;
-	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();
+	return parseCoord(normalized, pos, POS_LAT, NEG_LAT, POS_LON + NEG_LON, *latitude) &&
+		parseCoord(normalized, pos, POS_LON, NEG_LON, "", *longitude) &&
+		pos == normalized.size();
 }
 
 bool gpsHasChanged(struct dive *dive, struct dive *master, const QString &gps_text, bool *parsed_out)
diff --git a/tests/testgpscoords.cpp b/tests/testgpscoords.cpp
index a38dcc8..c5a4d22 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 5add3da..784bc30 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,
-- 
2.1.0

_______________________________________________
subsurface mailing list
[email protected]
http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface

Reply via email to