mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-28 05:00:20 +00:00
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>
This commit is contained in:
parent
0a91669efe
commit
546fecd850
2 changed files with 83 additions and 8 deletions
1
dive.h
1
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"
|
||||
|
|
90
info.c
90
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);
|
||||
|
|
Loading…
Reference in a new issue