[PATCH 2/2] Turn latitude and longitude into integer micro-degree values

Linus Torvalds torvalds at linux-foundation.org
Wed Dec 5 10:15:38 PST 2012


From: Linus Torvalds <torvalds at linux-foundation.org>
Date: Wed, 5 Dec 2012 09:59:52 -0800
Subject: [PATCH 2/2] Turn latitude and longitude into integer micro-degree values

This actually makes us internally use 'micro-degrees' for latitude and
longitude, and we never turn them into floating point either at parse
time or save time.

That said, the Uemis downloader internally does still use atof() when
converting things, which is likely a bug (locale issues and all that),
but I'll ask Dirk to check it out.

Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
---

With this, all our basic dive types are integers.

Sure, we still use floating point for displaying things and calculations, 
and the data is often converted from user input using FP (almost all unit 
conversions involve some FP math), but internally our basic data 
structures are now nice precise ones.

Dirk - notice the Uemis downloader use of 'atof()'. I don't know exactly 
where the data comes from, but that looks like a locale bug. It's not a 
new one, though.

 dive.c             |  4 ++--
 dive.h             |  6 +++++-
 parse-xml.c        | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 save-xml.c         | 13 ++++++-------
 uemis-downloader.c |  2 +-
 uemis.c            | 11 ++++++-----
 uemis.h            |  3 ++-
 7 files changed, 72 insertions(+), 19 deletions(-)

diff --git a/dive.c b/dive.c
index d18a3d458c2f..f527fae37347 100644
--- a/dive.c
+++ b/dive.c
@@ -1366,8 +1366,8 @@ struct dive *merge_dives(struct dive *a, struct dive *b, int offset, gboolean pr
 	res->when = dl ? dl->when : a->when;
 	res->selected = a->selected || b->selected;
 	merge_trip(res, a, b);
-	MERGE_NONZERO(res, a, b, latitude);
-	MERGE_NONZERO(res, a, b, longitude);
+	MERGE_NONZERO(res, a, b, latitude.udeg);
+	MERGE_NONZERO(res, a, b, longitude.udeg);
 	MERGE_TXT(res, a, b, location);
 	MERGE_TXT(res, a, b, notes);
 	MERGE_TXT(res, a, b, buddy);
diff --git a/dive.h b/dive.h
index 746d2a9fc846..4cfa6fa38f20 100644
--- a/dive.h
+++ b/dive.h
@@ -75,6 +75,10 @@ typedef struct {
 	int grams;
 } weight_t;
 
+typedef struct {
+	int udeg;
+} degrees_t;
+
 struct gasmix {
 	fraction_t o2;
 	fraction_t he;
@@ -300,7 +304,7 @@ struct dive {
 	char *notes;
 	char *divemaster, *buddy;
 	int rating;
-	double latitude, longitude;
+	degrees_t latitude, longitude;
 	depth_t maxdepth, meandepth;
 	int salinity; // kg per 10000 l
 	duration_t duration, surfacetime;
diff --git a/parse-xml.c b/parse-xml.c
index 2470f8bb42a5..35299257aae6 100644
--- a/parse-xml.c
+++ b/parse-xml.c
@@ -788,13 +788,61 @@ static int uddf_dive_match(struct dive *dive, const char *name, int len, char *b
 		0;
 }
 
+/*
+ * This parses "floating point" into micro-degrees.
+ * We don't do exponentials etc, if somebody does
+ * gps locations in that format, they are insane.
+ */
+static degrees_t parse_degrees(char *buf, char **end)
+{
+	int sign = 1, decimals = 6, value = 0;
+	degrees_t ret;
+
+	while (isspace(*buf))
+		buf++;
+	switch (*buf) {
+	case '-':
+		sign = -1;
+		/* fallthrough */
+	case '+':
+		buf++;
+	}
+	while (isdigit(*buf)) {
+		value = 10*value + *buf - '0';
+		buf++;
+	}
+
+	/* Get the first six decimals if they exist */
+	if (*buf == '.')
+		buf++;
+	do {
+		value *= 10;
+		if (isdigit(*buf)) {
+			value += *buf - '0';
+			buf++;
+		}
+	} while (--decimals);
+
+	/* Rounding */
+	switch (*buf) {
+	case '5' ... '9':
+		value++;
+	}
+	while (isdigit(*buf))
+		buf++;
+
+	*end = buf;
+	ret.udeg = value * sign;
+	return ret;
+}
+
 static void gps_location(char *buffer, void *_dive)
 {
 	char *end;
 	struct dive *dive = _dive;
 
-	dive->latitude = g_ascii_strtod(buffer, &end);
-	dive->longitude = g_ascii_strtod(end, &end);
+	dive->latitude = parse_degrees(buffer, &end);
+	dive->longitude = parse_degrees(end, &end);
 	free(buffer);
 }
 
diff --git a/save-xml.c b/save-xml.c
index 948f2ed08607..5dc47795f39f 100644
--- a/save-xml.c
+++ b/save-xml.c
@@ -4,7 +4,6 @@
 #include <stdlib.h>
 #include <errno.h>
 #include <time.h>
-#include <math.h>
 
 #include "dive.h"
 
@@ -183,9 +182,9 @@ static void save_salinity(FILE *f, struct dive *dive)
  * on a great circle (ie longitude at equator). And micro-degrees
  * is also enough to fit in a fixed-point 32-bit integer.
  */
-static int format_degrees(char *buffer, double value)
+static int format_degrees(char *buffer, degrees_t value)
 {
-	int udeg = round(value * 1000000.0);
+	int udeg = value.udeg;
 	const char *sign = "";
 
 	if (udeg < 0) {
@@ -196,7 +195,7 @@ static int format_degrees(char *buffer, double value)
 		sign, udeg / 1000000, udeg % 1000000);
 }
 
-static int format_location(char *buffer, double latitude, double longitude)
+static int format_location(char *buffer, degrees_t latitude, degrees_t longitude)
 {
 	int len = sprintf(buffer, "gps='");
 
@@ -212,8 +211,8 @@ static void show_location(FILE *f, struct dive *dive)
 {
 	char buffer[80];
 	const char *prefix = "  <location>";
-	double latitude = dive->latitude;
-	double longitude = dive->longitude;
+	degrees_t latitude = dive->latitude;
+	degrees_t longitude = dive->longitude;
 
 	/*
 	 * Ok, theoretically I guess you could dive at
@@ -221,7 +220,7 @@ static void show_location(FILE *f, struct dive *dive)
 	 * if you do, just fudge it a bit, and say that
 	 * you dove a few meters away.
 	 */
-	if (latitude || longitude) {
+	if (latitude.udeg || longitude.udeg) {
 		int len = sprintf(buffer, "  <location ");
 
 		len += format_location(buffer+len, latitude, longitude);
diff --git a/uemis-downloader.c b/uemis-downloader.c
index d3a3c28d90ef..00dfc5b43284 100644
--- a/uemis-downloader.c
+++ b/uemis-downloader.c
@@ -553,7 +553,7 @@ static void parse_divespot(char *buf)
 	uemis_set_divelocation(divespot, strdup(locationstring), latitude, longitude);
 }
 
-static void track_divespot(char *val, int diveid, char **location, double *latitude, double *longitude)
+static void track_divespot(char *val, int diveid, char **location, degrees_t *latitude, degrees_t *longitude)
 {
 	int id = atoi(val);
 	if (id >= 0 && id > nr_divespots)
diff --git a/uemis.c b/uemis.c
index af344a5f1128..4306a1b5565e 100644
--- a/uemis.c
+++ b/uemis.c
@@ -15,6 +15,7 @@
 #include <glib/gi18n.h>
 #define __USE_XOPEN
 #include <time.h>
+#include <math.h>
 
 #include "dive.h"
 #include "uemis.h"
@@ -107,8 +108,8 @@ struct uemis_helper {
 	int lbs;
 	int divespot;
 	char **location;
-	double *latitude;
-	double *longitude;
+	degrees_t *latitude;
+	degrees_t *longitude;
 	struct uemis_helper *next;
 };
 static struct uemis_helper *uemis_helper = NULL;
@@ -153,7 +154,7 @@ int uemis_get_weight_unit(int diveid)
 	return 0;
 }
 
-void uemis_mark_divelocation(int diveid, int divespot, char **location, double *longitude, double *latitude)
+void uemis_mark_divelocation(int diveid, int divespot, char **location, degrees_t *longitude, degrees_t *latitude)
 {
 	struct uemis_helper *hp = uemis_get_helper(diveid);
 	hp->divespot = divespot;
@@ -168,8 +169,8 @@ void uemis_set_divelocation(int divespot, char *text, double longitude, double l
 	while (hp) {
 		if (hp->divespot == divespot && hp->location) {
 			*hp->location = text;
-			*hp->longitude = longitude;
-			*hp->latitude = latitude;
+			hp->longitude->udeg = round(longitude * 1000000);
+			hp->latitude->udeg = round(latitude * 1000000);
 		}
 		hp = hp->next;
 	}
diff --git a/uemis.h b/uemis.h
index ac47b0978381..f8489f5c3a5e 100644
--- a/uemis.h
+++ b/uemis.h
@@ -6,10 +6,11 @@
 #define UEMIS_H
 
 #include <stdint.h>
+#include "dive.h"
 
 void uemis_parse_divelog_binary(char *base64, void *divep);
 int uemis_get_weight_unit(int diveid);
-void uemis_mark_divelocation(int diveid, int divespot, char **location, double *longitude, double *latitude);
+void uemis_mark_divelocation(int diveid, int divespot, char **location, degrees_t *longitude, degrees_t *latitude);
 void uemis_set_divelocation(int divespot, char *text, double longitude, double latitude);
 
 typedef struct {
-- 
1.8.0.dirty



More information about the subsurface mailing list