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 <patrick@thus.ch>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
Patrick Valsecchi 2015-02-23 13:38:41 +01:00 committed by Dirk Hohndel
parent 0f6f1c7ccf
commit ce79b9ffa4
3 changed files with 73 additions and 22 deletions

View file

@ -71,6 +71,9 @@ extern "C" const char *printGPSCoords(int lat, int lon)
return strdup(result.toUtf8().data()); 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, static bool parseCoord(const QString& txt, int& pos, const QString& positives,
const QString& negatives, const QString& others, const QString& negatives, const QString& others,
double& value) double& value)
@ -92,7 +95,7 @@ static bool parseCoord(const QString& txt, int& pos, const QString& positives,
numberDefined = true; numberDefined = true;
posBeforeNumber = pos; posBeforeNumber = pos;
pos += numberRe.cap(1).size() - 1; pos += numberRe.cap(1).size() - 1;
} else if (positives.indexOf(txt[pos].toUpper()) >= 0) { } else if (positives.indexOf(txt[pos]) >= 0) {
if (sign != 0) if (sign != 0)
return false; return false;
sign = 1; sign = 1;
@ -102,7 +105,7 @@ static bool parseCoord(const QString& txt, int& pos, const QString& positives,
++pos; ++pos;
break; break;
} }
} else if (negatives.indexOf(txt[pos].toUpper()) >= 0) { } else if (negatives.indexOf(txt[pos]) >= 0) {
if (sign != 0) { if (sign != 0) {
if (others.indexOf(txt[pos]) >= 0) if (others.indexOf(txt[pos]) >= 0)
//special case for the '-' sign => next coordinate //special case for the '-' sign => next coordinate
@ -116,10 +119,11 @@ static bool parseCoord(const QString& txt, int& pos, const QString& positives,
++pos; ++pos;
break; break;
} }
} else if (others.indexOf(txt[pos].toUpper()) >= 0) { } else if (others.indexOf(txt[pos]) >= 0) {
//we are at the next coordinate. //we are at the next coordinate.
break; break;
} else if (DEGREE_SIGNS.indexOf(txt[pos]) >= 0) { } else if (DEGREE_SIGNS.indexOf(txt[pos]) >= 0 ||
(txt[pos].isSpace() && !degreesDefined && numberDefined)) {
if (!numberDefined) if (!numberDefined)
return false; return false;
if (degreesDefined) { if (degreesDefined) {
@ -131,20 +135,18 @@ static bool parseCoord(const QString& txt, int& pos, const QString& positives,
value += number; value += number;
numberDefined = false; numberDefined = false;
degreesDefined = true; degreesDefined = true;
} else if (txt[pos] == '\'') { } else if (txt[pos] == '\'' || (txt[pos].isSpace() && !minutesDefined && numberDefined)) {
if (!numberDefined || minutesDefined) if (!numberDefined || minutesDefined)
return false; return false;
value += number / 60.0; value += number / 60.0;
numberDefined = false; numberDefined = false;
minutesDefined = true; minutesDefined = true;
} else if (txt[pos] == '"') { } else if (txt[pos] == '"' || (txt[pos].isSpace() && !secondsDefined && numberDefined)) {
if (!numberDefined || secondsDefined) if (!numberDefined || secondsDefined)
return false; return false;
value += number / 3600.0; value += number / 3600.0;
numberDefined = false; numberDefined = false;
secondsDefined = true; secondsDefined = true;
} else if (txt[pos] == ' ' || txt[pos] == '\t') {
//ignore spaces
} else { } else {
return false; return false;
} }
@ -152,31 +154,53 @@ static bool parseCoord(const QString& txt, int& pos, const QString& positives,
} }
if (!degreesDefined && numberDefined) { if (!degreesDefined && numberDefined) {
value = number; //just a single number => degrees value = number; //just a single number => degrees
numberDefined = false; } else if (!minutesDefined && numberDefined) {
degreesDefined = true; value += number / 60.0;
} } else if (!secondsDefined && numberDefined) {
if (!degreesDefined || numberDefined) value += number / 3600.0;
} else if (numberDefined) {
return false; return false;
}
if (sign == -1) value *= -1.0; if (sign == -1) value *= -1.0;
return true; return true;
} }
bool parseGpsText(const QString &gps_text, double *latitude, double *longitude) /**
{ * Parse special coordinate formats that cannot be handled by parseCoord.
const QString trimmed = gps_text.trimmed(); */
if (trimmed.isEmpty()) { static bool parseSpecialCoords(const QString& txt, double& latitude, double& longitude) {
*latitude = 0.0; QRegExp xmlFormat("(-?\\d+(?:\\.\\d+)?)\\s+(-?\\d+(?:\\.\\d+)?)");
*longitude = 0.0; if (xmlFormat.exactMatch(txt)) {
latitude = xmlFormat.cap(1).toDouble();
longitude = xmlFormat.cap(2).toDouble();
return true; 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 POS_LAT = QString("+N") + translate("gettextFromC", "N");
static const QString NEG_LAT = QString("-S") + translate("gettextFromC", "S"); static const QString NEG_LAT = QString("-S") + translate("gettextFromC", "S");
static const QString POS_LON = QString("+E") + translate("gettextFromC", "E"); static const QString POS_LON = QString("+E") + translate("gettextFromC", "E");
static const QString NEG_LON = QString("-W") + translate("gettextFromC", "W"); 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) && //remove the useless spaces (but keep the ones separating numbers)
pos == gps_text.size(); 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 #if 0 // we'll need something like this for the dive site management, eventually

View file

@ -77,6 +77,29 @@ void TestGpsCoords::testSpaceDecimalParse()
coord2double(52.83), coord2double(1.61)); 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, void TestGpsCoords::testParseOK(const QString &txt, double expectedLat,
double expectedLon) double expectedLon)

View file

@ -18,6 +18,10 @@ private slots:
void testDecimalParse(); void testDecimalParse();
void testSpaceDecimalParse(); void testSpaceDecimalParse();
void testDecimalInversedParse(); void testDecimalInversedParse();
void testXmlFormatParse();
void testNoUnitParse();
void testNegativeXmlFormatParse();
void testPrefixNoUnitParse();
private: private:
static void testParseOK(const QString &txt, double expectedLat, static void testParseOK(const QString &txt, double expectedLat,