From 8843ee61560fb3c6a66b7ae9f10367f57eddb109 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 31 Jan 2013 11:08:36 +1100 Subject: Allow using two different tables to hold dives and gps locations This only changes the infrastructure and actually loses functionality as it no longer does the simplistic "just treat the locations as dives and merge them". The new code that does something "smart" with the gps_location_table is yet to be written. But now we can use the XML parser to put the gps locations downloaded from the webservice into their own data structure. In the process I noticed that we never used the two delete functions in parse-xml.c and removed them. Signed-off-by: Dirk Hohndel --- dive.h | 3 +-- file.c | 2 +- parse-xml.c | 82 +++++++++++++----------------------------------------------- webservice.c | 7 ++++-- 4 files changed, 24 insertions(+), 70 deletions(-) diff --git a/dive.h b/dive.h index 4132d0c30..2f4dcbd9e 100644 --- a/dive.h +++ b/dive.h @@ -469,7 +469,7 @@ static inline struct dive *get_dive_by_diveid(int diveid, int deviceid) extern int match_one_dc(struct divecomputer *a, struct divecomputer *b); extern void parse_xml_init(void); -extern void parse_xml_buffer(const char *url, const char *buf, int size, GError **error); +extern void parse_xml_buffer(const char *url, const char *buf, int size, struct dive_table *table, GError **error); extern void parse_xml_exit(void); extern void set_filename(const char *filename, gboolean force); @@ -500,7 +500,6 @@ extern void utc_mkdate(timestamp_t, struct tm *tm); extern struct dive *alloc_dive(void); extern void record_dive(struct dive *dive); -extern void delete_dive(struct dive *dive); extern struct sample *prepare_sample(struct divecomputer *dc); extern void finish_sample(struct divecomputer *dc); diff --git a/file.c b/file.c index 10bb4274e..73c07f82d 100644 --- a/file.c +++ b/file.c @@ -246,7 +246,7 @@ static void parse_file_buffer(const char *filename, struct memblock *mem, GError if (fmt && open_by_filename(filename, fmt+1, mem, error)) return; - parse_xml_buffer(filename, mem->buffer, mem->size, error); + parse_xml_buffer(filename, mem->buffer, mem->size, &dive_table, error); } void parse_file(const char *filename, GError **error, gboolean possible_default_filename) diff --git a/parse-xml.c b/parse-xml.c index 184a0e89d..eb75e74c0 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -4,6 +4,7 @@ #include #include #include +#include #define __USE_XOPEN #include #include @@ -18,85 +19,34 @@ int verbose; +/* the dive table holds the overall dive list; target table points at + * the table we are currently filling */ struct dive_table dive_table; - +struct dive_table *target_table = NULL; /* * Add a dive into the dive_table array */ -void record_dive(struct dive *dive) +static void record_dive_to_table(struct dive *dive, struct dive_table *table) { - int nr = dive_table.nr, allocated = dive_table.allocated; - struct dive **dives = dive_table.dives; + assert(table != NULL); + int nr = table->nr, allocated = table->allocated; + struct dive **dives = table->dives; if (nr >= allocated) { allocated = (nr + 32) * 3 / 2; dives = realloc(dives, allocated * sizeof(struct dive *)); if (!dives) exit(1); - dive_table.dives = dives; - dive_table.allocated = allocated; + table->dives = dives; + table->allocated = allocated; } dives[nr] = fixup_dive(dive); - dive_table.nr = nr+1; + table->nr = nr+1; } -static void delete_dive_renumber(struct dive **dives, int i, int nr) -{ - struct dive *dive = dives[i]; - int number = dive->number, j; - - if (!number) - return; - - /* - * Check that all numbered dives after the deleted - * ones are consecutive, return without renumbering - * if that is not the case. - */ - for (j = i+1; j < nr; j++) { - struct dive *next = dives[j]; - if (!next->number) - break; - number++; - if (next->number != number) - return; - } - - /* - * Ok, we hit the end of the dives or a unnumbered - * dive - renumber. - */ - for (j = i+1 ; j < nr; j++) { - struct dive *next = dives[j]; - if (!next->number) - break; - next->number--; - } -} - -/* - * Remove a dive from the dive_table array - */ -void delete_dive(struct dive *dive) +void record_dive(struct dive *dive) { - int nr = dive_table.nr, i; - struct dive **dives = dive_table.dives; - - /* - * Stupid. We know the dive table is sorted by date, - * we could do a binary lookup. Sue me. - */ - for (i = 0; i < nr; i++) { - struct dive *d = dives[i]; - if (d != dive) - continue; - /* should we re-number? */ - delete_dive_renumber(dives, i, nr); - memmove(dives+i, dives+i+1, sizeof(struct dive *)*(nr-i-1)); - dives[nr] = NULL; - dive_table.nr = nr-1; - break; - } + record_dive_to_table(dive, &dive_table); } static void start_match(const char *type, const char *name, char *buffer) @@ -1172,7 +1122,7 @@ static void dive_end(void) if (!is_dive()) free(cur_dive); else - record_dive(cur_dive); + record_dive_to_table(cur_dive, target_table); cur_dive = NULL; cur_dc = NULL; cur_cylinder_index = 0; @@ -1480,10 +1430,12 @@ static void reset_all(void) import_source = UNKNOWN; } -void parse_xml_buffer(const char *url, const char *buffer, int size, GError **error) +void parse_xml_buffer(const char *url, const char *buffer, int size, + struct dive_table *table, GError **error) { xmlDoc *doc; + target_table = table; doc = xmlReadMemory(buffer, size, url, NULL, 0); if (!doc) { fprintf(stderr, _("Failed to parse '%s'.\n"), url); diff --git a/webservice.c b/webservice.c index 243d9b5a0..9dde771e8 100644 --- a/webservice.c +++ b/webservice.c @@ -6,6 +6,8 @@ #include "dive.h" #include "display-gtk.h" +struct dive_table gps_location_table; + enum { DD_STATUS_OK, DD_STATUS_ERROR_CONNECT, @@ -203,8 +205,9 @@ void webservice_download_dialog(void) result = gtk_dialog_run(GTK_DIALOG(dialog)); if (result == GTK_RESPONSE_ACCEPT) { /* apply download */ - parse_xml_buffer(_("Webservice"), state.xmldata, state.xmldata_len, NULL); - report_dives(TRUE, FALSE); + parse_xml_buffer(_("Webservice"), state.xmldata, state.xmldata_len, &gps_location_table, NULL); + /* now merge the data in the gps_location table into the dive_table */ + // TBD /* store last entered uid in config */ subsurface_set_conf("webservice_uid", gtk_entry_get_text(GTK_ENTRY(uid))); } -- cgit v1.2.3-70-g09d2 From 635c28923d28335bb7d39f95c17c2edbf16ee2ac Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 31 Jan 2013 14:09:16 +1100 Subject: Better algorithm to merge gps locations & locations names from webservice This no longer abuses the dive merging code (which would leave stray "dives" behind if a gps fix couldn't be merged with any of the dives) and instead parses the gps fixes into a second table and then walks that table and tries to find matching dives. The code tries to be reasonably smart about this. If we have auto-generated GPS fixes at regular intervals, we look for a fix that is during a dive (that's likely when the boat where the phone is staying dry is more or less above the diver having fun). And if we have named entries (so the user typed in a location name) we try to match them in order to the dives that happened "that day" (where "that day" is about 6h before and after the timestamp of the gps fix). This commit also renames dive_has_location() to dive_has_gps_location() as the difference between if(!dive->location) and if(dives_has_location) is a bit too subtle... Signed-off-by: Dirk Hohndel --- dive.c | 35 +++++++++++++++++++++++++++++++++++ dive.h | 24 +++++++++++++++++++++++- divelist.c | 6 +++--- gps.c | 2 +- info.c | 2 +- main.c | 7 ++++++- webservice.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 7 files changed, 119 insertions(+), 8 deletions(-) diff --git a/dive.c b/dive.c index d86c7de2a..a0cb60ba6 100644 --- a/dive.c +++ b/dive.c @@ -1519,3 +1519,38 @@ struct dive *merge_dives(struct dive *a, struct dive *b, int offset, gboolean pr fixup_dive(res); return res; } + +struct dive *find_dive_including(timestamp_t when) +{ + int i; + struct dive *dive; + + /* binary search, anyone? Too lazy for now; + * also we always use the duration from the first divecomputer + * could this ever be a problem? */ + for_each_dive(i, dive) { + if (dive->when <= when && when <= dive->when + dive->dc.duration.seconds) + return dive; + } + return NULL; +} + +gboolean dive_within_time_range(struct dive *dive, timestamp_t when, timestamp_t offset) +{ + return when - offset <= dive->when && dive->when + dive->dc.duration.seconds <= when + offset; +} + +/* find the n-th dive that is part of a group of dives within the offset around 'when'. + * How is that for a vague definition of what this function should do... */ +struct dive *find_dive_n_near(timestamp_t when, int n, timestamp_t offset) +{ + int i, j = 0; + struct dive *dive; + + for_each_dive(i, dive) { + if (dive_within_time_range(dive, when, offset)) + if (++j == n) + return dive; + } + return NULL; +} diff --git a/dive.h b/dive.h index 2f4dcbd9e..e719f2289 100644 --- a/dive.h +++ b/dive.h @@ -321,11 +321,19 @@ struct dive { struct divecomputer dc; }; -static inline int dive_has_location(struct dive *dive) +static inline int dive_has_gps_location(struct dive *dive) { return dive->latitude.udeg || dive->longitude.udeg; } +static inline void copy_gps_location(struct dive *from, struct dive *to) +{ + if (from && to) { + to->latitude.udeg = from->latitude.udeg; + to->longitude.udeg = from->longitude.udeg; + } +} + /* Pa = N/m^2 - so we determine the weight (in N) of the mass of 10m * of water (and use standard salt water at 1.03kg per liter if we don't know salinity) * and add that to the surface pressure (or to 1013 if that's unknown) */ @@ -433,6 +441,13 @@ extern struct dive_table dive_table; extern int selected_dive; #define current_dive (get_dive(selected_dive)) +static inline struct dive *get_gps_location(int nr, struct dive_table *table) +{ + if (nr >= table->nr || nr < 0) + return NULL; + return table->dives[nr]; +} + static inline struct dive *get_dive(int nr) { if (nr >= dive_table.nr || nr < 0) @@ -450,6 +465,9 @@ static inline struct dive *get_dive(int nr) #define for_each_dive(_i,_x) \ for ((_i) = 0; ((_x) = get_dive(_i)) != NULL; (_i)++) +#define for_each_gps_location(_i,_x) \ + for ((_i) = 0; ((_x) = get_gps_location(_i, &gps_location_table)) != NULL; (_i)++) + static inline struct dive *get_dive_by_diveid(int diveid, int deviceid) { int i; @@ -464,6 +482,9 @@ static inline struct dive *get_dive_by_diveid(int diveid, int deviceid) } return NULL; } +extern struct dive *find_dive_including(timestamp_t when); +extern gboolean dive_within_time_range(struct dive *dive, timestamp_t when, timestamp_t offset); +struct dive *find_dive_n_near(timestamp_t when, int n, timestamp_t offset); /* Check if two dive computer entries are the exact same dive (-1=no/0=maybe/1=yes) */ extern int match_one_dc(struct divecomputer *a, struct divecomputer *b); @@ -504,6 +525,7 @@ extern void record_dive(struct dive *dive); extern struct sample *prepare_sample(struct divecomputer *dc); extern void finish_sample(struct divecomputer *dc); +extern void sort_table(struct dive_table *table); extern void report_dives(gboolean imported, gboolean prefer_imported); extern struct dive *fixup_dive(struct dive *dive); extern struct dive *merge_dives(struct dive *a, struct dive *b, int offset, gboolean prefer_downloaded); diff --git a/divelist.c b/divelist.c index 882ba3bc6..5a942c13f 100644 --- a/divelist.c +++ b/divelist.c @@ -875,7 +875,7 @@ GdkPixbuf *get_gps_icon(void) static GdkPixbuf *get_gps_icon_for_dive(struct dive *dive) { - if (dive_has_location(dive)) + if (dive_has_gps_location(dive)) return get_gps_icon(); else return NULL; @@ -1608,7 +1608,7 @@ gboolean icon_click_cb(GtkWidget *w, GdkEventButton *event, gpointer data) gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); dive = get_dive(idx); - if (dive && dive_has_location(dive)) + if (dive && dive_has_gps_location(dive)) show_gps_location(dive, NULL); } if (path) @@ -2435,7 +2435,7 @@ static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int } #if HAVE_OSM_GPS_MAP /* Only offer to show on map if it has a location. */ - if (dive_has_location(dive)) { + if (dive_has_gps_location(dive)) { menuitem = gtk_menu_item_new_with_label(_("Show in map")); g_signal_connect(menuitem, "activate", G_CALLBACK(show_gps_location_cb), dive); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); diff --git a/gps.c b/gps.c index 31be2bbc5..15ee65830 100644 --- a/gps.c +++ b/gps.c @@ -242,7 +242,7 @@ void show_gps_locations() map = init_map(); for_each_dive(idx, dive) { - if (dive_has_location(dive)) { + if (dive_has_gps_location(dive)) { add_gps_point(map, dive->latitude.udeg / 1000000.0, dive->longitude.udeg / 1000000.0); } diff --git a/info.c b/info.c index 65ad8b128..2916c0eaf 100644 --- a/info.c +++ b/info.c @@ -675,7 +675,7 @@ 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)) + if (dive_has_gps_location(dive)) print_gps_coordinates(gps_text, sizeof(gps_text), dive->latitude.udeg / 1000000.0, dive->longitude.udeg / 1000000.0); hbox = gtk_hbox_new(FALSE, 2); diff --git a/main.c b/main.c index 4cacb0375..170481035 100644 --- a/main.c +++ b/main.c @@ -50,6 +50,11 @@ static int sortfn(const void *_a, const void *_b) return 0; } +void sort_table(struct dive_table *table) +{ + qsort(table->dives, table->nr, sizeof(struct dive *), sortfn); +} + const char *weekday(int wday) { static const char wday_array[7][7] = { @@ -157,7 +162,7 @@ void report_dives(gboolean is_imported, gboolean prefer_imported) /* This does the right thing for -1: NULL */ last = get_dive(preexisting-1); - qsort(dive_table.dives, dive_table.nr, sizeof(struct dive *), sortfn); + sort_table(&dive_table); for (i = 1; i < dive_table.nr; i++) { struct dive **pp = &dive_table.dives[i-1]; diff --git a/webservice.c b/webservice.c index 9dde771e8..fec2ccf2d 100644 --- a/webservice.c +++ b/webservice.c @@ -4,6 +4,7 @@ #include #include #include "dive.h" +#include "divelist.h" #include "display-gtk.h" struct dive_table gps_location_table; @@ -144,6 +145,52 @@ static void download_dialog_delete(GtkWidget *w, gpointer data) download_dialog_release_xml(state); } +static gboolean is_automatic_fix(struct dive *gpsfix) +{ + if (gpsfix && gpsfix->location && !strcmp(gpsfix->location, "automatic fix")) + return TRUE; + return FALSE; +} + +#define SAME_GROUP 6 * 3600 // six hours + +static void merge_locations_into_dives(void) +{ + int i, nr = 0; + struct dive *gpsfix, *last_named_fix = NULL, *dive; + + sort_table(&gps_location_table); + + for_each_gps_location(i, gpsfix) { + if (is_automatic_fix(gpsfix)) { + dive = find_dive_including(gpsfix->when); + if (dive && !dive_has_gps_location(dive)) + copy_gps_location(gpsfix, dive); + } else { + if (last_named_fix && dive_within_time_range(last_named_fix, gpsfix->when, SAME_GROUP)) { + nr++; + } else { + nr = 1; + last_named_fix = gpsfix; + } + dive = find_dive_n_near(gpsfix->when, nr, SAME_GROUP); + if (dive) { + if (!dive_has_gps_location(dive)) + copy_gps_location(gpsfix, dive); + if (!dive->location) + dive->location = strdup(gpsfix->location); + } else { + struct tm tm; + utc_mkdate(gpsfix->when, &tm); + printf("didn't find dive matching gps fix named %s @ %04d-%02d-%02d %02d:%02d:%02d\n", + gpsfix->location, + tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + } + } + } +} + void webservice_download_dialog(void) { const guint pad = 6; @@ -207,7 +254,9 @@ void webservice_download_dialog(void) /* apply download */ parse_xml_buffer(_("Webservice"), state.xmldata, state.xmldata_len, &gps_location_table, NULL); /* now merge the data in the gps_location table into the dive_table */ - // TBD + merge_locations_into_dives(); + mark_divelist_changed(TRUE); + dive_list_update_dives(); /* store last entered uid in config */ subsurface_set_conf("webservice_uid", gtk_entry_get_text(GTK_ENTRY(uid))); } -- cgit v1.2.3-70-g09d2