summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Dirk Hohndel <dirk@hohndel.org>2013-01-27 14:42:14 -0800
committerGravatar Dirk Hohndel <dirk@hohndel.org>2013-01-27 14:42:14 -0800
commit546fecd850a6e7342a41925b895c1e4275dc7aca (patch)
tree7edde5e87ab7c686d5540371298a7d414d2895d9
parent0a91669efe9f3ad217c5cdd220d0d71eba0caf8b (diff)
downloadsubsurface-546fecd850a6e7342a41925b895c1e4275dc7aca.tar.gz
UTF8 aware parser for some more GPS formats
I'm sure there are better ways to do this, but this appears to grok most rational formats I was able to find. NSEW or positive/negative numbers. Decimal degrees (WGS84) or degrees and decimal minutes (that's what most GPSs seem to provide). I'm sure there are still corner cases that confuse it, but it seemed reasonably robust in testing. I don't really love the ';' as separator but that solves the obvious problem with locales that use a decimal comma. Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
-rw-r--r--dive.h1
-rw-r--r--info.c90
2 files changed, 83 insertions, 8 deletions
diff --git a/dive.h b/dive.h
index 02e6a9e73..c9f43d81e 100644
--- a/dive.h
+++ b/dive.h
@@ -548,6 +548,7 @@ const char *weekday(int wday);
const char *monthname(int mon);
#define UTF8_DEGREE "\xc2\xb0"
+#define UCS4_DEGREE 0xb0
#define UTF8_SUBSCRIPT_2 "\xe2\x82\x82"
#define UTF8_WHITESTAR "\xe2\x98\x86"
#define UTF8_BLACKSTAR "\xe2\x98\x85"
diff --git a/info.c b/info.c
index d8a2fdc00..4104b5af5 100644
--- a/info.c
+++ b/info.c
@@ -394,12 +394,19 @@ static int get_rating(const char *string)
return rating_val;
}
+/* this has to be done with UTF8 as people might want to enter the degree symbol */
static gboolean parse_gps_text(const char *gps_text, double *latitude, double *longitude)
{
const char *text = gps_text;
+ char *endptr;
+ gboolean south = FALSE;
+ gboolean west = FALSE;
+ double parselat, parselong;
+ gunichar degrees = UCS4_DEGREE;
+ gunichar c;
- while (isspace(*text))
- text++;
+ while (g_unichar_isspace(g_utf8_get_char(text)))
+ text = g_utf8_next_char(text);
/* an empty string is interpreted as 0.0,0.0 and therefore "no gps location" */
if (!*text) {
@@ -407,11 +414,78 @@ static gboolean parse_gps_text(const char *gps_text, double *latitude, double *l
*longitude = 0.0;
return TRUE;
}
- /* WGS84 style decimal degrees */
- if (sscanf(text, "%lf,%lf", latitude, longitude) == 2)
- return TRUE;
+ /* ok, let's parse by hand - first degrees of latitude */
+ if (g_unichar_toupper(g_utf8_get_char(text)) == 'N')
+ text++;
+ if (g_unichar_toupper(g_utf8_get_char(text)) == 'S') {
+ text++;
+ south = TRUE;
+ }
+ parselat = strtod(text, &endptr);
+ if (text == endptr)
+ return FALSE;
+ text = endptr;
+ if (parselat < 0.0) {
+ south = TRUE;
+ parselat *= -1;
+ }
- return FALSE;
+ /* next optional minutes as decimal, skipping degree symbol */
+ while (g_unichar_isspace(c = g_utf8_get_char(text)) || c == degrees)
+ text = g_utf8_next_char(text);
+ if (g_unichar_toupper(c) != 'E' && g_unichar_toupper(c) != 'W' && c != ';' && c != ',') {
+ parselat += strtod(text, &endptr) / 60.0;
+ if (text == endptr)
+ return FALSE;
+ text = endptr;
+ /* skip trailing minute symbol */
+ if (g_utf8_get_char(text) == '\'')
+ text = g_utf8_next_char(text);
+ }
+ /* skip seperator between latitude and longitude */
+ while (g_unichar_isspace(c = g_utf8_get_char(text)) || c == ';' || c == ',')
+ text = g_utf8_next_char(text);
+
+ /* next degrees of longitude */
+ if (g_unichar_toupper(g_utf8_get_char(text)) == 'E')
+ text++;
+ if (g_unichar_toupper(g_utf8_get_char(text)) == 'W') {
+ text++;
+ west = TRUE;
+ }
+ parselong = strtod(text, &endptr);
+ if (text == endptr)
+ return FALSE;
+ text = endptr;
+ if (parselong < 0.0) {
+ west = TRUE;
+ parselong *= -1;
+ }
+
+ /* next optional minutes as decimal, skipping degree symbol */
+ while (g_unichar_isspace(c = g_utf8_get_char(text)) || c == degrees)
+ text = g_utf8_next_char(text);
+ if (*text) {
+ parselong += strtod(text, &endptr) / 60.0;
+ if (text == endptr)
+ return FALSE;
+ text = endptr;
+ /* skip trailing minute symbol */
+ if (g_utf8_get_char(text) == '\'')
+ text = g_utf8_next_char(text);
+ /* make sure there's nothing else left on the input */
+ while (g_unichar_isspace(g_utf8_get_char(text)))
+ text = g_utf8_next_char(text);
+ if (*text)
+ return FALSE;
+ }
+ if (west && parselong > 0.0)
+ parselong *= -1;
+ if (south && parselat > 0.0)
+ parselat *= -1;
+ *latitude = parselat;
+ *longitude = parselong;
+ return TRUE;
}
static gboolean gps_changed(struct dive *dive, struct dive *master, const char *gps_text)
@@ -571,9 +645,9 @@ static void dive_info_widget(GtkWidget *box, struct dive *dive, struct dive_info
info->location = text_entry(box, _("Location"), location_list, dive->location);
if (dive_has_location(dive))
- snprintf(gps_text, sizeof(gps_text), "%3.5lf,%3.5lf", dive->latitude.udeg / 1000000.0,
+ snprintf(gps_text, sizeof(gps_text), "%3.5lf;%3.5lf", dive->latitude.udeg / 1000000.0,
dive->longitude.udeg / 1000000.0);
- info->gps = single_text_entry(box, _("GPS"), gps_text);
+ info->gps = single_text_entry(box, _("GPS (WGS84 or GPS format - use ';' as separator)"), gps_text);
hbox = gtk_hbox_new(FALSE, 3);
gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0);