Contents of /trunk/src/precpos.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 133 - (show annotations)
Mon Oct 12 20:27:55 2009 UTC (14 years, 6 months ago) by harbaum
File MIME type: text/plain
File size: 13491 byte(s)
Various fremantle fixes
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 ENABLE_MAEMO_MAPPER
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 static void on_copy_clicked(GtkButton *button, gpointer data) {
335 pp_context_t *context = (pp_context_t*)data;
336 char str[64];
337
338 /* make a textual representation of the coordinate */
339 pos_lat_str(str, sizeof(str), context->avg.lat);
340 strcat(str, " ");
341 pos_lon_str(str+strlen(str), sizeof(str)-strlen(str), context->avg.lon);
342
343 printf("set clipboard to \"%s\"\n", str);
344 gtk_clipboard_set_text(context->appdata->clipboard, str, -1);
345 }
346
347 void precise_position(appdata_t *appdata) {
348 pp_context_t *context = g_new0(pp_context_t, 1);
349
350 context->appdata = appdata;
351
352 if(!appdata->use_gps) {
353 errorf(_("GPS is disabled. Please enable it to use this feature."));
354 g_free(context);
355 return;
356 }
357
358 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("Precise Position"),
359 GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
360 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
361
362 #if defined(USE_MAEMO) && defined(HILDON_HELP)
363 hildon_help_dialog_help_enable(GTK_DIALOG(dialog),
364 HELP_ID_PRECPOS, appdata->osso_context);
365 #endif
366
367 GtkWidget *hbox = gtk_hbox_new(FALSE,20);
368
369 /* --------------- left part ------------------------ */
370 GtkWidget *vbox = gtk_vbox_new(FALSE,2);
371 context->area = gtk_drawing_area_new();
372 gtk_drawing_area_size(GTK_DRAWING_AREA(context->area),
373 PP_WIDTH, PP_HEIGHT);
374
375 gtk_signal_connect(GTK_OBJECT(context->area), "expose_event",
376 G_CALLBACK(pp_expose_event), context);
377 gtk_signal_connect(GTK_OBJECT(context->area),"configure_event",
378 G_CALLBACK(pp_configure_event), context);
379 g_signal_connect(G_OBJECT(dialog), "destroy",
380 G_CALLBACK(pp_destroy_event), context);
381
382 gtk_box_pack_start_defaults(GTK_BOX(vbox), context->area);
383 gtk_box_pack_start_defaults(GTK_BOX(vbox),
384 context->range_label = gtk_label_new(""));
385
386 gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox);
387
388 /* --------------- right part ------------------------ */
389 vbox = gtk_vbox_new(FALSE,2);
390
391 gtk_box_pack_start_defaults(GTK_BOX(vbox),
392 context->total_label = gtk_label_new(""));
393
394 gtk_box_pack_start_defaults(GTK_BOX(vbox), gtk_hseparator_new());
395
396 gtk_box_pack_start_defaults(GTK_BOX(vbox), gtk_label_new(_("Latitude:")));
397 gtk_box_pack_start_defaults(GTK_BOX(vbox),
398 context->lat_label = gtk_label_new(""));
399 gtk_box_pack_start_defaults(GTK_BOX(vbox), gtk_label_new(_("Longitude:")));
400 gtk_box_pack_start_defaults(GTK_BOX(vbox),
401 context->lon_label = gtk_label_new(""));
402
403 GtkWidget *copy_but = gtk_button_new_with_label(_("Copy"));
404 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
405 hildon_gtk_widget_set_theme_size(copy_but,
406 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
407 #endif
408 gtk_signal_connect(GTK_OBJECT(copy_but), "clicked",
409 (GtkSignalFunc)on_copy_clicked, context);
410 gtk_box_pack_start_defaults(GTK_BOX(vbox), copy_but);
411
412 gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox);
413
414 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
415
416 context->handler_id = gtk_timeout_add(1000, update, context);
417
418 #ifdef ENABLE_MAEMO_MAPPER
419 /* ------------- maemo mapper button ---------------- */
420 GtkWidget *button = gtk_button_new();
421 gtk_button_set_image(GTK_BUTTON(button), icon_get_widget(ICON_MISC, 0));
422 gtk_signal_connect(GTK_OBJECT(button), "clicked",
423 (GtkSignalFunc)on_mm_export_clicked, context);
424 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), button);
425 #endif
426
427 update(context);
428 gtk_widget_show_all(dialog);
429 gtk_dialog_run(GTK_DIALOG(dialog));
430 gtk_widget_destroy(dialog);
431 }