Merge branch 'integrate-from-webservice'

Bring in the better implementation of merging gps locations from the
webservice
This commit is contained in:
Dirk Hohndel 2013-01-31 14:44:14 +11:00
commit 36b34bd5a5
9 changed files with 142 additions and 77 deletions

35
dive.c
View file

@ -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;
}

27
dive.h
View file

@ -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);

View file

@ -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);

2
file.c
View file

@ -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)

2
gps.c
View file

@ -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);
}

2
info.c
View file

@ -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);

7
main.c
View file

@ -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];

View file

@ -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)
void record_dive(struct dive *dive)
{
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)
{
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);

View file

@ -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)));
}