Contents of /trunk/src/precpos.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 288 - (show annotations)
Mon Jun 7 19:19:50 2010 UTC (13 years, 11 months ago) by harbaum
File MIME type: text/plain
File size: 12676 byte(s)
GPS focus enable, portrait support and some attribute icon work
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 lat_label_set(context->lat_label, context->avg.lat);
315 lon_label_set(context->lon_label, context->avg.lon);
316
317 return TRUE; // fire again
318 }
319
320 static void on_copy_clicked(GtkButton *button, gpointer data) {
321 pp_context_t *context = (pp_context_t*)data;
322 char str[64];
323
324 /* make a textual representation of the coordinate */
325 pos_lat_str(str, sizeof(str), context->avg.lat);
326 strcat(str, " ");
327 pos_lon_str(str+strlen(str), sizeof(str)-strlen(str), context->avg.lon);
328
329 printf("set clipboard to \"%s\"\n", str);
330 gtk_clipboard_set_text(context->appdata->clipboard, str, -1);
331 }
332
333 void precise_position(appdata_t *appdata) {
334 pp_context_t *context = g_new0(pp_context_t, 1);
335
336 context->appdata = appdata;
337
338 if(!appdata->use_gps) {
339 errorf(_("GPS is disabled. Please enable it to use this feature."));
340 g_free(context);
341 return;
342 }
343
344 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("Precise Position"),
345 GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
346 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
347
348 #if defined(USE_MAEMO) && defined(HILDON_HELP)
349 hildon_help_dialog_help_enable(GTK_DIALOG(dialog),
350 HELP_ID_PRECPOS, appdata->osso_context);
351 #endif
352
353 GtkWidget *hbox = portrait_box_new(FALSE,20);
354
355 /* --------------- left part ------------------------ */
356 GtkWidget *vbox = gtk_vbox_new(FALSE,2);
357 context->area = gtk_drawing_area_new();
358 gtk_drawing_area_size(GTK_DRAWING_AREA(context->area),
359 PP_WIDTH, PP_HEIGHT);
360
361 gtk_signal_connect(GTK_OBJECT(context->area), "expose_event",
362 G_CALLBACK(pp_expose_event), context);
363 gtk_signal_connect(GTK_OBJECT(context->area),"configure_event",
364 G_CALLBACK(pp_configure_event), context);
365 g_signal_connect(G_OBJECT(dialog), "destroy",
366 G_CALLBACK(pp_destroy_event), context);
367
368 gtk_box_pack_start_defaults(GTK_BOX(vbox), context->area);
369 gtk_box_pack_start_defaults(GTK_BOX(vbox),
370 context->range_label = gtk_label_new(""));
371
372 gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox);
373
374 /* --------------- right part ------------------------ */
375 vbox = gtk_vbox_new(FALSE,2);
376
377 gtk_box_pack_start_defaults(GTK_BOX(vbox),
378 context->total_label = gtk_label_new(""));
379
380 gtk_box_pack_start_defaults(GTK_BOX(vbox), gtk_hseparator_new());
381
382 gtk_box_pack_start_defaults(GTK_BOX(vbox), gtk_label_new(_("Latitude:")));
383 gtk_box_pack_start_defaults(GTK_BOX(vbox),
384 context->lat_label = gtk_label_new(""));
385 gtk_box_pack_start_defaults(GTK_BOX(vbox), gtk_label_new(_("Longitude:")));
386 gtk_box_pack_start_defaults(GTK_BOX(vbox),
387 context->lon_label = gtk_label_new(""));
388
389 GtkWidget *copy_but = gtk_button_new_with_label(_("Copy"));
390 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
391 hildon_gtk_widget_set_theme_size(copy_but,
392 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
393 #endif
394 gtk_signal_connect(GTK_OBJECT(copy_but), "clicked",
395 (GtkSignalFunc)on_copy_clicked, context);
396 gtk_box_pack_start_defaults(GTK_BOX(vbox), copy_but);
397
398 gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox);
399
400 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
401
402 context->handler_id = gtk_timeout_add(1000, update, context);
403
404 update(context);
405 gtk_widget_show_all(dialog);
406 gtk_dialog_run(GTK_DIALOG(dialog));
407 gtk_widget_destroy(dialog);
408 }