summaryrefslogtreecommitdiffstats
path: root/info.c
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 /info.c
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>
Diffstat (limited to 'info.c')
-rw-r--r--info.c90
1 files changed, 82 insertions, 8 deletions
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);