Contents of /trunk/src/precpos.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2 - (hide annotations)
Sat Jun 20 11:44:14 2009 UTC (14 years, 11 months ago) by harbaum
File MIME type: text/plain
File size: 13365 byte(s)
No copy'n paste on fremantle
1 harbaum 1 /*
2     * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3     *
4     * This file is part of GPXView.
5     *
6     * GPXView is free software: you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation, either version 3 of the License, or
9     * (at your option) any later version.
10     *
11     * GPXView is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with GPXView. If not, see <http://www.gnu.org/licenses/>.
18     */
19    
20     #include "gpxview.h"
21    
22     #define PP_WIDTH 150
23     #define PP_HEIGHT 150
24    
25     #define MAX_POS 250 // positions saved per chunk
26    
27     typedef struct {
28     double lat, lon;
29     } dpos_t;
30    
31     typedef struct pos_list {
32     int len;
33     pos_t pos[MAX_POS];
34     struct pos_list *next;
35     } pos_list_t;
36    
37     typedef struct {
38     appdata_t *appdata;
39     guint handler_id;
40     GtkWidget *area;
41     GtkWidget *total_label, *range_label;
42     GtkWidget *lat_label, *lon_label;
43     GdkPixmap *pixmap;
44     pos_list_t *pos_list;
45    
46     /* values calculated on update */
47     pos_t min, max, mid;
48     dpos_t avg;
49     double scale;
50     int total;
51    
52     } pp_context_t;
53    
54     GdkGC *clone_gc(GdkPixmap *pixmap, GtkWidget *widget, char *color) {
55     GdkGC *gc = gdk_gc_new(pixmap);
56     gdk_gc_copy(gc, widget->style->black_gc);
57     GdkColor used_color;
58     gdk_color_parse(color, &used_color); // green
59     gdk_gc_set_rgb_fg_color(gc, &used_color);
60    
61     return gc;
62     }
63    
64     static void context_update(pp_context_t *context) {
65     /* count stored positions and get min/max values */
66     context->total = 0;
67     context->min.lat = context->min.lon = 180.0;
68     context->max.lat = context->max.lon = -180.0;
69     context->avg.lat = context->avg.lon = 0.0;
70    
71     pos_list_t *pos_list = context->pos_list;
72     while(pos_list) {
73     context->total += pos_list->len;
74    
75     int j;
76     for(j=0;j<pos_list->len;j++) {
77     if(pos_list->pos[j].lat > context->max.lat)
78     context->max.lat = pos_list->pos[j].lat;
79     if(pos_list->pos[j].lat < context->min.lat)
80     context->min.lat = pos_list->pos[j].lat;
81     if(pos_list->pos[j].lon > context->max.lon)
82     context->max.lon = pos_list->pos[j].lon;
83     if(pos_list->pos[j].lon < context->min.lon)
84     context->min.lon = pos_list->pos[j].lon;
85    
86     context->avg.lat += pos_list->pos[j].lat;
87     context->avg.lon += pos_list->pos[j].lon;
88     }
89    
90     pos_list = pos_list->next;
91     }
92    
93     if(!context->total)
94     return;
95    
96     context->avg.lat /= context->total;
97     context->avg.lon /= context->total;
98    
99     #define SCALE 0.9
100    
101     context->mid.lat = (context->max.lat+context->min.lat)/2.0;
102     context->mid.lon = (context->max.lon+context->min.lon)/2.0;
103     float lat_scale = SCALE/(context->max.lat-context->min.lat);
104     float lon_scale = SCALE/(context->max.lon-context->min.lon);
105    
106     context->scale = (lat_scale < lon_scale)?lat_scale:lon_scale;
107     }
108    
109     #define XOFF 1
110     static void gdk_draw_cross(GdkDrawable *drawable, GdkGC *gc, gint x, gint y) {
111     gdk_draw_line(drawable, gc, x-XOFF, y-XOFF, x+XOFF, y+XOFF);
112     gdk_draw_line(drawable, gc, x-XOFF, y+XOFF, x+XOFF, y-XOFF);
113     }
114    
115     static void pp_draw(GtkWidget *widget, pp_context_t *context) {
116     gint width = widget->allocation.width;
117     gint height = widget->allocation.height;
118     gint diameter = (height < width)?height:width;
119    
120     gint xcenter = width/2;
121     gint ycenter = height/2;
122    
123     /* erase background */
124     gdk_draw_rectangle(context->pixmap,
125     widget->style->bg_gc[GTK_STATE_NORMAL], TRUE,
126     0, 0, width, height);
127    
128     GdkGC *circle_gc = widget->style->white_gc;
129     if(widget->style->bg[GTK_STATE_NORMAL].red +
130     widget->style->bg[GTK_STATE_NORMAL].green +
131     widget->style->bg[GTK_STATE_NORMAL].blue > 3*60000) {
132     circle_gc = gdk_gc_new(context->pixmap);
133     gdk_gc_copy(circle_gc, widget->style->black_gc);
134     GdkColor lgrey_color;
135     gdk_color_parse("#DDDDDD", &lgrey_color);
136     gdk_gc_set_rgb_fg_color(circle_gc, &lgrey_color);
137     }
138    
139     gdk_draw_arc(context->pixmap, circle_gc, TRUE,
140     xcenter - (SCALE*diameter/2),
141     ycenter - (SCALE*diameter/2),
142     SCALE*diameter, SCALE*diameter,
143     0, 360*64);
144    
145     /* make sure data captured so far allows for useful rendering */
146     if(context->total < 2) return;
147     if(context->min.lat >= context->max.lat) return;
148     if(context->min.lon >= context->max.lon) return;
149     if(context->scale > 50000) return;
150    
151     /* setup required colors */
152     GdkGC *green_gc = clone_gc(context->pixmap, widget, "#008000");
153     GdkGC *red_gc = clone_gc(context->pixmap, widget, "#800000");
154     GdkGC *blue_gc = clone_gc(context->pixmap, widget, "#000080");
155    
156     #if 0
157     printf("---------- %f\n", context->scale);
158     printf("X: %f->%f->%f Y: %f->%f->%f\n",
159     context->min.lon, context->mid.lon, context->max.lon,
160     context->min.lat, context->mid.lat, context->max.lat);
161     #endif
162    
163     /* draw all dots */
164     pos_list_t *pos_list = context->pos_list;
165     double dscale = context->scale * diameter;
166     while(pos_list) {
167     int j;
168     for(j=0;j<pos_list->len;j++) {
169     #if 0
170     printf("%f %f -> y = %f, x = %f\n",
171     pos_list->pos[j].lat, pos_list->pos[j].lon,
172     (pos_list->pos[j].lat-context->mid.lat)*context->scale,
173     (pos_list->pos[j].lon-context->mid.lon)*context->scale);
174     #endif
175    
176     if(pos_list->next || j != pos_list->len-1)
177     gdk_draw_cross(context->pixmap, green_gc,
178     xcenter + ((pos_list->pos[j].lon-context->mid.lon)*dscale),
179     ycenter + ((pos_list->pos[j].lat-context->mid.lat)*dscale));
180     else
181     gdk_draw_arc(context->pixmap, red_gc, TRUE,
182     xcenter + ((pos_list->pos[j].lon-context->mid.lon)*dscale)-3,
183     ycenter + ((pos_list->pos[j].lat-context->mid.lat)*dscale)-3,
184     7, 7, 0, 360*64);
185     }
186    
187     pos_list = pos_list->next;
188     }
189     #if 0
190     printf("D y = %f, x = %f\n",
191     (context->avg.lat-context->mid.lat)*context->scale,
192     (context->avg.lon-context->mid.lon)*context->scale);
193     #endif
194     gdk_draw_arc(context->pixmap, blue_gc, TRUE,
195     xcenter + ((context->avg.lon-context->mid.lon)*dscale)-5,
196     ycenter + ((context->avg.lat-context->mid.lat)*dscale)-5,
197     11, 11, 0, 360*64);
198     }
199    
200     /* Create a new backing pixmap of the appropriate size */
201     static gint pp_configure_event(GtkWidget *widget, GdkEventConfigure *event,
202     gpointer data) {
203     pp_context_t *context = (pp_context_t*)data;
204    
205     if(context->pixmap)
206     gdk_pixmap_unref(context->pixmap);
207    
208     context->pixmap = gdk_pixmap_new(widget->window,
209     widget->allocation.width,
210     widget->allocation.height,
211     -1);
212     context_update(context);
213     pp_draw(widget, context);
214    
215     return TRUE;
216     }
217    
218     /* Redraw the screen from the backing pixmap */
219     static gint pp_expose_event(GtkWidget *widget, GdkEventExpose *event,
220     gpointer data) {
221     pp_context_t *context = (pp_context_t*)data;
222    
223     gdk_draw_pixmap(widget->window,
224     widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
225     context->pixmap,
226     event->area.x, event->area.y,
227     event->area.x, event->area.y,
228     event->area.width, event->area.height);
229    
230     return FALSE;
231     }
232    
233     gint pp_destroy_event(GtkWidget *widget, gpointer data ) {
234     pp_context_t *context = (pp_context_t*)data;
235    
236     printf("destroying precise position view\n");
237    
238     /* stop timer */
239     if(context->handler_id)
240     gtk_timeout_remove(context->handler_id);
241    
242     pos_list_t *pos_list = context->pos_list;
243     while(pos_list) {
244     pos_list_t *next = pos_list->next;
245     free(pos_list);
246     pos_list = next;
247     }
248    
249     /* destroy context itself */
250     g_free(context);
251    
252     return FALSE;
253     }
254    
255     /* called once a second */
256     static gboolean update(gpointer data) {
257     pp_context_t *context = (pp_context_t*)data;
258    
259     pos_list_t **pos_list = &context->pos_list;
260     while(*pos_list && ((*pos_list)->len == MAX_POS))
261     pos_list = &(*pos_list)->next;
262    
263     if(!*pos_list) {
264     printf("alloc new list\n");
265     *pos_list = g_new0(pos_list_t, 1);
266     }
267    
268     /* get one position */
269     pos_t *p = gps_get_pos(context->appdata);
270     if(p) (*pos_list)->pos[(*pos_list)->len++] = *p;
271    
272     context_update(context);
273    
274     if(context->pixmap) {
275     /* draw sat view */
276     pp_draw(context->area, context);
277     gtk_widget_queue_draw_area(context->area, 0,0,
278     context->area->allocation.width,
279     context->area->allocation.height);
280     }
281    
282     /* and whatever else needs to be done ... */
283     char str[32];
284     snprintf(str, sizeof(str), _("Total: %d"), context->total);
285     gtk_label_set_text(GTK_LABEL(context->total_label), str);
286    
287     /* calculate range */
288     pos_t pos1 = { context->mid.lat, context->min.lon };
289     pos_t pos2 = { context->mid.lat, context->max.lon };
290    
291     // printf("Total = %d\n", context->total);
292    
293     if(p) {
294     snprintf(str, sizeof(str), _("Diameter: "));
295     if(context->total > 1) {
296     float dist = gpx_pos_get_distance(pos1, pos2,
297     context->appdata->imperial);
298     // printf("dist = %f\n", dist);
299     distance_str(str+strlen(str), sizeof(str)-strlen(str),
300     dist, context->appdata->imperial);
301     } else
302     strcat(str+strlen(str), "---");
303     } else
304     strcpy(str, _("No fix"));
305    
306     #ifndef USE_MAEMO
307     gtk_label_set_text(GTK_LABEL(context->range_label), str);
308     #else
309     char *mup = g_markup_printf_escaped("<span size='x-small'>%s</span>", str);
310     gtk_label_set_markup(GTK_LABEL(context->range_label), mup);
311     g_free(mup);
312     #endif
313    
314     pos_lat_str(str, sizeof(str), context->avg.lat);
315     gtk_label_set_text(GTK_LABEL(context->lat_label), str);
316     pos_lon_str(str, sizeof(str), context->avg.lon);
317     gtk_label_set_text(GTK_LABEL(context->lon_label), str);
318    
319     return TRUE; // fire again
320     }
321    
322     #ifdef USE_MAEMO
323     static void on_mm_export_clicked(GtkButton *button, gpointer data) {
324     pp_context_t *context = (pp_context_t*)data;
325    
326     if(!context->avg.lat || !context->avg.lon)
327     return;
328    
329     pos_t pos = { context->avg.lat, context->avg.lon } ;
330     dbus_mm_set_position(context->appdata, &pos);
331     }
332     #endif
333    
334 harbaum 2 #ifndef NO_COPY_N_PASTE
335 harbaum 1 static void on_copy_clicked(GtkButton *button, gpointer data) {
336     pp_context_t *context = (pp_context_t*)data;
337     char str[64];
338    
339     /* make a textual representation of the coordinate */
340     pos_lat_str(str, sizeof(str), context->avg.lat);
341     strcat(str, " ");
342     pos_lon_str(str+strlen(str), sizeof(str)-strlen(str), context->avg.lon);
343    
344     printf("set clipboard to \"%s\"\n", str);
345     gtk_clipboard_set_text(context->appdata->clipboard, str, -1);
346     }
347 harbaum 2 #endif
348 harbaum 1
349     void precise_position(appdata_t *appdata) {
350     pp_context_t *context = g_new0(pp_context_t, 1);
351    
352     context->appdata = appdata;
353    
354     if(!appdata->use_gps) {
355     errorf(_("GPS is disabled. Please enable it to use this feature."));
356     g_free(context);
357     return;
358     }
359    
360     GtkWidget *dialog = gtk_dialog_new_with_buttons(_("Precise Position"),
361     GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
362     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
363    
364     #if defined(USE_MAEMO) && defined(HILDON_HELP)
365     hildon_help_dialog_help_enable(GTK_DIALOG(dialog),
366     HELP_ID_PRECPOS, appdata->osso_context);
367     #endif
368    
369     GtkWidget *hbox = gtk_hbox_new(FALSE,20);
370    
371     /* --------------- left part ------------------------ */
372     GtkWidget *vbox = gtk_vbox_new(FALSE,2);
373     context->area = gtk_drawing_area_new();
374     gtk_drawing_area_size(GTK_DRAWING_AREA(context->area),
375     PP_WIDTH, PP_HEIGHT);
376    
377     gtk_signal_connect(GTK_OBJECT(context->area), "expose_event",
378     G_CALLBACK(pp_expose_event), context);
379     gtk_signal_connect(GTK_OBJECT(context->area),"configure_event",
380     G_CALLBACK(pp_configure_event), context);
381     g_signal_connect(G_OBJECT(dialog), "destroy",
382     G_CALLBACK(pp_destroy_event), context);
383    
384     gtk_box_pack_start_defaults(GTK_BOX(vbox), context->area);
385     gtk_box_pack_start_defaults(GTK_BOX(vbox),
386     context->range_label = gtk_label_new(""));
387    
388     gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox);
389    
390     /* --------------- right part ------------------------ */
391     vbox = gtk_vbox_new(FALSE,2);
392    
393     gtk_box_pack_start_defaults(GTK_BOX(vbox),
394     context->total_label = gtk_label_new(""));
395    
396     gtk_box_pack_start_defaults(GTK_BOX(vbox), gtk_hseparator_new());
397    
398     gtk_box_pack_start_defaults(GTK_BOX(vbox), gtk_label_new(_("Latitude:")));
399     gtk_box_pack_start_defaults(GTK_BOX(vbox),
400     context->lat_label = gtk_label_new(""));
401     gtk_box_pack_start_defaults(GTK_BOX(vbox), gtk_label_new(_("Longitude:")));
402     gtk_box_pack_start_defaults(GTK_BOX(vbox),
403     context->lon_label = gtk_label_new(""));
404 harbaum 2 #ifndef NO_COPY_N_PASTE
405 harbaum 1 GtkWidget *copy_but = gtk_button_new_with_label(_("Copy"));
406     gtk_signal_connect(GTK_OBJECT(copy_but), "clicked",
407     (GtkSignalFunc)on_copy_clicked, context);
408     gtk_box_pack_start_defaults(GTK_BOX(vbox), copy_but);
409 harbaum 2 #endif
410 harbaum 1
411     gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox);
412    
413     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
414    
415     context->handler_id = gtk_timeout_add(1000, update, context);
416    
417     #ifdef USE_MAEMO
418     /* ------------- maemo mapper button ---------------- */
419     GtkWidget *button = gtk_button_new();
420     gtk_button_set_image(GTK_BUTTON(button), icon_get_widget(ICON_MISC, 0));
421     gtk_signal_connect(GTK_OBJECT(button), "clicked",
422     (GtkSignalFunc)on_mm_export_clicked, context);
423     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), button);
424     #endif
425    
426     update(context);
427     gtk_widget_show_all(dialog);
428     gtk_dialog_run(GTK_DIALOG(dialog));
429     gtk_widget_destroy(dialog);
430     }