Contents of /trunk/src/osm-gps-map-osd-classic.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 70 - (hide annotations)
Thu Aug 20 19:17:23 2009 UTC (14 years, 8 months ago) by harbaum
File MIME type: text/plain
File size: 66262 byte(s)
Seperate OSD implementation
1 harbaum 70 /*
2     * Copyright (C) Till Harbaum 2009 <till@harbaum.org>
3     *
4     * osm-gps-map is free software: you can redistribute it and/or modify it
5     * under the terms of the GNU General Public License as published by the
6     * Free Software Foundation, either version 3 of the License, or
7     * (at your option) any later version.
8     *
9     * osm-gps-map is distributed in the hope that it will be useful, but
10     * WITHOUT ANY WARRANTY; without even the implied warranty of
11     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12     * See the GNU General Public License for more details.
13     *
14     * You should have received a copy of the GNU General Public License along
15     * with this program. If not, see <http://www.gnu.org/licenses/>.
16     */
17    
18     #include "config.h"
19    
20     #ifdef USE_CAIRO
21     #include <cairo.h>
22     #endif
23    
24     //the osd controls
25     typedef struct {
26     GdkPixmap *backup;
27     cairo_surface_t *overlay;
28     gint backup_x, backup_y;
29     OsmGpsMapOsdGpsCallback cb;
30     gpointer data;
31     } osd_priv_t;
32    
33     typedef struct {
34    
35    
36    
37    
38     gpointer priv;
39     } osm_gps_map_osd_t;
40    
41     /* position and extent of bounding box */
42     #define OSD_X (10)
43     #define OSD_Y (10)
44    
45     #define OSD_COLOR 0.5, 0.5, 1
46     #define OSD_COLOR_DISABLED 0.8, 0.8, 0.8
47    
48     /* parameters of the direction shape */
49     #ifndef OSM_GPS_MAP_OSD_DIAMETER
50     #define D_RAD (30) // diameter of dpad
51     #else
52     #define D_RAD (OSM_GPS_MAP_OSD_DIAMETER)
53     #endif
54     #define D_TIP (4*D_RAD/5) // distance of arrow tip from dpad center
55     #define D_LEN (D_RAD/4) // length of arrow
56     #define D_WID (D_LEN) // width of arrow
57    
58     /* parameters of the "zoom" pad */
59     #define Z_STEP (D_RAD/4) // distance between dpad and zoom
60     #define Z_RAD (D_RAD/2) // radius of "caps" of zoom bar
61    
62     /* shadow also depends on control size */
63     #define OSD_SHADOW (D_RAD/6)
64    
65     /* total width and height of controls incl. shadow */
66     #define OSD_W (2*D_RAD + OSD_SHADOW)
67     #define OSD_H (2*D_RAD + Z_STEP + 2*Z_RAD + OSD_SHADOW)
68    
69     #define OSD_LBL_SHADOW (OSD_SHADOW/2)
70    
71     #define Z_TOP (2 * D_RAD + Z_STEP)
72     #define Z_MID (Z_TOP + Z_RAD)
73     #define Z_BOT (Z_MID + Z_RAD)
74     #define Z_LEFT (Z_RAD)
75     #define Z_RIGHT (2 * D_RAD - Z_RAD)
76    
77     /* create the cairo shape used for the zoom buttons */
78     static void
79     osm_gps_map_osd_zoom_shape(cairo_t *cr, gint x, gint y) {
80     cairo_move_to (cr, x+Z_LEFT, y+Z_TOP);
81     cairo_line_to (cr, x+Z_RIGHT, y+Z_TOP);
82     cairo_arc (cr, x+Z_RIGHT, y+Z_MID, Z_RAD, -M_PI/2, M_PI/2);
83     cairo_line_to (cr, x+Z_LEFT, y+Z_BOT);
84     cairo_arc (cr, x+Z_LEFT, y+Z_MID, Z_RAD, M_PI/2, -M_PI/2);
85     }
86    
87     /* create the cairo shape used for the dpad */
88     static void
89     osm_gps_map_osd_dpad_shape(cairo_t *cr, gint x, gint y) {
90     cairo_arc (cr, x+D_RAD, y+D_RAD, D_RAD, 0, 2 * M_PI);
91     }
92    
93     typedef enum {
94     OSD_NONE = 0,
95     OSD_BG,
96     OSD_UP,
97     OSD_DOWN,
98     OSD_LEFT,
99     OSD_RIGHT,
100     OSD_IN,
101     OSD_OUT,
102     OSD_GPS
103     } osd_button_t;
104    
105     static gboolean
106     osm_gps_map_in_circle(gint x, gint y, gint cx, gint cy, gint rad)
107     {
108     return( pow(cx - x, 2) + pow(cy - y, 2) < rad * rad);
109     }
110    
111     /* check whether x/y is within the dpad */
112     static osd_button_t
113     osm_gps_map_osd_check_dpad(gint x, gint y)
114     {
115     /* within entire dpad circle */
116     if( osm_gps_map_in_circle(x, y, OSD_X + D_RAD, OSD_Y + D_RAD, D_RAD))
117     {
118     /* convert into position relative to dpads centre */
119     x -= (OSD_X + D_RAD);
120     y -= (OSD_Y + D_RAD);
121    
122     /* check for dpad center goes here! */
123     if( osm_gps_map_in_circle(x, y, 0, 0, D_RAD/3))
124     return OSD_GPS;
125    
126     if( y < 0 && abs(x) < abs(y))
127     return OSD_UP;
128    
129     if( y > 0 && abs(x) < abs(y))
130     return OSD_DOWN;
131    
132     if( x < 0 && abs(y) < abs(x))
133     return OSD_LEFT;
134    
135     if( x > 0 && abs(y) < abs(x))
136     return OSD_RIGHT;
137    
138     return OSD_BG;
139     }
140     return OSD_NONE;
141     }
142    
143     /* check whether x/y is within the zoom pads */
144     static osd_button_t
145     osm_gps_map_osd_check_zoom(gint x, gint y) {
146     if( x > OSD_X && x < (OSD_X + OSD_W) && y > Z_TOP && y < (OSD_Y+Z_BOT)) {
147    
148     /* within circle around (-) label */
149     if( osm_gps_map_in_circle(x, y, OSD_X + Z_LEFT, OSD_Y + Z_MID, Z_RAD))
150     return OSD_OUT;
151    
152     /* between center of (-) button and center of entire zoom control area */
153     if(x > OSD_LEFT && x < OSD_X + D_RAD)
154     return OSD_OUT;
155    
156     /* within circle around (+) label */
157     if( osm_gps_map_in_circle(x, y, OSD_X + Z_RIGHT, OSD_Y + Z_MID, Z_RAD))
158     return OSD_IN;
159    
160     /* between center of (+) button and center of entire zoom control area */
161     if(x < OSD_RIGHT && x > OSD_X + D_RAD)
162     return OSD_IN;
163     }
164    
165     return OSD_NONE;
166     }
167    
168     static osd_button_t
169     osm_gps_map_osd_check(gint x, gint y) {
170     osd_button_t but = OSD_NONE;
171    
172     /* first do a rough test for the OSD area. */
173     /* this is just to avoid an unnecessary detailed test */
174     if(x > OSD_X && x < OSD_X + OSD_W &&
175     y > OSD_Y && y < OSD_Y + OSD_H) {
176     but = osm_gps_map_osd_check_dpad(x, y);
177    
178     if(but == OSD_NONE)
179     but = osm_gps_map_osd_check_zoom(x, y);
180     }
181    
182     return but;
183     }
184    
185     static void
186     osm_gps_map_osd_shape_shadow(cairo_t *cr) {
187     cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
188     cairo_fill (cr);
189     cairo_stroke (cr);
190     }
191    
192     static void
193     osm_gps_map_osd_shape(cairo_t *cr) {
194     cairo_set_source_rgb (cr, 1, 1, 1);
195     cairo_fill_preserve (cr);
196     cairo_set_source_rgb (cr, OSD_COLOR);
197     cairo_set_line_width (cr, 1);
198     cairo_stroke (cr);
199     }
200    
201     static void
202     osm_gps_map_osd_dpad_labels(cairo_t *cr, gint x, gint y) {
203     /* move reference to dpad center */
204     x += D_RAD;
205     y += D_RAD;
206    
207     const static gint offset[][3][2] = {
208     /* left arrow/triangle */
209     { { -D_TIP+D_LEN, -D_WID }, { -D_LEN, D_WID }, { +D_LEN, D_WID } },
210     /* right arrow/triangle */
211     { { +D_TIP-D_LEN, -D_WID }, { +D_LEN, D_WID }, { -D_LEN, D_WID } },
212     /* top arrow/triangle */
213     { { -D_WID, -D_TIP+D_LEN }, { D_WID, -D_LEN }, { D_WID, +D_LEN } },
214     /* bottom arrow/triangle */
215     { { -D_WID, +D_TIP-D_LEN }, { D_WID, +D_LEN }, { D_WID, -D_LEN } }
216     };
217    
218     int i;
219     for(i=0;i<4;i++) {
220     cairo_move_to (cr, x + offset[i][0][0], y + offset[i][0][1]);
221     cairo_rel_line_to (cr, offset[i][1][0], offset[i][1][1]);
222     cairo_rel_line_to (cr, offset[i][2][0], offset[i][2][1]);
223     }
224     }
225    
226     /* draw the sattelite dish icon in the center of the dpad */
227     #define GPS_V0 (D_RAD/8)
228     #define GPS_V1 (D_RAD/10)
229     #define GPS_V2 (D_RAD/5)
230    
231     /* draw a satellite receiver dish */
232     static void
233     osm_gps_map_osd_dpad_gps(cairo_t *cr, gint x, gint y) {
234     /* move reference to dpad center */
235     x += D_RAD;
236     y += D_RAD + GPS_V0;
237    
238     cairo_move_to (cr, x-GPS_V0, y+GPS_V0);
239     cairo_rel_line_to (cr, +GPS_V0, -GPS_V0);
240     cairo_rel_line_to (cr, +GPS_V0, +GPS_V0);
241     cairo_close_path (cr);
242    
243     cairo_move_to (cr, x+GPS_V1-GPS_V2, y-2*GPS_V2);
244     cairo_curve_to (cr, x-GPS_V2, y, x+GPS_V1, y+GPS_V1, x+GPS_V1+GPS_V2, y);
245     cairo_close_path (cr);
246    
247     x += GPS_V1;
248     cairo_move_to (cr, x, y-GPS_V2);
249     cairo_rel_line_to (cr, +GPS_V1, -GPS_V1);
250     }
251    
252     #define Z_LEN (2*Z_RAD/3)
253    
254     static void
255     osm_gps_map_osd_zoom_labels(cairo_t *cr, gint x, gint y) {
256     cairo_move_to (cr, x + Z_LEFT - Z_LEN, y + Z_MID);
257     cairo_line_to (cr, x + Z_LEFT + Z_LEN, y + Z_MID);
258    
259     cairo_move_to (cr, x + Z_RIGHT, y + Z_MID - Z_LEN);
260     cairo_line_to (cr, x + Z_RIGHT, y + Z_MID + Z_LEN);
261     cairo_move_to (cr, x + Z_RIGHT - Z_LEN, y + Z_MID);
262     cairo_line_to (cr, x + Z_RIGHT + Z_LEN, y + Z_MID);
263     }
264    
265     static void
266     osm_gps_map_osd_labels(cairo_t *cr, gint width, gboolean enabled) {
267     if(enabled) cairo_set_source_rgb (cr, OSD_COLOR);
268     else cairo_set_source_rgb (cr, OSD_COLOR_DISABLED);
269     cairo_set_line_width (cr, width);
270     cairo_stroke (cr);
271     }
272    
273     static void
274     osm_gps_map_osd_labels_shadow(cairo_t *cr, gint width, gboolean enabled) {
275     cairo_set_source_rgba (cr, 0, 0, 0, enabled?0.3:0.15);
276     cairo_set_line_width (cr, width);
277     cairo_stroke (cr);
278     }
279    
280     static void
281     osm_gps_map_osd_render(OsmGpsMapPrivate *priv) {
282    
283     /* first fill with transparency */
284     cairo_t *cr = cairo_create(priv->osd.overlay);
285     cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
286     cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
287     cairo_paint(cr);
288    
289     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
290    
291     /* --------- draw zoom and dpad shape shadow ----------- */
292     gint x = 0, y = 0;
293    
294     osm_gps_map_osd_zoom_shape(cr, x + OSD_SHADOW, y + OSD_SHADOW);
295     osm_gps_map_osd_shape_shadow(cr);
296     osm_gps_map_osd_dpad_shape(cr, x + OSD_SHADOW, y + OSD_SHADOW);
297     osm_gps_map_osd_shape_shadow(cr);
298    
299     /* --------- draw zoom and dpad shape ----------- */
300    
301     osm_gps_map_osd_zoom_shape(cr, x, y);
302     osm_gps_map_osd_shape(cr);
303     osm_gps_map_osd_dpad_shape(cr, x, y);
304     osm_gps_map_osd_shape(cr);
305    
306     /* --------- draw zoom and dpad labels --------- */
307    
308     osm_gps_map_osd_zoom_labels(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW);
309     osm_gps_map_osd_dpad_labels(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW);
310     osm_gps_map_osd_labels_shadow(cr, Z_RAD/3, TRUE);
311     osm_gps_map_osd_dpad_gps(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW);
312     osm_gps_map_osd_labels_shadow(cr, Z_RAD/6, priv->osd.cb != NULL);
313    
314     osm_gps_map_osd_zoom_labels(cr, x, y);
315     osm_gps_map_osd_dpad_labels(cr, x, y);
316     osm_gps_map_osd_labels(cr, Z_RAD/3, TRUE);
317     osm_gps_map_osd_dpad_gps(cr, x, y);
318     osm_gps_map_osd_labels(cr, Z_RAD/6, priv->osd.cb != NULL);
319    
320     cairo_destroy(cr);
321     }
322    
323     static void
324     osm_gps_map_osd_draw_controls (OsmGpsMap *map, gint xoffset, gint yoffset)
325     {
326     OsmGpsMapPrivate *priv = map->priv;
327    
328     /* backup previous contents */
329     if(!priv->osd.backup)
330     priv->osd.backup = gdk_pixmap_new(priv->pixmap, OSD_W+2, OSD_H+2, -1);
331    
332     gint x = OSD_X + EXTRA_BORDER + xoffset;
333     gint y = OSD_Y + EXTRA_BORDER + yoffset;
334    
335     /* create backup of background */
336     gdk_draw_drawable(priv->osd.backup,
337     GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(GTK_WIDGET(map))],
338     priv->pixmap, x-1, y-1, 0, 0, OSD_W+2, OSD_H+2);
339    
340     priv->osd.backup_x = x-1;
341     priv->osd.backup_y = y-1;
342    
343    
344     #ifdef USE_CAIRO
345     /* OSD itself uses some off-screen rendering, so check if the */
346     /* offscreen buffer is present and create it if not */
347     if(!priv->osd.overlay) {
348     /* create overlay ... */
349     priv->osd.overlay =
350     cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W, OSD_H);
351     /* ... and render it */
352     osm_gps_map_osd_render(priv);
353     }
354    
355     // now draw this onto the original context
356     cairo_t *cr = gdk_cairo_create(priv->pixmap);
357     cairo_set_source_surface(cr, priv->osd.overlay, x, y);
358     cairo_paint(cr);
359     cairo_destroy(cr);
360    
361     #else
362     #warning "OSD control display lacks a non-cairo implementation!"
363     #endif
364     }
365    
366     static void
367     osm_gps_map_osd_restore (OsmGpsMap *map)
368     {
369     OsmGpsMapPrivate *priv = map->priv;
370    
371     /* restore backup of previous contents */
372     if(priv->osd.backup) {
373     /* create backup of background */
374     gdk_draw_drawable(priv->pixmap,
375     GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(GTK_WIDGET(map))],
376     priv->osd.backup, 0, 0,
377     priv->osd.backup_x, priv->osd.backup_y, OSD_W+2, OSD_H+2);
378     }
379     }
380    
381     #endif
382    
383     static gboolean
384     osm_gps_map_map_redraw (OsmGpsMap *map)
385     {
386     OsmGpsMapPrivate *priv = map->priv;
387    
388     priv->idle_map_redraw = 0;
389    
390     /* the motion_notify handler uses priv->pixmap to redraw the area; if we
391     * change it while we are dragging, we will end up showing it in the wrong
392     * place. This could be fixed by carefully recompute the coordinates, but
393     * for now it's easier just to disable redrawing the map while dragging */
394     if (priv->dragging)
395     return FALSE;
396    
397     priv->redraw_cycle++;
398    
399     /* draw white background to initialise pixmap */
400     gdk_draw_rectangle (
401     priv->pixmap,
402     GTK_WIDGET(map)->style->white_gc,
403     TRUE,
404     0, 0,
405     GTK_WIDGET(map)->allocation.width + EXTRA_BORDER * 2,
406     GTK_WIDGET(map)->allocation.height + EXTRA_BORDER * 2);
407    
408     osm_gps_map_fill_tiles_pixel(map);
409    
410     osm_gps_map_print_tracks(map);
411     osm_gps_map_draw_gps_point(map);
412     osm_gps_map_print_images(map);
413     #ifdef ENABLE_BALLOON
414     osm_gps_map_draw_balloon_int(map);
415     #endif
416     #ifdef ENABLE_OSD
417     osm_gps_map_osd_draw_controls(map, 0, 0);
418     #endif
419    
420     //osm_gps_map_osd_speed(map, 1.5);
421     osm_gps_map_purge_cache(map);
422     gtk_widget_queue_draw (GTK_WIDGET (map));
423    
424     return FALSE;
425     }
426    
427     static void
428     osm_gps_map_map_redraw_idle (OsmGpsMap *map)
429     {
430     OsmGpsMapPrivate *priv = map->priv;
431    
432     if (priv->idle_map_redraw == 0)
433     priv->idle_map_redraw = g_idle_add ((GSourceFunc)osm_gps_map_map_redraw, map);
434     }
435    
436     static void
437     osm_gps_map_init (OsmGpsMap *object)
438     {
439     OsmGpsMapPrivate *priv;
440    
441     priv = G_TYPE_INSTANCE_GET_PRIVATE (object, OSM_TYPE_GPS_MAP, OsmGpsMapPrivate);
442     object->priv = priv;
443    
444     priv->pixmap = NULL;
445    
446     priv->trip_history = NULL;
447     priv->gps = g_new0(coord_t, 1);
448     priv->gps_valid = FALSE;
449    
450     #ifdef ENABLE_BALLOON
451     priv->balloon.coo = g_new0(coord_t, 1);
452     priv->balloon.valid = FALSE;
453     priv->balloon.cb = NULL;
454     #endif
455    
456     #ifdef ENABLE_OSD
457     priv->osd.backup = NULL;
458     priv->osd.overlay = NULL;
459     priv->osd.cb = NULL;
460     #endif
461    
462     priv->tracks = NULL;
463     priv->images = NULL;
464    
465     priv->drag_counter = 0;
466     priv->drag_mouse_dx = 0;
467     priv->drag_mouse_dy = 0;
468     priv->drag_start_mouse_x = 0;
469     priv->drag_start_mouse_y = 0;
470    
471     priv->uri_format = 0;
472     priv->the_google = FALSE;
473    
474     priv->map_source = -1;
475    
476     #ifndef LIBSOUP22
477     //Change naumber of concurrent connections option?
478     priv->soup_session = soup_session_async_new_with_options(
479     SOUP_SESSION_USER_AGENT,
480     "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11",
481     NULL);
482     #else
483     /* libsoup-2.2 seems not to be able to set the user agent */
484     priv->soup_session = soup_session_async_new();
485     #endif
486    
487     //Hash table which maps tile d/l URIs to SoupMessage requests
488     priv->tile_queue = g_hash_table_new (g_str_hash, g_str_equal);
489    
490     //Some mapping providers (Google) have varying degrees of tiles at multiple
491     //zoom levels
492     priv->missing_tiles = g_hash_table_new (g_str_hash, g_str_equal);
493    
494     /* memory cache for most recently used tiles */
495     priv->tile_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
496     g_free, (GDestroyNotify)cached_tile_free);
497     priv->max_tile_cache_size = 20;
498    
499     gtk_widget_add_events (GTK_WIDGET (object),
500     GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
501     GDK_POINTER_MOTION_MASK |
502     GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
503     GTK_WIDGET_SET_FLAGS (object, GTK_CAN_FOCUS);
504    
505     g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, my_log_handler, NULL);
506     }
507    
508     #ifndef G_CHECKSUM_MD5
509     /* simple hash algorithm hack if md5 is not present */
510     static char *simple_hash(char *str) {
511     union {
512     char str[4];
513     gulong val;
514     } hash = { .val = 0x55555555 };
515    
516     while(*str) {
517     hash.str[(int)str & 3] ^= *str;
518     str++;
519     }
520     return g_strdup_printf("%08lX", hash.val);
521     }
522     #endif
523    
524     static GObject *
525     osm_gps_map_constructor (GType gtype, guint n_properties, GObjectConstructParam *properties)
526     {
527     GObject *object;
528     OsmGpsMapPrivate *priv;
529     OsmGpsMap *map;
530     const char *uri;
531    
532     //Always chain up to the parent constructor
533     object = G_OBJECT_CLASS(osm_gps_map_parent_class)->constructor(gtype, n_properties, properties);
534     map = OSM_GPS_MAP(object);
535     priv = OSM_GPS_MAP_PRIVATE(object);
536    
537     //user can specify a map source ID, or a repo URI as the map source
538     uri = osm_gps_map_source_get_repo_uri(OSM_GPS_MAP_SOURCE_NULL);
539     if ( (priv->map_source == 0) || (strcmp(priv->repo_uri, uri) == 0) ) {
540     g_debug("Using null source");
541     priv->map_source = OSM_GPS_MAP_SOURCE_NULL;
542    
543     priv->null_tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 256, 256);
544     gdk_pixbuf_fill(priv->null_tile, 0xcccccc00);
545     }
546     else if (priv->map_source >= 0) {
547     //check if the source given is valid
548     uri = osm_gps_map_source_get_repo_uri(priv->map_source);
549     if (uri) {
550     g_debug("Setting map source from ID");
551     g_free(priv->repo_uri);
552    
553     priv->repo_uri = g_strdup(uri);
554     priv->image_format = g_strdup(
555     osm_gps_map_source_get_image_format(priv->map_source));
556     priv->max_zoom = osm_gps_map_source_get_max_zoom(priv->map_source);
557     priv->min_zoom = osm_gps_map_source_get_min_zoom(priv->map_source);
558     }
559     }
560    
561     if (!priv->cache_dir_is_full_path) {
562     #ifdef G_CHECKSUM_MD5
563     char *md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, priv->repo_uri, -1);
564     #else
565     char *md5 = simple_hash(priv->repo_uri);
566     #endif
567    
568     if (priv->cache_dir) {
569     char *old = priv->cache_dir;
570     //the new cachedir is the given cache dir + the md5 of the repo_uri
571     priv->cache_dir = g_strdup_printf("%s%c%s", old, G_DIR_SEPARATOR, md5);
572     g_debug("Adjusting cache dir %s -> %s", old, priv->cache_dir);
573     g_free(old);
574     } else {
575     //the new cachedir is the current dir + the md5 of the repo_uri
576     priv->cache_dir = g_strdup(md5);
577     }
578    
579     g_free(md5);
580     }
581    
582     inspect_map_uri(map);
583    
584     return object;
585     }
586    
587     static void
588     osm_gps_map_dispose (GObject *object)
589     {
590     OsmGpsMap *map = OSM_GPS_MAP(object);
591     OsmGpsMapPrivate *priv = map->priv;
592    
593     if (priv->is_disposed)
594     return;
595    
596     priv->is_disposed = TRUE;
597    
598     soup_session_abort(priv->soup_session);
599     g_object_unref(priv->soup_session);
600    
601     g_hash_table_destroy(priv->tile_queue);
602     g_hash_table_destroy(priv->missing_tiles);
603     g_hash_table_destroy(priv->tile_cache);
604    
605     osm_gps_map_free_images(map);
606    
607     if(priv->pixmap)
608     g_object_unref (priv->pixmap);
609    
610     if (priv->null_tile)
611     g_object_unref (priv->null_tile);
612    
613     if(priv->gc_map)
614     g_object_unref(priv->gc_map);
615    
616     if (priv->idle_map_redraw != 0)
617     g_source_remove (priv->idle_map_redraw);
618    
619     g_free(priv->gps);
620    
621     #ifdef ENABLE_BALLOON
622     g_free(priv->balloon.coo);
623     #endif
624    
625     #ifdef ENABLE_OSD
626     if (priv->osd.backup)
627     g_object_unref(priv->osd.backup);
628    
629     if (priv->osd.overlay)
630     cairo_surface_destroy(priv->osd.overlay);
631     #endif
632    
633     G_OBJECT_CLASS (osm_gps_map_parent_class)->dispose (object);
634     }
635    
636     static void
637     osm_gps_map_finalize (GObject *object)
638     {
639     OsmGpsMap *map = OSM_GPS_MAP(object);
640     OsmGpsMapPrivate *priv = map->priv;
641    
642     g_free(priv->cache_dir);
643     g_free(priv->repo_uri);
644     g_free(priv->image_format);
645    
646     osm_gps_map_free_trip(map);
647     osm_gps_map_free_tracks(map);
648    
649     G_OBJECT_CLASS (osm_gps_map_parent_class)->finalize (object);
650     }
651    
652     static void
653     osm_gps_map_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
654     {
655     g_return_if_fail (OSM_IS_GPS_MAP (object));
656     OsmGpsMap *map = OSM_GPS_MAP(object);
657     OsmGpsMapPrivate *priv = map->priv;
658    
659     switch (prop_id)
660     {
661     case PROP_AUTO_CENTER:
662     priv->map_auto_center = g_value_get_boolean (value);
663     break;
664     case PROP_RECORD_TRIP_HISTORY:
665     priv->record_trip_history = g_value_get_boolean (value);
666     break;
667     case PROP_SHOW_TRIP_HISTORY:
668     priv->show_trip_history = g_value_get_boolean (value);
669     break;
670     case PROP_AUTO_DOWNLOAD:
671     priv->map_auto_download = g_value_get_boolean (value);
672     break;
673     case PROP_REPO_URI:
674     priv->repo_uri = g_value_dup_string (value);
675     break;
676     case PROP_PROXY_URI:
677     if ( g_value_get_string(value) ) {
678     priv->proxy_uri = g_value_dup_string (value);
679     g_debug("Setting proxy server: %s", priv->proxy_uri);
680    
681     #ifndef LIBSOUP22
682     GValue val = {0};
683    
684     SoupURI* uri = soup_uri_new(priv->proxy_uri);
685     g_value_init(&val, SOUP_TYPE_URI);
686     g_value_take_boxed(&val, uri);
687    
688     g_object_set_property(G_OBJECT(priv->soup_session),SOUP_SESSION_PROXY_URI,&val);
689     #else
690     SoupUri* uri = soup_uri_new(priv->proxy_uri);
691     g_object_set(G_OBJECT(priv->soup_session), SOUP_SESSION_PROXY_URI, uri, NULL);
692     #endif
693     } else
694     priv->proxy_uri = NULL;
695    
696     break;
697     case PROP_TILE_CACHE_DIR:
698     if ( g_value_get_string(value) )
699     priv->cache_dir = g_value_dup_string (value);
700     break;
701     case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
702     priv->cache_dir_is_full_path = g_value_get_boolean (value);
703     break;
704     case PROP_ZOOM:
705     priv->map_zoom = g_value_get_int (value);
706     break;
707     case PROP_MAX_ZOOM:
708     priv->max_zoom = g_value_get_int (value);
709     break;
710     case PROP_MIN_ZOOM:
711     priv->min_zoom = g_value_get_int (value);
712     break;
713     case PROP_MAP_X:
714     priv->map_x = g_value_get_int (value);
715     priv->center_coord_set = FALSE;
716     break;
717     case PROP_MAP_Y:
718     priv->map_y = g_value_get_int (value);
719     priv->center_coord_set = FALSE;
720     break;
721     case PROP_GPS_TRACK_WIDTH:
722     priv->ui_gps_track_width = g_value_get_int (value);
723     break;
724     case PROP_GPS_POINT_R1:
725     priv->ui_gps_point_inner_radius = g_value_get_int (value);
726     break;
727     case PROP_GPS_POINT_R2:
728     priv->ui_gps_point_outer_radius = g_value_get_int (value);
729     break;
730     case PROP_MAP_SOURCE:
731     priv->map_source = g_value_get_int (value);
732     break;
733     case PROP_IMAGE_FORMAT:
734     priv->image_format = g_value_dup_string (value);
735     break;
736     default:
737     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
738     break;
739     }
740     }
741    
742     static void
743     osm_gps_map_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
744     {
745     g_return_if_fail (OSM_IS_GPS_MAP (object));
746     float lat,lon;
747     OsmGpsMap *map = OSM_GPS_MAP(object);
748     OsmGpsMapPrivate *priv = map->priv;
749    
750     switch (prop_id)
751     {
752     case PROP_AUTO_CENTER:
753     g_value_set_boolean(value, priv->map_auto_center);
754     break;
755     case PROP_RECORD_TRIP_HISTORY:
756     g_value_set_boolean(value, priv->record_trip_history);
757     break;
758     case PROP_SHOW_TRIP_HISTORY:
759     g_value_set_boolean(value, priv->show_trip_history);
760     break;
761     case PROP_AUTO_DOWNLOAD:
762     g_value_set_boolean(value, priv->map_auto_download);
763     break;
764     case PROP_REPO_URI:
765     g_value_set_string(value, priv->repo_uri);
766     break;
767     case PROP_PROXY_URI:
768     g_value_set_string(value, priv->proxy_uri);
769     break;
770     case PROP_TILE_CACHE_DIR:
771     g_value_set_string(value, priv->cache_dir);
772     break;
773     case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
774     g_value_set_boolean(value, priv->cache_dir_is_full_path);
775     break;
776     case PROP_ZOOM:
777     g_value_set_int(value, priv->map_zoom);
778     break;
779     case PROP_MAX_ZOOM:
780     g_value_set_int(value, priv->max_zoom);
781     break;
782     case PROP_MIN_ZOOM:
783     g_value_set_int(value, priv->min_zoom);
784     break;
785     case PROP_LATITUDE:
786     lat = pixel2lat(priv->map_zoom,
787     priv->map_y + (GTK_WIDGET(map)->allocation.height / 2));
788     g_value_set_float(value, rad2deg(lat));
789     break;
790     case PROP_LONGITUDE:
791     lon = pixel2lon(priv->map_zoom,
792     priv->map_x + (GTK_WIDGET(map)->allocation.width / 2));
793     g_value_set_float(value, rad2deg(lon));
794     break;
795     case PROP_MAP_X:
796     g_value_set_int(value, priv->map_x);
797     break;
798     case PROP_MAP_Y:
799     g_value_set_int(value, priv->map_y);
800     break;
801     case PROP_TILES_QUEUED:
802     g_value_set_int(value, g_hash_table_size(priv->tile_queue));
803     break;
804     case PROP_GPS_TRACK_WIDTH:
805     g_value_set_int(value, priv->ui_gps_track_width);
806     break;
807     case PROP_GPS_POINT_R1:
808     g_value_set_int(value, priv->ui_gps_point_inner_radius);
809     break;
810     case PROP_GPS_POINT_R2:
811     g_value_set_int(value, priv->ui_gps_point_outer_radius);
812     break;
813     case PROP_MAP_SOURCE:
814     g_value_set_int(value, priv->map_source);
815     break;
816     case PROP_IMAGE_FORMAT:
817     g_value_set_string(value, priv->image_format);
818     break;
819     default:
820     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
821     break;
822     }
823     }
824    
825     static gboolean
826     osm_gps_map_scroll_event (GtkWidget *widget, GdkEventScroll *event)
827     {
828     OsmGpsMap *map = OSM_GPS_MAP(widget);
829     OsmGpsMapPrivate *priv = map->priv;
830    
831     if (event->direction == GDK_SCROLL_UP)
832     {
833     osm_gps_map_set_zoom(map, priv->map_zoom+1);
834     }
835     else
836     {
837     osm_gps_map_set_zoom(map, priv->map_zoom-1);
838     }
839    
840     return FALSE;
841     }
842    
843     static gboolean
844     osm_gps_map_button_press (GtkWidget *widget, GdkEventButton *event)
845     {
846     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
847    
848     #ifdef ENABLE_BALLOON
849     /* don't drag if the user clicked within the balloon */
850     if (osm_gps_map_in_balloon(priv,
851     event->x + EXTRA_BORDER,
852     event->y + EXTRA_BORDER))
853     {
854     priv->drag_counter = -1;
855     return FALSE;
856     }
857     #endif
858    
859     #ifdef ENABLE_OSD
860     #define SCROLL_STEP 10
861    
862     /* pressed inside OSD control? */
863     osd_button_t but = osm_gps_map_osd_check(event->x, event->y);
864     if(but != OSD_NONE)
865     {
866     priv->drag_counter = -1;
867    
868     switch(but) {
869     case OSD_GPS:
870     priv->osd.cb(priv->osd.data);
871     break;
872    
873     case OSD_UP:
874     priv->map_y -= GTK_WIDGET(widget)->allocation.height/SCROLL_STEP;
875     priv->center_coord_set = FALSE;
876     break;
877    
878     case OSD_DOWN:
879     priv->map_y += GTK_WIDGET(widget)->allocation.height/SCROLL_STEP;
880     priv->center_coord_set = FALSE;
881     break;
882    
883     case OSD_LEFT:
884     priv->map_x -= GTK_WIDGET(widget)->allocation.width/SCROLL_STEP;
885     priv->center_coord_set = FALSE;
886     break;
887    
888     case OSD_RIGHT:
889     priv->map_x += GTK_WIDGET(widget)->allocation.width/SCROLL_STEP;
890     priv->center_coord_set = FALSE;
891     break;
892    
893     case OSD_IN:
894     osm_gps_map_set_zoom(OSM_GPS_MAP(widget), priv->map_zoom+1);
895     break;
896    
897     case OSD_OUT:
898     osm_gps_map_set_zoom(OSM_GPS_MAP(widget), priv->map_zoom-1);
899     break;
900    
901     default:
902     break;
903     }
904    
905     osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
906    
907     return FALSE;
908     }
909     #endif
910    
911     priv->drag_counter = 0;
912     priv->drag_start_mouse_x = (int) event->x;
913     priv->drag_start_mouse_y = (int) event->y;
914     priv->drag_start_map_x = priv->map_x;
915     priv->drag_start_map_y = priv->map_y;
916    
917     return FALSE;
918     }
919    
920     static gboolean
921     osm_gps_map_button_release (GtkWidget *widget, GdkEventButton *event)
922     {
923     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
924    
925     #ifdef ENABLE_BALLOON
926     /* released inside the balloon? */
927     if (osm_gps_map_in_balloon(priv,
928     event->x + EXTRA_BORDER,
929     event->y + EXTRA_BORDER))
930     {
931     osm_gps_map_handle_balloon_click(OSM_GPS_MAP(widget),
932     event->x - priv->balloon.rect.x + EXTRA_BORDER,
933     event->y - priv->balloon.rect.y + EXTRA_BORDER);
934     }
935     #endif
936    
937     if (priv->dragging)
938     {
939     priv->dragging = FALSE;
940    
941     priv->map_x = priv->drag_start_map_x;
942     priv->map_y = priv->drag_start_map_y;
943    
944     priv->map_x += (priv->drag_start_mouse_x - (int) event->x);
945     priv->map_y += (priv->drag_start_mouse_y - (int) event->y);
946    
947     priv->center_coord_set = FALSE;
948    
949     osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
950     }
951    
952     priv->drag_mouse_dx = 0;
953     priv->drag_mouse_dy = 0;
954     priv->drag_counter = -1;
955    
956     return FALSE;
957     }
958    
959     static gboolean
960     osm_gps_map_expose (GtkWidget *widget, GdkEventExpose *event);
961    
962     static gboolean
963     osm_gps_map_motion_notify (GtkWidget *widget, GdkEventMotion *event)
964     {
965     int x, y;
966     GdkModifierType state;
967     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
968    
969     if (event->is_hint)
970     gdk_window_get_pointer (event->window, &x, &y, &state);
971     else
972     {
973     x = event->x;
974     y = event->y;
975     state = event->state;
976     }
977    
978     // are we being dragged
979     if (!(state & GDK_BUTTON1_MASK))
980     return FALSE;
981    
982     if (priv->drag_counter < 0)
983     return FALSE;
984    
985     priv->drag_counter++;
986    
987     // we havent dragged more than 6 pixels
988     if (priv->drag_counter < 6)
989     return FALSE;
990    
991     #ifdef USE_MAEMO
992     /* reduce update frequency on maemo to keep screen update fluid */
993     static guint32 last_time = 0;
994    
995     if(event->time - last_time < 100) return FALSE;
996     last_time = event->time;
997     #endif
998    
999     priv->dragging = TRUE;
1000    
1001     if (priv->map_auto_center)
1002     g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
1003    
1004     priv->drag_mouse_dx = x - priv->drag_start_mouse_x;
1005     priv->drag_mouse_dy = y - priv->drag_start_mouse_y;
1006    
1007     #ifdef ENABLE_OSD
1008     /* undo OSD */
1009     osm_gps_map_osd_restore (OSM_GPS_MAP(widget));
1010    
1011     /* draw new OSD */
1012     osm_gps_map_osd_draw_controls (OSM_GPS_MAP(widget),
1013     -priv->drag_mouse_dx,
1014     -priv->drag_mouse_dy);
1015     #endif
1016    
1017     osm_gps_map_expose (widget, NULL);
1018    
1019    
1020     return FALSE;
1021     }
1022    
1023     static gboolean
1024     osm_gps_map_configure (GtkWidget *widget, GdkEventConfigure *event)
1025     {
1026     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1027    
1028     /* create pixmap */
1029     if (priv->pixmap)
1030     g_object_unref (priv->pixmap);
1031    
1032     priv->pixmap = gdk_pixmap_new (
1033     widget->window,
1034     widget->allocation.width + EXTRA_BORDER * 2,
1035     widget->allocation.height + EXTRA_BORDER * 2,
1036     -1);
1037    
1038     /* and gc, used for clipping (I think......) */
1039     if(priv->gc_map)
1040     g_object_unref(priv->gc_map);
1041    
1042     priv->gc_map = gdk_gc_new(priv->pixmap);
1043    
1044     osm_gps_map_map_redraw(OSM_GPS_MAP(widget));
1045    
1046     return FALSE;
1047     }
1048    
1049     static gboolean
1050     osm_gps_map_expose (GtkWidget *widget, GdkEventExpose *event)
1051     {
1052     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1053    
1054     gdk_draw_drawable (
1055     widget->window,
1056     widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1057     priv->pixmap,
1058     0,0,
1059     priv->drag_mouse_dx - EXTRA_BORDER,
1060     priv->drag_mouse_dy - EXTRA_BORDER,
1061     -1,-1);
1062    
1063     //Paint white outside of the map if dragging. Its less
1064     //ugly than painting the corrupted map
1065     if(priv->drag_mouse_dx>EXTRA_BORDER) {
1066     gdk_draw_rectangle (
1067     widget->window,
1068     widget->style->white_gc,
1069     TRUE,
1070     0, 0,
1071     priv->drag_mouse_dx - EXTRA_BORDER,
1072     widget->allocation.height);
1073     }
1074     else if (-priv->drag_mouse_dx > EXTRA_BORDER)
1075     {
1076     gdk_draw_rectangle (
1077     widget->window,
1078     widget->style->white_gc,
1079     TRUE,
1080     priv->drag_mouse_dx + widget->allocation.width + EXTRA_BORDER, 0,
1081     -priv->drag_mouse_dx - EXTRA_BORDER,
1082     widget->allocation.height);
1083     }
1084    
1085     if (priv->drag_mouse_dy>EXTRA_BORDER) {
1086     gdk_draw_rectangle (
1087     widget->window,
1088     widget->style->white_gc,
1089     TRUE,
1090     0, 0,
1091     widget->allocation.width,
1092     priv->drag_mouse_dy - EXTRA_BORDER);
1093     }
1094     else if (-priv->drag_mouse_dy > EXTRA_BORDER)
1095     {
1096     gdk_draw_rectangle (
1097     widget->window,
1098     widget->style->white_gc,
1099     TRUE,
1100     0, priv->drag_mouse_dy + widget->allocation.height + EXTRA_BORDER,
1101     widget->allocation.width,
1102     -priv->drag_mouse_dy - EXTRA_BORDER);
1103     }
1104     #if 0
1105     if(!priv->dragging)
1106     gdk_draw_drawable (
1107     widget->window,
1108     widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1109     priv->pixmap,
1110     event->area.x + EXTRA_BORDER,
1111     event->area.y + EXTRA_BORDER,
1112     event->area.x, event->area.y,
1113     event->area.width, event->area.height);
1114     #endif
1115     return FALSE;
1116     }
1117    
1118     static void
1119     osm_gps_map_class_init (OsmGpsMapClass *klass)
1120     {
1121     GObjectClass* object_class = G_OBJECT_CLASS (klass);
1122     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1123    
1124     g_type_class_add_private (klass, sizeof (OsmGpsMapPrivate));
1125    
1126     object_class->dispose = osm_gps_map_dispose;
1127     object_class->finalize = osm_gps_map_finalize;
1128     object_class->constructor = osm_gps_map_constructor;
1129     object_class->set_property = osm_gps_map_set_property;
1130     object_class->get_property = osm_gps_map_get_property;
1131    
1132     widget_class->expose_event = osm_gps_map_expose;
1133     widget_class->configure_event = osm_gps_map_configure;
1134     widget_class->button_press_event = osm_gps_map_button_press;
1135     widget_class->button_release_event = osm_gps_map_button_release;
1136     widget_class->motion_notify_event = osm_gps_map_motion_notify;
1137     widget_class->scroll_event = osm_gps_map_scroll_event;
1138    
1139     g_object_class_install_property (object_class,
1140     PROP_AUTO_CENTER,
1141     g_param_spec_boolean ("auto-center",
1142     "auto center",
1143     "map auto center",
1144     TRUE,
1145     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1146    
1147     g_object_class_install_property (object_class,
1148     PROP_RECORD_TRIP_HISTORY,
1149     g_param_spec_boolean ("record-trip-history",
1150     "record trip history",
1151     "should all gps points be recorded in a trip history",
1152     TRUE,
1153     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1154    
1155     g_object_class_install_property (object_class,
1156     PROP_SHOW_TRIP_HISTORY,
1157     g_param_spec_boolean ("show-trip-history",
1158     "show trip history",
1159     "should the recorded trip history be shown on the map",
1160     TRUE,
1161     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1162    
1163     g_object_class_install_property (object_class,
1164     PROP_AUTO_DOWNLOAD,
1165     g_param_spec_boolean ("auto-download",
1166     "auto download",
1167     "map auto download",
1168     TRUE,
1169     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1170    
1171     g_object_class_install_property (object_class,
1172     PROP_REPO_URI,
1173     g_param_spec_string ("repo-uri",
1174     "repo uri",
1175     "map source tile repository uri",
1176     OSM_REPO_URI,
1177     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1178    
1179     g_object_class_install_property (object_class,
1180     PROP_PROXY_URI,
1181     g_param_spec_string ("proxy-uri",
1182     "proxy uri",
1183     "http proxy uri on NULL",
1184     NULL,
1185     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1186    
1187     g_object_class_install_property (object_class,
1188     PROP_TILE_CACHE_DIR,
1189     g_param_spec_string ("tile-cache",
1190     "tile cache",
1191     "osm local tile cache dir",
1192     NULL,
1193     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1194    
1195     g_object_class_install_property (object_class,
1196     PROP_TILE_CACHE_DIR_IS_FULL_PATH,
1197     g_param_spec_boolean ("tile-cache-is-full-path",
1198     "tile cache is full path",
1199     "if true, the path passed to tile-cache is interpreted as the full cache path",
1200     FALSE,
1201     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1202    
1203     g_object_class_install_property (object_class,
1204     PROP_ZOOM,
1205     g_param_spec_int ("zoom",
1206     "zoom",
1207     "zoom level",
1208     MIN_ZOOM, /* minimum property value */
1209     MAX_ZOOM, /* maximum property value */
1210     3,
1211     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1212    
1213     g_object_class_install_property (object_class,
1214     PROP_MAX_ZOOM,
1215     g_param_spec_int ("max-zoom",
1216     "max zoom",
1217     "maximum zoom level",
1218     MIN_ZOOM, /* minimum property value */
1219     MAX_ZOOM, /* maximum property value */
1220     OSM_MAX_ZOOM,
1221     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1222    
1223     g_object_class_install_property (object_class,
1224     PROP_MIN_ZOOM,
1225     g_param_spec_int ("min-zoom",
1226     "min zoom",
1227     "minimum zoom level",
1228     MIN_ZOOM, /* minimum property value */
1229     MAX_ZOOM, /* maximum property value */
1230     OSM_MIN_ZOOM,
1231     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1232    
1233     g_object_class_install_property (object_class,
1234     PROP_LATITUDE,
1235     g_param_spec_float ("latitude",
1236     "latitude",
1237     "latitude in degrees",
1238     -90.0, /* minimum property value */
1239     90.0, /* maximum property value */
1240     0,
1241     G_PARAM_READABLE));
1242    
1243     g_object_class_install_property (object_class,
1244     PROP_LONGITUDE,
1245     g_param_spec_float ("longitude",
1246     "longitude",
1247     "longitude in degrees",
1248     -180.0, /* minimum property value */
1249     180.0, /* maximum property value */
1250     0,
1251     G_PARAM_READABLE));
1252    
1253     g_object_class_install_property (object_class,
1254     PROP_MAP_X,
1255     g_param_spec_int ("map-x",
1256     "map-x",
1257     "initial map x location",
1258     G_MININT, /* minimum property value */
1259     G_MAXINT, /* maximum property value */
1260     890,
1261     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1262    
1263     g_object_class_install_property (object_class,
1264     PROP_MAP_Y,
1265     g_param_spec_int ("map-y",
1266     "map-y",
1267     "initial map y location",
1268     G_MININT, /* minimum property value */
1269     G_MAXINT, /* maximum property value */
1270     515,
1271     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1272    
1273     g_object_class_install_property (object_class,
1274     PROP_TILES_QUEUED,
1275     g_param_spec_int ("tiles-queued",
1276     "tiles-queued",
1277     "number of tiles currently waiting to download",
1278     G_MININT, /* minimum property value */
1279     G_MAXINT, /* maximum property value */
1280     0,
1281     G_PARAM_READABLE));
1282    
1283     g_object_class_install_property (object_class,
1284     PROP_GPS_TRACK_WIDTH,
1285     g_param_spec_int ("gps-track-width",
1286     "gps-track-width",
1287     "width of the lines drawn for the gps track",
1288     1, /* minimum property value */
1289     G_MAXINT, /* maximum property value */
1290     4,
1291     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1292    
1293     g_object_class_install_property (object_class,
1294     PROP_GPS_POINT_R1,
1295     g_param_spec_int ("gps-track-point-radius",
1296     "gps-track-point-radius",
1297     "radius of the gps point inner circle",
1298     0, /* minimum property value */
1299     G_MAXINT, /* maximum property value */
1300     10,
1301     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1302    
1303     g_object_class_install_property (object_class,
1304     PROP_GPS_POINT_R2,
1305     g_param_spec_int ("gps-track-highlight-radius",
1306     "gps-track-highlight-radius",
1307     "radius of the gps point highlight circle",
1308     0, /* minimum property value */
1309     G_MAXINT, /* maximum property value */
1310     20,
1311     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1312    
1313     g_object_class_install_property (object_class,
1314     PROP_MAP_SOURCE,
1315     g_param_spec_int ("map-source",
1316     "map source",
1317     "map source ID",
1318     -1, /* minimum property value */
1319     G_MAXINT, /* maximum property value */
1320     -1,
1321     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1322    
1323     g_object_class_install_property (object_class,
1324     PROP_IMAGE_FORMAT,
1325     g_param_spec_string ("image-format",
1326     "image format",
1327     "map source tile repository image format (jpg, png)",
1328     OSM_IMAGE_FORMAT,
1329     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1330     }
1331    
1332     const char*
1333     osm_gps_map_source_get_friendly_name(OsmGpsMapSource_t source)
1334     {
1335     switch(source)
1336     {
1337     case OSM_GPS_MAP_SOURCE_NULL:
1338     return "None";
1339     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
1340     return "OpenStreetMap";
1341     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
1342     return "OpenStreetMap Renderer";
1343     case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
1344     return "OpenAerialMap";
1345     case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
1346     return "Maps-For-Free";
1347     case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
1348     return "Google Maps";
1349     case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
1350     return "Google Satellite";
1351     case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
1352     return "Google Hybrid";
1353     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
1354     return "Virtual Earth";
1355     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
1356     return "Virtual Earth Satellite";
1357     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
1358     return "Virtual Earth Hybrid";
1359     case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
1360     return "Yahoo Maps";
1361     case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
1362     return "Yahoo Satellite";
1363     case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
1364     return "Yahoo Hybrid";
1365     default:
1366     return NULL;
1367     }
1368     return NULL;
1369     }
1370    
1371     //http://www.internettablettalk.com/forums/showthread.php?t=5209
1372     //https://garage.maemo.org/plugins/scmsvn/viewcvs.php/trunk/src/maps.c?root=maemo-mapper&view=markup
1373     //http://www.ponies.me.uk/maps/GoogleTileUtils.java
1374     //http://www.mgmaps.com/cache/MapTileCacher.perl
1375     const char*
1376     osm_gps_map_source_get_repo_uri(OsmGpsMapSource_t source)
1377     {
1378     switch(source)
1379     {
1380     case OSM_GPS_MAP_SOURCE_NULL:
1381     return "none://";
1382     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
1383     return OSM_REPO_URI;
1384     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
1385     return "http://tah.openstreetmap.org/Tiles/tile/#Z/#X/#Y.png";
1386     case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
1387     return "http://tile.openaerialmap.org/tiles/1.0.0/openaerialmap-900913/#Z/#X/#Y.jpg";
1388     case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
1389     return "http://maps-for-free.com/layer/relief/z#Z/row#Y/#Z_#X-#Y.jpg";
1390     case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
1391     return "http://mt#R.google.com/vt/v=w2.97&x=#X&y=#Y&z=#Z";
1392     case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
1393     return "http://khm#R.google.com/kh?n=404&v=3&t=#Q";
1394     case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
1395     return NULL; /* No longer working "http://mt#R.google.com/mt?n=404&v=w2t.99&x=#X&y=#Y&zoom=#S" */
1396     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
1397     return "http://a#R.ortho.tiles.virtualearth.net/tiles/r#W.jpeg?g=50";
1398     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
1399     return "http://a#R.ortho.tiles.virtualearth.net/tiles/a#W.jpeg?g=50";
1400     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
1401     return "http://a#R.ortho.tiles.virtualearth.net/tiles/h#W.jpeg?g=50";
1402     case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
1403     case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
1404     case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
1405     /* TODO: Implement signed Y, aka U
1406     * http://us.maps3.yimg.com/aerial.maps.yimg.com/ximg?v=1.7&t=a&s=256&x=%d&y=%-d&z=%d
1407     * x = tilex,
1408     * y = (1 << (MAX_ZOOM - zoom)) - tiley - 1,
1409     * z = zoom - (MAX_ZOOM - 17));
1410     */
1411     return NULL;
1412     default:
1413     return NULL;
1414     }
1415     return NULL;
1416     }
1417    
1418     const char *
1419     osm_gps_map_source_get_image_format(OsmGpsMapSource_t source)
1420     {
1421     switch(source) {
1422     case OSM_GPS_MAP_SOURCE_NULL:
1423     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
1424     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
1425     return "png";
1426     case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
1427     case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
1428     case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
1429     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
1430     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
1431     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
1432     case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
1433     case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
1434     case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
1435     case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
1436     case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
1437     return "jpg";
1438     default:
1439     return "bin";
1440     }
1441     return "bin";
1442     }
1443    
1444    
1445     int
1446     osm_gps_map_source_get_min_zoom(OsmGpsMapSource_t source)
1447     {
1448     return 1;
1449     }
1450    
1451     int
1452     osm_gps_map_source_get_max_zoom(OsmGpsMapSource_t source)
1453     {
1454     switch(source) {
1455     case OSM_GPS_MAP_SOURCE_NULL:
1456     return 18;
1457     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
1458     return OSM_MAX_ZOOM;
1459     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
1460     case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
1461     case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
1462     case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
1463     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
1464     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
1465     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
1466     case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
1467     case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
1468     case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
1469     return 17;
1470     case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
1471     return 11;
1472     case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
1473     return 18;
1474     default:
1475     return 17;
1476     }
1477     return 17;
1478     }
1479    
1480     void
1481     osm_gps_map_download_maps (OsmGpsMap *map, coord_t *pt1, coord_t *pt2, int zoom_start, int zoom_end)
1482     {
1483     int i,j,zoom,num_tiles;
1484     OsmGpsMapPrivate *priv = map->priv;
1485    
1486     if (pt1 && pt2)
1487     {
1488     gchar *filename;
1489     num_tiles = 0;
1490     zoom_end = CLAMP(zoom_end, priv->min_zoom, priv->max_zoom);
1491     g_debug("Download maps: z:%d->%d",zoom_start, zoom_end);
1492    
1493     for(zoom=zoom_start; zoom<=zoom_end; zoom++)
1494     {
1495     int x1,y1,x2,y2;
1496    
1497     x1 = (int)floor((float)lon2pixel(zoom, pt1->rlon) / (float)TILESIZE);
1498     y1 = (int)floor((float)lat2pixel(zoom, pt1->rlat) / (float)TILESIZE);
1499    
1500     x2 = (int)floor((float)lon2pixel(zoom, pt2->rlon) / (float)TILESIZE);
1501     y2 = (int)floor((float)lat2pixel(zoom, pt2->rlat) / (float)TILESIZE);
1502    
1503     // loop x1-x2
1504     for(i=x1; i<=x2; i++)
1505     {
1506     // loop y1 - y2
1507     for(j=y1; j<=y2; j++)
1508     {
1509     // x = i, y = j
1510     filename = g_strdup_printf("%s%c%d%c%d%c%d.%s",
1511     priv->cache_dir, G_DIR_SEPARATOR,
1512     zoom, G_DIR_SEPARATOR,
1513     i, G_DIR_SEPARATOR,
1514     j,
1515     priv->image_format);
1516     if (!g_file_test(filename, G_FILE_TEST_EXISTS))
1517     {
1518     osm_gps_map_download_tile(map, zoom, i, j, FALSE);
1519     num_tiles++;
1520     }
1521     g_free(filename);
1522     }
1523     }
1524     g_debug("DL @Z:%d = %d tiles",zoom,num_tiles);
1525     }
1526     }
1527     }
1528    
1529     void
1530     osm_gps_map_get_bbox (OsmGpsMap *map, coord_t *pt1, coord_t *pt2)
1531     {
1532     OsmGpsMapPrivate *priv = map->priv;
1533    
1534     if (pt1 && pt2) {
1535     pt1->rlat = pixel2lat(priv->map_zoom, priv->map_y);
1536     pt1->rlon = pixel2lon(priv->map_zoom, priv->map_x);
1537     pt2->rlat = pixel2lat(priv->map_zoom, priv->map_y + GTK_WIDGET(map)->allocation.height);
1538     pt2->rlon = pixel2lon(priv->map_zoom, priv->map_x + GTK_WIDGET(map)->allocation.width);
1539    
1540     g_debug("BBOX: %f %f %f %f", pt1->rlat, pt1->rlon, pt2->rlat, pt2->rlon);
1541     }
1542     }
1543    
1544     void
1545     osm_gps_map_set_mapcenter (OsmGpsMap *map, float latitude, float longitude, int zoom)
1546     {
1547     osm_gps_map_set_center (map, latitude, longitude);
1548     osm_gps_map_set_zoom (map, zoom);
1549     }
1550    
1551     void
1552     osm_gps_map_set_center (OsmGpsMap *map, float latitude, float longitude)
1553     {
1554     int pixel_x, pixel_y;
1555     OsmGpsMapPrivate *priv;
1556    
1557     g_return_if_fail (OSM_IS_GPS_MAP (map));
1558     priv = map->priv;
1559    
1560     priv->center_rlat = deg2rad(latitude);
1561     priv->center_rlon = deg2rad(longitude);
1562     priv->center_coord_set = TRUE;
1563    
1564     // pixel_x,y, offsets
1565     pixel_x = lon2pixel(priv->map_zoom, priv->center_rlon);
1566     pixel_y = lat2pixel(priv->map_zoom, priv->center_rlat);
1567    
1568     priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
1569     priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
1570    
1571     osm_gps_map_map_redraw_idle(map);
1572     }
1573    
1574     int
1575     osm_gps_map_set_zoom (OsmGpsMap *map, int zoom)
1576     {
1577     int zoom_old;
1578     double factor = 0.0;
1579     int width_center, height_center;
1580     OsmGpsMapPrivate *priv;
1581    
1582     g_return_val_if_fail (OSM_IS_GPS_MAP (map), 0);
1583     priv = map->priv;
1584    
1585     if (zoom != priv->map_zoom)
1586     {
1587     width_center = GTK_WIDGET(map)->allocation.width / 2;
1588     height_center = GTK_WIDGET(map)->allocation.height / 2;
1589    
1590     zoom_old = priv->map_zoom;
1591     //constrain zoom min_zoom -> max_zoom
1592     priv->map_zoom = CLAMP(zoom, priv->min_zoom, priv->max_zoom);
1593    
1594     if (priv->center_coord_set)
1595     {
1596     priv->map_x = lon2pixel(priv->map_zoom, priv->center_rlon) - width_center;
1597     priv->map_y = lat2pixel(priv->map_zoom, priv->center_rlat) - height_center;
1598     }
1599     else
1600     {
1601     factor = exp(priv->map_zoom * M_LN2)/exp(zoom_old * M_LN2);
1602     priv->map_x = ((priv->map_x + width_center) * factor) - width_center;
1603     priv->map_y = ((priv->map_y + height_center) * factor) - height_center;
1604     }
1605    
1606     g_debug("Zoom changed from %d to %d factor:%f x:%d",
1607     zoom_old, priv->map_zoom, factor, priv->map_x);
1608    
1609     osm_gps_map_map_redraw_idle(map);
1610     }
1611     return priv->map_zoom;
1612     }
1613    
1614     void
1615     osm_gps_map_add_track (OsmGpsMap *map, GSList *track)
1616     {
1617     OsmGpsMapPrivate *priv;
1618    
1619     g_return_if_fail (OSM_IS_GPS_MAP (map));
1620     priv = map->priv;
1621    
1622     if (track) {
1623     priv->tracks = g_slist_append(priv->tracks, track);
1624     osm_gps_map_map_redraw_idle(map);
1625     }
1626     }
1627    
1628     void
1629     osm_gps_map_clear_tracks (OsmGpsMap *map)
1630     {
1631     g_return_if_fail (OSM_IS_GPS_MAP (map));
1632    
1633     osm_gps_map_free_tracks(map);
1634     osm_gps_map_map_redraw_idle(map);
1635     }
1636    
1637     void
1638     osm_gps_map_add_image (OsmGpsMap *map, float latitude, float longitude, GdkPixbuf *image)
1639     {
1640     g_return_if_fail (OSM_IS_GPS_MAP (map));
1641    
1642     if (image) {
1643     OsmGpsMapPrivate *priv = map->priv;
1644     image_t *im;
1645    
1646     //cache w/h for speed, and add image to list
1647     im = g_new0(image_t,1);
1648     im->w = gdk_pixbuf_get_width(image);
1649     im->h = gdk_pixbuf_get_height(image);
1650     im->pt.rlat = deg2rad(latitude);
1651     im->pt.rlon = deg2rad(longitude);
1652    
1653     g_object_ref(image);
1654     im->image = image;
1655    
1656     priv->images = g_slist_append(priv->images, im);
1657    
1658     osm_gps_map_map_redraw_idle(map);
1659     }
1660     }
1661    
1662     gboolean
1663     osm_gps_map_remove_image (OsmGpsMap *map, GdkPixbuf *image)
1664     {
1665     OsmGpsMapPrivate *priv = map->priv;
1666     if (priv->images) {
1667     GSList *list;
1668     for(list = priv->images; list != NULL; list = list->next)
1669     {
1670     image_t *im = list->data;
1671     if (im->image == image)
1672     {
1673     priv->images = g_slist_remove_link(priv->images, list);
1674     g_object_unref(im->image);
1675     g_free(im);
1676     osm_gps_map_map_redraw_idle(map);
1677     return TRUE;
1678     }
1679     }
1680     }
1681     return FALSE;
1682     }
1683    
1684     void
1685     osm_gps_map_clear_images (OsmGpsMap *map)
1686     {
1687     g_return_if_fail (OSM_IS_GPS_MAP (map));
1688    
1689     osm_gps_map_free_images(map);
1690     osm_gps_map_map_redraw_idle(map);
1691     }
1692    
1693     void
1694     osm_gps_map_osd_speed (OsmGpsMap *map, float speed)
1695     {
1696     OsmGpsMapPrivate *priv;
1697    
1698     PangoContext *context = NULL;
1699     PangoLayout *layout = NULL;
1700     PangoFontDescription *desc = NULL;
1701    
1702     GdkColor color;
1703     GdkGC *gc;
1704    
1705     gchar *buffer;
1706     //static int x = 10, y = 10;
1707     static int width = 0, height = 0;
1708    
1709     g_return_if_fail (OSM_IS_GPS_MAP (map));
1710     priv = map->priv;
1711    
1712     buffer = g_strdup_printf("%.0f", speed);
1713    
1714     /* pango initialisation */
1715     context = gtk_widget_get_pango_context (GTK_WIDGET(map));
1716     layout = pango_layout_new (context);
1717     desc = pango_font_description_new();
1718    
1719     pango_font_description_set_size (desc, 40 * PANGO_SCALE);
1720     pango_layout_set_font_description (layout, desc);
1721     pango_layout_set_text (layout, buffer, strlen(buffer));
1722    
1723     gc = gdk_gc_new (GTK_WIDGET(map)->window);
1724    
1725     color.red = (0 > 50) ? 0xffff : 0;
1726     color.green = 0;
1727     color.blue = 0;
1728    
1729     gdk_gc_set_rgb_fg_color (gc, &color);
1730    
1731     /* faster / less flicker alternative:*/
1732     gdk_draw_drawable (
1733     GTK_WIDGET(map)->window,
1734     GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(map)],
1735     priv->pixmap,
1736     0,0,
1737     0,0,
1738     width+10,width+10);
1739    
1740     gdk_draw_layout(GTK_WIDGET(map)->window,
1741     gc,
1742     0, 0,
1743     layout);
1744    
1745     /* set width and height */
1746     pango_layout_get_pixel_size(layout, &width, &height);
1747    
1748     g_free(buffer);
1749     pango_font_description_free (desc);
1750     g_object_unref (layout);
1751     g_object_unref (gc);
1752     }
1753    
1754     void
1755     osm_gps_map_draw_gps (OsmGpsMap *map, float latitude, float longitude, float heading)
1756     {
1757     int pixel_x, pixel_y;
1758     OsmGpsMapPrivate *priv;
1759    
1760     g_return_if_fail (OSM_IS_GPS_MAP (map));
1761     priv = map->priv;
1762    
1763     priv->gps->rlat = deg2rad(latitude);
1764     priv->gps->rlon = deg2rad(longitude);
1765     priv->gps_valid = TRUE;
1766    
1767     // pixel_x,y, offsets
1768     pixel_x = lon2pixel(priv->map_zoom, priv->gps->rlon);
1769     pixel_y = lat2pixel(priv->map_zoom, priv->gps->rlat);
1770    
1771     //If trip marker add to list of gps points.
1772     if (priv->record_trip_history) {
1773     coord_t *tp = g_new0(coord_t,1);
1774     tp->rlat = priv->gps->rlat;
1775     tp->rlon = priv->gps->rlon;
1776     priv->trip_history = g_slist_append(priv->trip_history, tp);
1777     }
1778    
1779     // dont draw anything if we are dragging
1780     if (priv->dragging) {
1781     g_debug("Dragging");
1782     return;
1783     }
1784    
1785     //Automatically center the map if the track approaches the edge
1786     if(priv->map_auto_center) {
1787     int x = pixel_x - priv->map_x;
1788     int y = pixel_y - priv->map_y;
1789     int width = GTK_WIDGET(map)->allocation.width;
1790     int height = GTK_WIDGET(map)->allocation.height;
1791     if( x < (width/2 - width/8) || x > (width/2 + width/8) ||
1792     y < (height/2 - height/8) || y > (height/2 + height/8)) {
1793    
1794     priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
1795     priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
1796     priv->center_coord_set = FALSE;
1797     }
1798     }
1799    
1800     // this redraws the map (including the gps track, and adjusts the
1801     // map center if it was changed
1802     osm_gps_map_map_redraw_idle(map);
1803     }
1804    
1805     void
1806     osm_gps_map_clear_gps (OsmGpsMap *map)
1807     {
1808     osm_gps_map_free_trip(map);
1809     osm_gps_map_map_redraw_idle(map);
1810     }
1811    
1812     coord_t
1813     osm_gps_map_get_co_ordinates (OsmGpsMap *map, int pixel_x, int pixel_y)
1814     {
1815     coord_t coord;
1816     OsmGpsMapPrivate *priv = map->priv;
1817    
1818     coord.rlat = pixel2lat(priv->map_zoom, priv->map_y + pixel_y);
1819     coord.rlon = pixel2lon(priv->map_zoom, priv->map_x + pixel_x);
1820     return coord;
1821     }
1822    
1823     GtkWidget *
1824     osm_gps_map_new (void)
1825     {
1826     return g_object_new (OSM_TYPE_GPS_MAP, NULL);
1827     }
1828    
1829     void
1830     osm_gps_map_screen_to_geographic (OsmGpsMap *map, gint pixel_x, gint pixel_y,
1831     gfloat *latitude, gfloat *longitude)
1832     {
1833     OsmGpsMapPrivate *priv;
1834    
1835     g_return_if_fail (OSM_IS_GPS_MAP (map));
1836     priv = map->priv;
1837    
1838     if (latitude)
1839     *latitude = rad2deg(pixel2lat(priv->map_zoom, priv->map_y + pixel_y));
1840     if (longitude)
1841     *longitude = rad2deg(pixel2lon(priv->map_zoom, priv->map_x + pixel_x));
1842     }
1843    
1844     void
1845     osm_gps_map_geographic_to_screen (OsmGpsMap *map,
1846     gfloat latitude, gfloat longitude,
1847     gint *pixel_x, gint *pixel_y)
1848     {
1849     OsmGpsMapPrivate *priv;
1850    
1851     g_return_if_fail (OSM_IS_GPS_MAP (map));
1852     priv = map->priv;
1853    
1854     if (pixel_x)
1855     *pixel_x = lon2pixel(priv->map_zoom, deg2rad(longitude)) - priv->map_x;
1856     if (pixel_y)
1857     *pixel_y = lat2pixel(priv->map_zoom, deg2rad(latitude)) - priv->map_y;
1858     }
1859    
1860     void
1861     osm_gps_map_scroll (OsmGpsMap *map, gint dx, gint dy)
1862     {
1863     OsmGpsMapPrivate *priv;
1864    
1865     g_return_if_fail (OSM_IS_GPS_MAP (map));
1866     priv = map->priv;
1867    
1868     priv->center_coord_set = FALSE;
1869     priv->map_x += dx;
1870     priv->map_y += dy;
1871    
1872     osm_gps_map_map_redraw_idle (map);
1873     }
1874    
1875     float
1876     osm_gps_map_get_scale(OsmGpsMap *map)
1877     {
1878     OsmGpsMapPrivate *priv;
1879    
1880     g_return_val_if_fail (OSM_IS_GPS_MAP (map), OSM_NAN);
1881     priv = map->priv;
1882    
1883     return osm_gps_map_get_scale_at_point(priv->map_zoom, priv->center_rlat, priv->center_rlon);
1884     }
1885    
1886     #ifdef ENABLE_BALLOON
1887     void
1888     osm_gps_map_draw_balloon (OsmGpsMap *map, float latitude, float longitude,
1889     OsmGpsMapBalloonCallback cb, gpointer data)
1890     {
1891     OsmGpsMapPrivate *priv;
1892    
1893     /* remove and previously installed balloon */
1894     osm_gps_map_clear_balloon (map);
1895    
1896     g_return_if_fail (OSM_IS_GPS_MAP (map));
1897     priv = map->priv;
1898    
1899     priv->balloon.coo->rlat = deg2rad(latitude);
1900     priv->balloon.coo->rlon = deg2rad(longitude);
1901     priv->balloon.valid = TRUE;
1902    
1903     priv->balloon.cb = cb;
1904     priv->balloon.data = data;
1905    
1906     // this redraws the map
1907     osm_gps_map_map_redraw_idle(map);
1908     }
1909    
1910     void
1911     osm_gps_map_clear_balloon (OsmGpsMap *map)
1912     {
1913     OsmGpsMapPrivate *priv;
1914    
1915     g_return_if_fail (OSM_IS_GPS_MAP (map));
1916     priv = map->priv;
1917    
1918     priv->balloon.valid = FALSE;
1919    
1920     osm_gps_map_map_redraw_idle(map);
1921     }
1922     #endif
1923    
1924     #ifdef ENABLE_OSD
1925     void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdGpsCallback cb, gpointer data) {
1926     OsmGpsMapPrivate *priv;
1927    
1928     g_return_if_fail (OSM_IS_GPS_MAP (map));
1929     priv = map->priv;
1930    
1931     priv->osd.cb = cb;
1932     priv->osd.data = data;
1933    
1934     /* this may have changed the state of the gps button */
1935     /* we thus re-render the overlay */
1936     osm_gps_map_osd_render(priv);
1937    
1938     osm_gps_map_map_redraw_idle(map);
1939     }
1940     #endif