diff options
-rw-r--r-- | dive.c | 35 | ||||
-rw-r--r-- | dive.h | 27 | ||||
-rw-r--r-- | divelist.c | 6 | ||||
-rw-r--r-- | file.c | 2 | ||||
-rw-r--r-- | gps.c | 2 | ||||
-rw-r--r-- | info.c | 2 | ||||
-rw-r--r-- | main.c | 7 | ||||
-rw-r--r-- | parse-xml.c | 82 | ||||
-rw-r--r-- | webservice.c | 56 |
9 files changed, 142 insertions, 77 deletions
@@ -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; +} @@ -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,12 +482,15 @@ 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); 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,11 +521,11 @@ 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); +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); @@ -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) @@ -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); } @@ -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); @@ -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/parse-xml.c b/parse-xml.c index 184a0e89d..eb75e74c0 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -4,6 +4,7 @@ #include <stdlib.h> #include <errno.h> #include <unistd.h> +#include <assert.h> #define __USE_XOPEN #include <time.h> #include <libxml/parser.h> @@ -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..fec2ccf2d 100644 --- a/webservice.c +++ b/webservice.c @@ -4,8 +4,11 @@ #include <libxml/tree.h> #include <libxml/parser.h> #include "dive.h" +#include "divelist.h" #include "display-gtk.h" +struct dive_table gps_location_table; + enum { DD_STATUS_OK, DD_STATUS_ERROR_CONNECT, @@ -142,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; @@ -203,8 +252,11 @@ 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 */ + 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))); } |