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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 73 - (hide annotations)
Sun Aug 23 19:38:15 2009 UTC (14 years, 8 months ago) by harbaum
File MIME type: text/plain
File size: 100353 byte(s)
OSD seperated from osm-gps-map
1 harbaum 32 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
2     /* vim:set et sw=4 ts=4 cino=t0,(0: */
3     /*
4     * osm-gps-map.c
5     * Copyright (C) Marcus Bauer 2008 <marcus.bauer@gmail.com>
6     * Copyright (C) John Stowers 2009 <john.stowers@gmail.com>
7     *
8     * Contributions by
9     * Everaldo Canuto 2009 <everaldo.canuto@gmail.com>
10     *
11     * osm-gps-map.c is free software: you can redistribute it and/or modify it
12     * under the terms of the GNU General Public License as published by the
13     * Free Software Foundation, either version 3 of the License, or
14     * (at your option) any later version.
15     *
16     * osm-gps-map.c is distributed in the hope that it will be useful, but
17     * WITHOUT ANY WARRANTY; without even the implied warranty of
18     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19     * See the GNU General Public License for more details.
20     *
21     * You should have received a copy of the GNU General Public License along
22     * with this program. If not, see <http://www.gnu.org/licenses/>.
23     */
24    
25     #include "config.h"
26    
27     #include <fcntl.h>
28     #include <math.h>
29     #include <unistd.h>
30     #include <stdio.h>
31     #include <stdlib.h>
32     #include <string.h>
33    
34     #include <gdk/gdk.h>
35     #include <glib.h>
36 harbaum 55 #include <glib/gstdio.h>
37 harbaum 32 #include <glib/gprintf.h>
38     #include <libsoup/soup.h>
39    
40     #include "converter.h"
41     #include "osm-gps-map-types.h"
42     #include "osm-gps-map.h"
43    
44     #ifdef USE_CAIRO
45     #include <cairo.h>
46     #endif
47    
48     #define ENABLE_DEBUG 0
49    
50     #define EXTRA_BORDER (TILESIZE / 2)
51    
52     struct _OsmGpsMapPrivate
53     {
54     GHashTable *tile_queue;
55     GHashTable *missing_tiles;
56     GHashTable *tile_cache;
57    
58     int map_zoom;
59     int max_zoom;
60     int min_zoom;
61     gboolean map_auto_center;
62     gboolean map_auto_download;
63     int map_x;
64     int map_y;
65    
66     /* Latitude and longitude of the center of the map, in radians */
67     gfloat center_rlat;
68     gfloat center_rlon;
69    
70     guint max_tile_cache_size;
71     /* Incremented at each redraw */
72     guint redraw_cycle;
73     /* ID of the idle redraw operation */
74     guint idle_map_redraw;
75    
76     //how we download tiles
77     SoupSession *soup_session;
78     char *proxy_uri;
79    
80     //where downloaded tiles are cached
81     char *cache_dir;
82     gboolean cache_dir_is_full_path;
83    
84     //contains flags indicating the various special characters
85     //the uri string contains, that will be replaced when calculating
86     //the uri to download.
87 harbaum 55 OsmGpsMapSource_t map_source;
88 harbaum 32 char *repo_uri;
89 harbaum 55 char *image_format;
90 harbaum 32 int uri_format;
91     //flag indicating if the map source is located on the google
92     gboolean the_google;
93    
94     //gps tracking state
95     gboolean record_trip_history;
96     gboolean show_trip_history;
97     GSList *trip_history;
98     coord_t *gps;
99     gboolean gps_valid;
100    
101 harbaum 65 #ifdef ENABLE_BALLOON
102 harbaum 57 //a balloon with additional info
103 harbaum 58 struct {
104     coord_t *coo;
105     gboolean valid;
106     OsmGpsMapRect_t rect;
107     OsmGpsMapBalloonCallback cb;
108     gpointer data;
109     } balloon;
110 harbaum 65 #endif
111 harbaum 57
112 harbaum 61 #ifdef ENABLE_OSD
113 harbaum 73 //the osd controls (if present)
114     osm_gps_map_osd_t *osd;
115 harbaum 61 #endif
116 harbaum 73
117 harbaum 32 //additional images or tracks added to the map
118     GSList *tracks;
119     GSList *images;
120    
121     //Used for storing the joined tiles
122     GdkPixmap *pixmap;
123     GdkGC *gc_map;
124    
125     //The tile painted when one cannot be found
126 harbaum 55 GdkPixbuf *null_tile;
127 harbaum 32
128     //For tracking click and drag
129     int drag_counter;
130     int drag_mouse_dx;
131     int drag_mouse_dy;
132     int drag_start_mouse_x;
133     int drag_start_mouse_y;
134     int drag_start_map_x;
135     int drag_start_map_y;
136    
137     //for customizing the redering of the gps track
138     int ui_gps_track_width;
139     int ui_gps_point_inner_radius;
140     int ui_gps_point_outer_radius;
141    
142     guint is_disposed : 1;
143     guint dragging : 1;
144     guint center_coord_set : 1;
145     };
146    
147     #define OSM_GPS_MAP_PRIVATE(o) (OSM_GPS_MAP (o)->priv)
148    
149     typedef struct
150     {
151     GdkPixbuf *pixbuf;
152     /* We keep track of the number of the redraw cycle this tile was last used,
153     * so that osm_gps_map_purge_cache() can remove the older ones */
154     guint redraw_cycle;
155     } OsmCachedTile;
156    
157     enum
158     {
159     PROP_0,
160    
161     PROP_AUTO_CENTER,
162     PROP_RECORD_TRIP_HISTORY,
163     PROP_SHOW_TRIP_HISTORY,
164     PROP_AUTO_DOWNLOAD,
165     PROP_REPO_URI,
166     PROP_PROXY_URI,
167     PROP_TILE_CACHE_DIR,
168     PROP_TILE_CACHE_DIR_IS_FULL_PATH,
169     PROP_ZOOM,
170     PROP_MAX_ZOOM,
171     PROP_MIN_ZOOM,
172     PROP_LATITUDE,
173     PROP_LONGITUDE,
174     PROP_MAP_X,
175     PROP_MAP_Y,
176     PROP_TILES_QUEUED,
177     PROP_GPS_TRACK_WIDTH,
178     PROP_GPS_POINT_R1,
179 harbaum 55 PROP_GPS_POINT_R2,
180     PROP_MAP_SOURCE,
181     PROP_IMAGE_FORMAT
182 harbaum 32 };
183    
184     G_DEFINE_TYPE (OsmGpsMap, osm_gps_map, GTK_TYPE_DRAWING_AREA);
185    
186     /*
187     * Drawing function forward defintions
188     */
189     static gchar *replace_string(const gchar *src, const gchar *from, const gchar *to);
190     static void inspect_map_uri(OsmGpsMap *map);
191     static gchar *replace_map_uri(OsmGpsMap *map, const gchar *uri, int zoom, int x, int y);
192     static void osm_gps_map_print_images (OsmGpsMap *map);
193     static void osm_gps_map_draw_gps_point (OsmGpsMap *map);
194     static void osm_gps_map_blit_tile(OsmGpsMap *map, GdkPixbuf *pixbuf, int offset_x, int offset_y);
195 harbaum 55 #ifdef LIBSOUP22
196     static void osm_gps_map_tile_download_complete (SoupMessage *msg, gpointer user_data);
197     #else
198     static void osm_gps_map_tile_download_complete (SoupSession *session, SoupMessage *msg, gpointer user_data);
199     #endif
200 harbaum 32 static void osm_gps_map_download_tile (OsmGpsMap *map, int zoom, int x, int y, gboolean redraw);
201     static void osm_gps_map_load_tile (OsmGpsMap *map, int zoom, int x, int y, int offset_x, int offset_y);
202     static void osm_gps_map_fill_tiles_pixel (OsmGpsMap *map);
203     static gboolean osm_gps_map_map_redraw (OsmGpsMap *map);
204     static void osm_gps_map_map_redraw_idle (OsmGpsMap *map);
205    
206     static void
207     cached_tile_free (OsmCachedTile *tile)
208     {
209     g_object_unref (tile->pixbuf);
210     g_slice_free (OsmCachedTile, tile);
211     }
212    
213     /*
214     * Description:
215     * Find and replace text within a string.
216     *
217     * Parameters:
218     * src (in) - pointer to source string
219     * from (in) - pointer to search text
220     * to (in) - pointer to replacement text
221     *
222     * Returns:
223     * Returns a pointer to dynamically-allocated memory containing string
224     * with occurences of the text pointed to by 'from' replaced by with the
225     * text pointed to by 'to'.
226     */
227     static gchar *
228     replace_string(const gchar *src, const gchar *from, const gchar *to)
229     {
230     size_t size = strlen(src) + 1;
231     size_t fromlen = strlen(from);
232     size_t tolen = strlen(to);
233    
234     /* Allocate the first chunk with enough for the original string. */
235     gchar *value = g_malloc(size);
236    
237    
238     /* We need to return 'value', so let's make a copy to mess around with. */
239     gchar *dst = value;
240    
241     if ( value != NULL )
242     {
243     for ( ;; )
244     {
245     /* Try to find the search text. */
246     const gchar *match = g_strstr_len(src, size, from);
247     if ( match != NULL )
248     {
249     gchar *temp;
250     /* Find out how many characters to copy up to the 'match'. */
251     size_t count = match - src;
252    
253    
254     /* Calculate the total size the string will be after the
255     * replacement is performed. */
256     size += tolen - fromlen;
257    
258     temp = g_realloc(value, size);
259     if ( temp == NULL )
260     {
261     g_free(value);
262     return NULL;
263     }
264    
265     /* we'll want to return 'value' eventually, so let's point it
266     * to the memory that we are now working with.
267     * And let's not forget to point to the right location in
268     * the destination as well. */
269     dst = temp + (dst - value);
270     value = temp;
271    
272     /*
273     * Copy from the source to the point where we matched. Then
274     * move the source pointer ahead by the amount we copied. And
275     * move the destination pointer ahead by the same amount.
276     */
277     g_memmove(dst, src, count);
278     src += count;
279     dst += count;
280    
281     /* Now copy in the replacement text 'to' at the position of
282     * the match. Adjust the source pointer by the text we replaced.
283     * Adjust the destination pointer by the amount of replacement
284     * text. */
285     g_memmove(dst, to, tolen);
286     src += fromlen;
287     dst += tolen;
288     }
289     else
290     {
291     /*
292     * Copy any remaining part of the string. This includes the null
293     * termination character.
294     */
295     strcpy(dst, src);
296     break;
297     }
298     }
299     }
300     return value;
301     }
302    
303     static void
304     map_convert_coords_to_quadtree_string(OsmGpsMap *map, gint x, gint y, gint zoomlevel,
305     gchar *buffer, const gchar initial,
306     const gchar *const quadrant)
307     {
308     gchar *ptr = buffer;
309     gint n;
310    
311     if (initial)
312     *ptr++ = initial;
313    
314     for(n = zoomlevel-1; n >= 0; n--)
315     {
316     gint xbit = (x >> n) & 1;
317     gint ybit = (y >> n) & 1;
318     *ptr++ = quadrant[xbit + 2 * ybit];
319     }
320    
321     *ptr++ = '\0';
322     }
323    
324    
325     static void
326     inspect_map_uri(OsmGpsMap *map)
327     {
328     OsmGpsMapPrivate *priv = map->priv;
329    
330     if (g_strrstr(priv->repo_uri, URI_MARKER_X))
331     priv->uri_format |= URI_HAS_X;
332    
333     if (g_strrstr(priv->repo_uri, URI_MARKER_Y))
334     priv->uri_format |= URI_HAS_Y;
335    
336     if (g_strrstr(priv->repo_uri, URI_MARKER_Z))
337     priv->uri_format |= URI_HAS_Z;
338    
339     if (g_strrstr(priv->repo_uri, URI_MARKER_S))
340     priv->uri_format |= URI_HAS_S;
341    
342     if (g_strrstr(priv->repo_uri, URI_MARKER_Q))
343     priv->uri_format |= URI_HAS_Q;
344    
345     if (g_strrstr(priv->repo_uri, URI_MARKER_Q0))
346     priv->uri_format |= URI_HAS_Q0;
347    
348     if (g_strrstr(priv->repo_uri, URI_MARKER_YS))
349     priv->uri_format |= URI_HAS_YS;
350    
351     if (g_strrstr(priv->repo_uri, URI_MARKER_R))
352     priv->uri_format |= URI_HAS_R;
353    
354     if (g_strrstr(priv->repo_uri, "google.com"))
355     priv->the_google = TRUE;
356    
357     g_debug("URI Format: 0x%X (google: %X)", priv->uri_format, priv->the_google);
358    
359     }
360    
361     static gchar *
362     replace_map_uri(OsmGpsMap *map, const gchar *uri, int zoom, int x, int y)
363     {
364     OsmGpsMapPrivate *priv = map->priv;
365     char *url;
366     unsigned int i;
367     char location[22];
368    
369     i = 1;
370     url = g_strdup(uri);
371     while (i < URI_FLAG_END)
372     {
373     char *s = NULL;
374     char *old;
375    
376     old = url;
377     switch(i & priv->uri_format)
378     {
379     case URI_HAS_X:
380     s = g_strdup_printf("%d", x);
381     url = replace_string(url, URI_MARKER_X, s);
382     //g_debug("FOUND " URI_MARKER_X);
383     break;
384     case URI_HAS_Y:
385     s = g_strdup_printf("%d", y);
386     url = replace_string(url, URI_MARKER_Y, s);
387     //g_debug("FOUND " URI_MARKER_Y);
388     break;
389     case URI_HAS_Z:
390     s = g_strdup_printf("%d", zoom);
391     url = replace_string(url, URI_MARKER_Z, s);
392     //g_debug("FOUND " URI_MARKER_Z);
393     break;
394     case URI_HAS_S:
395     s = g_strdup_printf("%d", priv->max_zoom-zoom);
396     url = replace_string(url, URI_MARKER_S, s);
397     //g_debug("FOUND " URI_MARKER_S);
398     break;
399     case URI_HAS_Q:
400     map_convert_coords_to_quadtree_string(map,x,y,zoom,location,'t',"qrts");
401     s = g_strdup_printf("%s", location);
402     url = replace_string(url, URI_MARKER_Q, s);
403     //g_debug("FOUND " URI_MARKER_Q);
404     break;
405     case URI_HAS_Q0:
406     map_convert_coords_to_quadtree_string(map,x,y,zoom,location,'\0', "0123");
407     s = g_strdup_printf("%s", location);
408     url = replace_string(url, URI_MARKER_Q0, s);
409     //g_debug("FOUND " URI_MARKER_Q0);
410     break;
411     case URI_HAS_YS:
412     // s = g_strdup_printf("%d", y);
413     // url = replace_string(url, URI_MARKER_YS, s);
414     g_warning("FOUND " URI_MARKER_YS " NOT IMPLEMENTED");
415     // retval = g_strdup_printf(repo->url,
416     // tilex,
417     // (1 << (MAX_ZOOM - zoom)) - tiley - 1,
418     // zoom - (MAX_ZOOM - 17));
419     break;
420     case URI_HAS_R:
421     s = g_strdup_printf("%d", g_random_int_range(0,4));
422     url = replace_string(url, URI_MARKER_R, s);
423     //g_debug("FOUND " URI_MARKER_R);
424     break;
425     default:
426     s = NULL;
427     break;
428     }
429    
430     if (s) {
431     g_free(s);
432     g_free(old);
433     }
434    
435     i = (i << 1);
436    
437     }
438    
439     return url;
440     }
441    
442     static void
443     my_log_handler (const gchar * log_domain, GLogLevelFlags log_level, const gchar * message, gpointer user_data)
444     {
445     if (!(log_level & G_LOG_LEVEL_DEBUG) || ENABLE_DEBUG)
446     g_log_default_handler (log_domain, log_level, message, user_data);
447     }
448    
449 harbaum 55 static float
450     osm_gps_map_get_scale_at_point(int zoom, float rlat, float rlon)
451     {
452     /* world at zoom 1 == 512 pixels */
453     return cos(rlat) * M_PI * OSM_EQ_RADIUS / (1<<(7+zoom));
454     }
455    
456 harbaum 32 /* clears the trip list and all resources */
457     static void
458     osm_gps_map_free_trip (OsmGpsMap *map)
459     {
460     OsmGpsMapPrivate *priv = map->priv;
461     if (priv->trip_history) {
462     g_slist_foreach(priv->trip_history, (GFunc) g_free, NULL);
463     g_slist_free(priv->trip_history);
464     priv->trip_history = NULL;
465     }
466     }
467    
468     /* clears the tracks and all resources */
469     static void
470     osm_gps_map_free_tracks (OsmGpsMap *map)
471     {
472     OsmGpsMapPrivate *priv = map->priv;
473     if (priv->tracks)
474     {
475     GSList* tmp = priv->tracks;
476     while (tmp != NULL)
477     {
478     g_slist_foreach(tmp->data, (GFunc) g_free, NULL);
479     g_slist_free(tmp->data);
480     tmp = g_slist_next(tmp);
481     }
482     g_slist_free(priv->tracks);
483     priv->tracks = NULL;
484     }
485     }
486    
487     /* free the poi image lists */
488     static void
489     osm_gps_map_free_images (OsmGpsMap *map)
490     {
491     OsmGpsMapPrivate *priv = map->priv;
492     if (priv->images) {
493     GSList *list;
494     for(list = priv->images; list != NULL; list = list->next)
495     {
496     image_t *im = list->data;
497     g_object_unref(im->image);
498     g_free(im);
499     }
500     g_slist_free(priv->images);
501     priv->images = NULL;
502     }
503     }
504    
505     static void
506     osm_gps_map_print_images (OsmGpsMap *map)
507     {
508     GSList *list;
509     int x,y,pixel_x,pixel_y;
510     int min_x = 0,min_y = 0,max_x = 0,max_y = 0;
511     int map_x0, map_y0;
512     OsmGpsMapPrivate *priv = map->priv;
513    
514     map_x0 = priv->map_x - EXTRA_BORDER;
515     map_y0 = priv->map_y - EXTRA_BORDER;
516     for(list = priv->images; list != NULL; list = list->next)
517     {
518     image_t *im = list->data;
519    
520     // pixel_x,y, offsets
521     pixel_x = lon2pixel(priv->map_zoom, im->pt.rlon);
522     pixel_y = lat2pixel(priv->map_zoom, im->pt.rlat);
523    
524     g_debug("Image %dx%d @: %f,%f (%d,%d)",
525     im->w, im->h,
526     im->pt.rlat, im->pt.rlon,
527     pixel_x, pixel_y);
528    
529     x = pixel_x - map_x0;
530     y = pixel_y - map_y0;
531    
532     gdk_draw_pixbuf (
533     priv->pixmap,
534     priv->gc_map,
535     im->image,
536     0,0,
537     x-(im->w/2),y-(im->h/2),
538     im->w,im->h,
539     GDK_RGB_DITHER_NONE, 0, 0);
540    
541     max_x = MAX(x+im->w,max_x);
542     min_x = MIN(x-im->w,min_x);
543     max_y = MAX(y+im->h,max_y);
544     min_y = MIN(y-im->h,min_y);
545     }
546    
547     gtk_widget_queue_draw_area (
548     GTK_WIDGET(map),
549     min_x + EXTRA_BORDER, min_y + EXTRA_BORDER,
550     max_x + EXTRA_BORDER, max_y + EXTRA_BORDER);
551    
552     }
553    
554     static void
555     osm_gps_map_draw_gps_point (OsmGpsMap *map)
556     {
557     OsmGpsMapPrivate *priv = map->priv;
558    
559     //incase we get called before we have got a gps point
560     if (priv->gps_valid) {
561     int map_x0, map_y0;
562     int x, y;
563     int r = priv->ui_gps_point_inner_radius;
564     int r2 = priv->ui_gps_point_outer_radius;
565 harbaum 55 // int lw = priv->ui_gps_track_width;
566 harbaum 32 int mr = MAX(r,r2);
567    
568     map_x0 = priv->map_x - EXTRA_BORDER;
569     map_y0 = priv->map_y - EXTRA_BORDER;
570     x = lon2pixel(priv->map_zoom, priv->gps->rlon) - map_x0;
571     y = lat2pixel(priv->map_zoom, priv->gps->rlat) - map_y0;
572     #ifdef USE_CAIRO
573     cairo_t *cr;
574     cairo_pattern_t *pat;
575     #else
576     GdkColor color;
577     GdkGC *marker;
578     #endif
579    
580     #ifdef USE_CAIRO
581     cr = gdk_cairo_create(priv->pixmap);
582    
583     // draw transparent area
584     if (r2 > 0) {
585     cairo_set_line_width (cr, 1.5);
586     cairo_set_source_rgba (cr, 0.75, 0.75, 0.75, 0.4);
587     cairo_arc (cr, x, y, r2, 0, 2 * M_PI);
588     cairo_fill (cr);
589     // draw transparent area border
590     cairo_set_source_rgba (cr, 0.55, 0.55, 0.55, 0.4);
591     cairo_arc (cr, x, y, r2, 0, 2 * M_PI);
592     cairo_stroke(cr);
593     }
594    
595     // draw ball gradient
596     if (r > 0) {
597     pat = cairo_pattern_create_radial (x-(r/5), y-(r/5), (r/5), x, y, r);
598     cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, 1.0);
599     cairo_pattern_add_color_stop_rgba (pat, 1, 0, 0, 1, 1.0);
600     cairo_set_source (cr, pat);
601     cairo_arc (cr, x, y, r, 0, 2 * M_PI);
602     cairo_fill (cr);
603     cairo_pattern_destroy (pat);
604     // draw ball border
605     cairo_set_line_width (cr, 1.0);
606     cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
607     cairo_arc (cr, x, y, r, 0, 2 * M_PI);
608     cairo_stroke(cr);
609     }
610    
611     cairo_destroy(cr);
612     gtk_widget_queue_draw_area (GTK_WIDGET(map),
613     x-mr,
614     y-mr,
615     mr*2,
616     mr*2);
617     #else
618     marker = gdk_gc_new(priv->pixmap);
619     color.red = 5000;
620     color.green = 5000;
621     color.blue = 55000;
622     gdk_gc_set_rgb_fg_color(marker, &color);
623     gdk_gc_set_line_attributes(marker, lw, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
624    
625     if (r2 > 0) {
626     gdk_draw_arc (priv->pixmap,
627     marker,
628     FALSE, //filled
629     x-r2, y-r2, // x,y
630     r2*2,r2*2, // width, height
631     0, 360*64); // start-end angle 64th, from 3h, anti clockwise
632     }
633     if (r > 0) {
634     gdk_draw_arc (priv->pixmap,
635     marker,
636     TRUE, //filled
637     x-r, y-r, // x,y
638     r*2,r*2, // width, height
639     0, 360*64); // start-end angle 64th, from 3h, anti clockwise
640     }
641    
642     g_object_unref(marker);
643     gtk_widget_queue_draw_area (GTK_WIDGET(map),
644     x-(mr+lw),
645     y-(mr+lw),
646     (mr*2)+lw+lw,
647     (mr*2)+lw+lw);
648     #endif
649     }
650 harbaum 57 }
651    
652 harbaum 65 #ifdef ENABLE_BALLOON
653 harbaum 57 /* most visual effects are hardcoded by now, but may be made */
654     /* available via properties later */
655 harbaum 65 #ifndef BALLOON_AREA_WIDTH
656 harbaum 60 #define BALLOON_AREA_WIDTH 290
657 harbaum 65 #endif
658     #ifndef BALLOON_AREA_HEIGHT
659 harbaum 58 #define BALLOON_AREA_HEIGHT 75
660 harbaum 65 #endif
661     #ifndef BALLOON_CORNER_RADIUS
662 harbaum 66 #define BALLOON_CORNER_RADIUS 10
663 harbaum 65 #endif
664 harbaum 58
665 harbaum 66 #define BALLOON_BORDER (BALLOON_CORNER_RADIUS/2)
666 harbaum 58 #define BALLOON_WIDTH (BALLOON_AREA_WIDTH + 2 * BALLOON_BORDER)
667     #define BALLOON_HEIGHT (BALLOON_AREA_HEIGHT + 2 * BALLOON_BORDER)
668 harbaum 57 #define BALLOON_TRANSPARENCY 0.8
669     #define POINTER_HEIGHT 20
670     #define POINTER_FOOT_WIDTH 20
671     #define POINTER_OFFSET (BALLOON_CORNER_RADIUS*3/4)
672 harbaum 66 #define BALLOON_SHADOW (BALLOON_CORNER_RADIUS/2)
673 harbaum 57 #define BALLOON_SHADOW_TRANSPARENCY 0.2
674    
675 harbaum 66 #define CLOSE_BUTTON_RADIUS (BALLOON_CORNER_RADIUS)
676 harbaum 58
677    
678 harbaum 57 /* draw the bubble shape. this is used twice, once for the shape and once */
679     /* for the shadow */
680     static void
681     osm_gps_map_draw_balloon_shape (cairo_t *cr, int x0, int y0, int x1, int y1,
682     gboolean bottom, int px, int py, int px0, int px1) {
683    
684     cairo_move_to (cr, x0, y0 + BALLOON_CORNER_RADIUS);
685 harbaum 66 cairo_arc (cr, x0 + BALLOON_CORNER_RADIUS, y0 + BALLOON_CORNER_RADIUS,
686     BALLOON_CORNER_RADIUS, -M_PI, -M_PI/2);
687 harbaum 57 if(!bottom) {
688 harbaum 58 /* insert top pointer */
689 harbaum 57 cairo_line_to (cr, px1, y0);
690     cairo_line_to (cr, px, py);
691     cairo_line_to (cr, px0, y0);
692     }
693    
694     cairo_line_to (cr, x1 - BALLOON_CORNER_RADIUS, y0);
695 harbaum 66 cairo_arc (cr, x1 - BALLOON_CORNER_RADIUS, y0 + BALLOON_CORNER_RADIUS,
696     BALLOON_CORNER_RADIUS, -M_PI/2, 0);
697 harbaum 57 cairo_line_to (cr, x1 , y1 - BALLOON_CORNER_RADIUS);
698 harbaum 66 cairo_arc (cr, x1 - BALLOON_CORNER_RADIUS, y1 - BALLOON_CORNER_RADIUS,
699     BALLOON_CORNER_RADIUS, 0, M_PI/2);
700 harbaum 57 if(bottom) {
701 harbaum 58 /* insert bottom pointer */
702 harbaum 57 cairo_line_to (cr, px0, y1);
703     cairo_line_to (cr, px, py);
704     cairo_line_to (cr, px1, y1);
705     }
706    
707     cairo_line_to (cr, x0 + BALLOON_CORNER_RADIUS, y1);
708 harbaum 66 cairo_arc (cr, x0 + BALLOON_CORNER_RADIUS, y1 - BALLOON_CORNER_RADIUS,
709     BALLOON_CORNER_RADIUS, M_PI/2, M_PI);
710 harbaum 57
711     cairo_close_path (cr);
712 harbaum 32 }
713    
714     static void
715 harbaum 57 osm_gps_map_draw_balloon_int (OsmGpsMap *map)
716 harbaum 56 {
717     OsmGpsMapPrivate *priv = map->priv;
718    
719 harbaum 58 if (priv->balloon.valid) {
720 harbaum 57
721     /* ------- convert given coordinate into screen position --------- */
722 harbaum 58 int x0 = lon2pixel(priv->map_zoom, priv->balloon.coo->rlon) -
723 harbaum 57 priv->map_x + EXTRA_BORDER;
724 harbaum 58 int y0 = lat2pixel(priv->map_zoom, priv->balloon.coo->rlat) -
725 harbaum 57 priv->map_y + EXTRA_BORDER;
726    
727     /* check position of this relative to screen center to determine */
728     /* pointer direction ... */
729     int pointer_x = x0, pointer_x0, pointer_x1;
730     int pointer_y = y0;
731    
732     /* ... and calculate position */
733     if((x0 - EXTRA_BORDER) > GTK_WIDGET(map)->allocation.width/2) {
734     x0 -= BALLOON_WIDTH - POINTER_OFFSET;
735     pointer_x0 = pointer_x - (BALLOON_CORNER_RADIUS - POINTER_OFFSET);
736     pointer_x1 = pointer_x0 - POINTER_FOOT_WIDTH;
737     } else {
738     x0 -= POINTER_OFFSET;
739     pointer_x1 = pointer_x + (BALLOON_CORNER_RADIUS - POINTER_OFFSET);
740     pointer_x0 = pointer_x1 + POINTER_FOOT_WIDTH;
741     }
742    
743     gboolean bottom = FALSE;
744     if((y0 - EXTRA_BORDER) > GTK_WIDGET(map)->allocation.height/2) {
745     bottom = TRUE;
746     y0 -= BALLOON_HEIGHT + POINTER_HEIGHT;
747     } else
748     y0 += POINTER_HEIGHT;
749    
750     /* calculate bottom/right of box */
751     int x1 = x0 + BALLOON_WIDTH, y1 = y0 + BALLOON_HEIGHT;
752    
753 harbaum 58 /* save balloon screen coordinates for later use */
754     priv->balloon.rect.x = x0 + BALLOON_BORDER;
755     priv->balloon.rect.y = y0 + BALLOON_BORDER;
756     priv->balloon.rect.w = x1 - x0 - 2*BALLOON_BORDER;
757     priv->balloon.rect.h = y1 - y0 - 2*BALLOON_BORDER;
758    
759 harbaum 56 #ifdef USE_CAIRO
760 harbaum 57 cairo_t *cr = gdk_cairo_create(priv->pixmap);
761 harbaum 56
762 harbaum 57 /* --------- draw shadow --------------- */
763     osm_gps_map_draw_balloon_shape (cr,
764     x0 + BALLOON_SHADOW, y0 + BALLOON_SHADOW,
765     x1 + BALLOON_SHADOW, y1 + BALLOON_SHADOW,
766     bottom, pointer_x, pointer_y,
767     pointer_x0 + BALLOON_SHADOW, pointer_x1 + BALLOON_SHADOW);
768 harbaum 56
769 harbaum 57 cairo_set_source_rgba (cr, 0, 0, 0, BALLOON_SHADOW_TRANSPARENCY);
770     cairo_fill_preserve (cr);
771     cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
772     cairo_set_line_width (cr, 0);
773     cairo_stroke (cr);
774 harbaum 56
775 harbaum 57 /* --------- draw main shape ----------- */
776     osm_gps_map_draw_balloon_shape (cr, x0, y0, x1, y1,
777     bottom, pointer_x, pointer_y, pointer_x0, pointer_x1);
778    
779     cairo_set_source_rgba (cr, 1, 1, 1, BALLOON_TRANSPARENCY);
780     cairo_fill_preserve (cr);
781     cairo_set_source_rgba (cr, 0, 0, 0, BALLOON_TRANSPARENCY);
782     cairo_set_line_width (cr, 1);
783     cairo_stroke (cr);
784 harbaum 56
785    
786 harbaum 57 /* ---------- draw close button --------- */
787    
788 harbaum 58 int cx = x1 - BALLOON_BORDER - CLOSE_BUTTON_RADIUS;
789     int cy = y0 + BALLOON_BORDER + CLOSE_BUTTON_RADIUS;
790     int crad = CLOSE_BUTTON_RADIUS;
791 harbaum 57
792     cairo_arc (cr, cx, cy, crad, 0, 2 * M_PI);
793     cairo_set_source_rgba (cr, 0.8, 0, 0, 1.0);
794     cairo_fill_preserve (cr);
795     cairo_set_source_rgba (cr, 0.3, 0, 0, 1.0);
796 harbaum 58 cairo_set_line_width (cr, 2);
797 harbaum 57 cairo_stroke(cr);
798    
799     cairo_set_source_rgba (cr, 1, 1, 1, 1.0);
800 harbaum 66 cairo_set_line_width (cr, BALLOON_CORNER_RADIUS/3.3);
801 harbaum 57 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
802 harbaum 58 cairo_move_to (cr, cx - crad/2, cy - crad/2);
803     cairo_line_to (cr, cx + crad/2, cy + crad/2);
804 harbaum 57 cairo_stroke (cr);
805 harbaum 58 cairo_move_to (cr, cx + crad/2, cy - crad/2);
806     cairo_line_to (cr, cx - crad/2, cy + crad/2);
807 harbaum 57 cairo_stroke (cr);
808    
809 harbaum 58 if (priv->balloon.cb) {
810     /* clip in case application tries to draw in */
811     /* exceed of the balloon */
812     cairo_rectangle (cr, priv->balloon.rect.x, priv->balloon.rect.y,
813     priv->balloon.rect.w, priv->balloon.rect.h);
814     cairo_clip (cr);
815     cairo_new_path (cr); /* current path is not
816     consumed by cairo_clip() */
817    
818     priv->balloon.cb(cr, &priv->balloon.rect, priv->balloon.data);
819     }
820 harbaum 57
821 harbaum 61 cairo_destroy(cr);
822    
823 harbaum 57 gtk_widget_queue_draw_area (GTK_WIDGET(map),
824     x0, y0, BALLOON_WIDTH,
825     BALLOON_HEIGHT + POINTER_HEIGHT);
826     #else
827     #warning "Balloon display lacks a non-cairo implementation!"
828 harbaum 56 #endif
829 harbaum 57 }
830 harbaum 56 }
831    
832 harbaum 58 /* the user clicked into the balloons main area. handle this */
833     static void
834     osm_gps_map_handle_balloon_click(OsmGpsMap *map, gint x, gint y)
835     {
836     OsmGpsMapPrivate *priv = map->priv;
837    
838     if (!priv->balloon.valid)
839     return;
840    
841     /* check if the close button was clicked */
842     if ((x > priv->balloon.rect.w - 2*CLOSE_BUTTON_RADIUS) &&
843     (x < priv->balloon.rect.w) &&
844     (y > 0) && (y < 2*CLOSE_BUTTON_RADIUS)) {
845    
846     priv->balloon.valid = FALSE;
847     osm_gps_map_map_redraw_idle(map);
848     }
849     }
850    
851     /* return true if balloon is being displayed and if */
852     /* the given coordinate is within this balloon */
853     static gboolean
854     osm_gps_map_in_balloon(OsmGpsMapPrivate *priv, gint x, gint y)
855     {
856     return (priv->balloon.valid &&
857     (x > priv->balloon.rect.x) &&
858     (x < priv->balloon.rect.x + priv->balloon.rect.w) &&
859     (y > priv->balloon.rect.y) &&
860     (y < priv->balloon.rect.y + priv->balloon.rect.h));
861     }
862 harbaum 65 #endif // ENABLE_BALLOON
863 harbaum 58
864 harbaum 56 static void
865 harbaum 32 osm_gps_map_blit_tile(OsmGpsMap *map, GdkPixbuf *pixbuf, int offset_x, int offset_y)
866     {
867     OsmGpsMapPrivate *priv = map->priv;
868    
869 harbaum 55 g_debug("Queing redraw @ %d,%d (w:%d h:%d)", offset_x,offset_y, TILESIZE,TILESIZE);
870 harbaum 32
871     /* draw pixbuf onto pixmap */
872     gdk_draw_pixbuf (priv->pixmap,
873     priv->gc_map,
874     pixbuf,
875     0,0,
876     offset_x,offset_y,
877     TILESIZE,TILESIZE,
878     GDK_RGB_DITHER_NONE, 0, 0);
879     }
880    
881     /* libsoup-2.2 and libsoup-2.4 use different ways to store the body data */
882     #ifdef LIBSOUP22
883     #define soup_message_headers_append(a,b,c) soup_message_add_header(a,b,c)
884     #define MSG_RESPONSE_BODY(a) ((a)->response.body)
885     #define MSG_RESPONSE_LEN(a) ((a)->response.length)
886     #define MSG_RESPONSE_LEN_FORMAT "%u"
887     #else
888     #define MSG_RESPONSE_BODY(a) ((a)->response_body->data)
889     #define MSG_RESPONSE_LEN(a) ((a)->response_body->length)
890     #define MSG_RESPONSE_LEN_FORMAT "%lld"
891     #endif
892    
893     #ifdef LIBSOUP22
894     static void
895     osm_gps_map_tile_download_complete (SoupMessage *msg, gpointer user_data)
896     #else
897     static void
898     osm_gps_map_tile_download_complete (SoupSession *session, SoupMessage *msg, gpointer user_data)
899     #endif
900     {
901 harbaum 55 FILE *file;
902 harbaum 32 tile_download_t *dl = (tile_download_t *)user_data;
903     OsmGpsMap *map = OSM_GPS_MAP(dl->map);
904     OsmGpsMapPrivate *priv = map->priv;
905     gboolean file_saved = FALSE;
906    
907     if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
908     {
909     /* save tile into cachedir if one has been specified */
910     if (priv->cache_dir)
911     {
912     if (g_mkdir_with_parents(dl->folder,0700) == 0)
913     {
914 harbaum 55 file = g_fopen(dl->filename, "wb");
915     if (file != NULL)
916 harbaum 32 {
917 harbaum 55 fwrite (MSG_RESPONSE_BODY(msg), 1, MSG_RESPONSE_LEN(msg), file);
918 harbaum 32 file_saved = TRUE;
919 harbaum 55 g_debug("Wrote "MSG_RESPONSE_LEN_FORMAT" bytes to %s", MSG_RESPONSE_LEN(msg), dl->filename);
920     fclose (file);
921 harbaum 32
922     }
923     }
924     else
925     {
926 harbaum 62 g_warning("Error creating tile download directory: %s",
927     dl->folder);
928     perror("perror:");
929 harbaum 32 }
930     }
931    
932     if (dl->redraw)
933     {
934     GdkPixbuf *pixbuf = NULL;
935    
936 harbaum 55 /* if the file was actually stored on disk, we can simply */
937     /* load and decode it from that file */
938 harbaum 32 if (priv->cache_dir)
939     {
940     if (file_saved)
941     {
942     pixbuf = gdk_pixbuf_new_from_file (dl->filename, NULL);
943     }
944     }
945     else
946     {
947     GdkPixbufLoader *loader;
948     char *extension = strrchr (dl->filename, '.');
949    
950     /* parse file directly from memory */
951     if (extension)
952     {
953     loader = gdk_pixbuf_loader_new_with_type (extension+1, NULL);
954     if (!gdk_pixbuf_loader_write (loader, (unsigned char*)MSG_RESPONSE_BODY(msg), MSG_RESPONSE_LEN(msg), NULL))
955     {
956     g_warning("Error: Decoding of image failed");
957     }
958     gdk_pixbuf_loader_close(loader, NULL);
959    
960     pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
961    
962     /* give up loader but keep the pixbuf */
963     g_object_ref(pixbuf);
964     g_object_unref(loader);
965     }
966     else
967     {
968     g_warning("Error: Unable to determine image file format");
969     }
970     }
971 harbaum 55
972 harbaum 32 /* Store the tile into the cache */
973     if (G_LIKELY (pixbuf))
974     {
975     OsmCachedTile *tile = g_slice_new (OsmCachedTile);
976     tile->pixbuf = pixbuf;
977     tile->redraw_cycle = priv->redraw_cycle;
978     /* if the tile is already in the cache (it could be one
979     * rendered from another zoom level), it will be
980     * overwritten */
981     g_hash_table_insert (priv->tile_cache, dl->filename, tile);
982     /* NULL-ify dl->filename so that it won't be freed, as
983     * we are using it as a key in the hash table */
984     dl->filename = NULL;
985     }
986     osm_gps_map_map_redraw_idle (map);
987     }
988     g_hash_table_remove(priv->tile_queue, dl->uri);
989    
990     g_free(dl->uri);
991     g_free(dl->folder);
992     g_free(dl->filename);
993     g_free(dl);
994     }
995     else
996     {
997     g_warning("Error downloading tile: %d - %s", msg->status_code, msg->reason_phrase);
998     if (msg->status_code == SOUP_STATUS_NOT_FOUND)
999     {
1000     g_hash_table_insert(priv->missing_tiles, dl->uri, NULL);
1001     g_hash_table_remove(priv->tile_queue, dl->uri);
1002     }
1003     else if (msg->status_code == SOUP_STATUS_CANCELLED)
1004     {
1005     ;//application exiting
1006     }
1007     else
1008     {
1009     #ifdef LIBSOUP22
1010     soup_session_requeue_message(dl->session, msg);
1011     #else
1012     soup_session_requeue_message(session, msg);
1013     #endif
1014     return;
1015     }
1016     }
1017    
1018    
1019     }
1020    
1021     static void
1022     osm_gps_map_download_tile (OsmGpsMap *map, int zoom, int x, int y, gboolean redraw)
1023     {
1024     SoupMessage *msg;
1025     OsmGpsMapPrivate *priv = map->priv;
1026     tile_download_t *dl = g_new0(tile_download_t,1);
1027    
1028     //calculate the uri to download
1029     dl->uri = replace_map_uri(map, priv->repo_uri, zoom, x, y);
1030    
1031     #ifdef LIBSOUP22
1032     dl->session = priv->soup_session;
1033     #endif
1034    
1035     //check the tile has not already been queued for download,
1036     //or has been attempted, and its missing
1037     if (g_hash_table_lookup_extended(priv->tile_queue, dl->uri, NULL, NULL) ||
1038     g_hash_table_lookup_extended(priv->missing_tiles, dl->uri, NULL, NULL) )
1039     {
1040     g_debug("Tile already downloading (or missing)");
1041     g_free(dl->uri);
1042     g_free(dl);
1043     } else {
1044 harbaum 55 dl->folder = g_strdup_printf("%s%c%d%c%d%c",
1045     priv->cache_dir, G_DIR_SEPARATOR,
1046     zoom, G_DIR_SEPARATOR,
1047     x, G_DIR_SEPARATOR);
1048     dl->filename = g_strdup_printf("%s%c%d%c%d%c%d.%s",
1049     priv->cache_dir, G_DIR_SEPARATOR,
1050     zoom, G_DIR_SEPARATOR,
1051     x, G_DIR_SEPARATOR,
1052     y,
1053     priv->image_format);
1054 harbaum 32 dl->map = map;
1055     dl->redraw = redraw;
1056    
1057     g_debug("Download tile: %d,%d z:%d\n\t%s --> %s", x, y, zoom, dl->uri, dl->filename);
1058    
1059     msg = soup_message_new (SOUP_METHOD_GET, dl->uri);
1060     if (msg) {
1061     if (priv->the_google) {
1062     //Set maps.google.com as the referrer
1063     g_debug("Setting Google Referrer");
1064     soup_message_headers_append(msg->request_headers, "Referer", "http://maps.google.com/");
1065     //For google satelite also set the appropriate cookie value
1066     if (priv->uri_format & URI_HAS_Q) {
1067     const char *cookie = g_getenv("GOOGLE_COOKIE");
1068     if (cookie) {
1069     g_debug("Adding Google Cookie");
1070     soup_message_headers_append(msg->request_headers, "Cookie", cookie);
1071     }
1072     }
1073     }
1074    
1075     g_hash_table_insert (priv->tile_queue, dl->uri, msg);
1076     soup_session_queue_message (priv->soup_session, msg, osm_gps_map_tile_download_complete, dl);
1077     } else {
1078     g_warning("Could not create soup message");
1079     g_free(dl->uri);
1080     g_free(dl->folder);
1081     g_free(dl->filename);
1082     g_free(dl);
1083     }
1084     }
1085     }
1086    
1087     static GdkPixbuf *
1088     osm_gps_map_load_cached_tile (OsmGpsMap *map, int zoom, int x, int y)
1089     {
1090     OsmGpsMapPrivate *priv = map->priv;
1091     gchar *filename;
1092 harbaum 55 GdkPixbuf *pixbuf = NULL;
1093 harbaum 32 OsmCachedTile *tile;
1094    
1095 harbaum 55 filename = g_strdup_printf("%s%c%d%c%d%c%d.%s",
1096     priv->cache_dir, G_DIR_SEPARATOR,
1097     zoom, G_DIR_SEPARATOR,
1098     x, G_DIR_SEPARATOR,
1099     y,
1100     priv->image_format);
1101 harbaum 32
1102     tile = g_hash_table_lookup (priv->tile_cache, filename);
1103     if (tile)
1104     {
1105     g_free (filename);
1106     }
1107 harbaum 55 else
1108 harbaum 32 {
1109     pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
1110     if (pixbuf)
1111     {
1112     tile = g_slice_new (OsmCachedTile);
1113     tile->pixbuf = pixbuf;
1114     g_hash_table_insert (priv->tile_cache, filename, tile);
1115     }
1116     }
1117    
1118     /* set/update the redraw_cycle timestamp on the tile */
1119     if (tile)
1120     {
1121     tile->redraw_cycle = priv->redraw_cycle;
1122     pixbuf = g_object_ref (tile->pixbuf);
1123 harbaum 55 }
1124 harbaum 32
1125     return pixbuf;
1126     }
1127    
1128     static GdkPixbuf *
1129     osm_gps_map_find_bigger_tile (OsmGpsMap *map, int zoom, int x, int y,
1130     int *zoom_found)
1131     {
1132     GdkPixbuf *pixbuf;
1133     int next_zoom, next_x, next_y;
1134    
1135     if (zoom == 0) return NULL;
1136     next_zoom = zoom - 1;
1137     next_x = x / 2;
1138     next_y = y / 2;
1139     pixbuf = osm_gps_map_load_cached_tile (map, next_zoom, next_x, next_y);
1140     if (pixbuf)
1141     *zoom_found = next_zoom;
1142     else
1143     pixbuf = osm_gps_map_find_bigger_tile (map, next_zoom, next_x, next_y,
1144     zoom_found);
1145     return pixbuf;
1146     }
1147    
1148     static GdkPixbuf *
1149     osm_gps_map_render_missing_tile_upscaled (OsmGpsMap *map, int zoom,
1150     int x, int y)
1151     {
1152     GdkPixbuf *pixbuf, *big, *area;
1153     int zoom_big, zoom_diff, area_size, area_x, area_y;
1154     int modulo;
1155    
1156     big = osm_gps_map_find_bigger_tile (map, zoom, x, y, &zoom_big);
1157     if (!big) return NULL;
1158    
1159     g_debug ("Found bigger tile (zoom = %d, wanted = %d)", zoom_big, zoom);
1160    
1161     /* get a Pixbuf for the area to magnify */
1162     zoom_diff = zoom - zoom_big;
1163     area_size = TILESIZE >> zoom_diff;
1164     modulo = 1 << zoom_diff;
1165     area_x = (x % modulo) * area_size;
1166     area_y = (y % modulo) * area_size;
1167     area = gdk_pixbuf_new_subpixbuf (big, area_x, area_y,
1168     area_size, area_size);
1169     g_object_unref (big);
1170     pixbuf = gdk_pixbuf_scale_simple (area, TILESIZE, TILESIZE,
1171     GDK_INTERP_NEAREST);
1172     g_object_unref (area);
1173     return pixbuf;
1174     }
1175    
1176     static GdkPixbuf *
1177     osm_gps_map_render_missing_tile (OsmGpsMap *map, int zoom, int x, int y)
1178     {
1179     /* maybe TODO: render from downscaled tiles, if the following fails */
1180     return osm_gps_map_render_missing_tile_upscaled (map, zoom, x, y);
1181     }
1182    
1183     static void
1184     osm_gps_map_load_tile (OsmGpsMap *map, int zoom, int x, int y, int offset_x, int offset_y)
1185     {
1186     OsmGpsMapPrivate *priv = map->priv;
1187 harbaum 55 gchar *filename;
1188 harbaum 32 GdkPixbuf *pixbuf;
1189    
1190     g_debug("Load tile %d,%d (%d,%d) z:%d", x, y, offset_x, offset_y, zoom);
1191    
1192 harbaum 55 if (priv->map_source == OSM_GPS_MAP_SOURCE_NULL) {
1193     osm_gps_map_blit_tile(map, priv->null_tile, offset_x,offset_y);
1194     return;
1195     }
1196 harbaum 32
1197 harbaum 55 filename = g_strdup_printf("%s%c%d%c%d%c%d.%s",
1198     priv->cache_dir, G_DIR_SEPARATOR,
1199     zoom, G_DIR_SEPARATOR,
1200     x, G_DIR_SEPARATOR,
1201     y,
1202     priv->image_format);
1203    
1204     /* try to get file from internal cache first */
1205     if(!(pixbuf = osm_gps_map_load_cached_tile(map, zoom, x, y)))
1206 harbaum 32 pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
1207    
1208 harbaum 55 if(pixbuf)
1209 harbaum 32 {
1210 harbaum 55 g_debug("Found tile %s", filename);
1211 harbaum 32 osm_gps_map_blit_tile(map, pixbuf, offset_x,offset_y);
1212     g_object_unref (pixbuf);
1213     }
1214     else
1215     {
1216     if (priv->map_auto_download)
1217     osm_gps_map_download_tile(map, zoom, x, y, TRUE);
1218    
1219     /* try to render the tile by scaling cached tiles from other zoom
1220     * levels */
1221     pixbuf = osm_gps_map_render_missing_tile (map, zoom, x, y);
1222     if (pixbuf)
1223     {
1224     gdk_draw_pixbuf (priv->pixmap,
1225     priv->gc_map,
1226     pixbuf,
1227     0,0,
1228     offset_x,offset_y,
1229     TILESIZE,TILESIZE,
1230     GDK_RGB_DITHER_NONE, 0, 0);
1231     g_object_unref (pixbuf);
1232     }
1233     else
1234     {
1235     //prevent some artifacts when drawing not yet loaded areas.
1236     gdk_draw_rectangle (priv->pixmap,
1237     GTK_WIDGET(map)->style->white_gc,
1238     TRUE, offset_x, offset_y, TILESIZE, TILESIZE);
1239     }
1240     }
1241 harbaum 55 g_free(filename);
1242 harbaum 32 }
1243    
1244     static void
1245     osm_gps_map_fill_tiles_pixel (OsmGpsMap *map)
1246     {
1247     OsmGpsMapPrivate *priv = map->priv;
1248     int i,j, width, height, tile_x0, tile_y0, tiles_nx, tiles_ny;
1249     int offset_xn = 0;
1250     int offset_yn = 0;
1251     int offset_x;
1252     int offset_y;
1253    
1254     g_debug("Fill tiles: %d,%d z:%d", priv->map_x, priv->map_y, priv->map_zoom);
1255    
1256     offset_x = - priv->map_x % TILESIZE;
1257     offset_y = - priv->map_y % TILESIZE;
1258     if (offset_x > 0) offset_x -= TILESIZE;
1259     if (offset_y > 0) offset_y -= TILESIZE;
1260    
1261     offset_xn = offset_x + EXTRA_BORDER;
1262     offset_yn = offset_y + EXTRA_BORDER;
1263    
1264     width = GTK_WIDGET(map)->allocation.width;
1265     height = GTK_WIDGET(map)->allocation.height;
1266    
1267     tiles_nx = (width - offset_x) / TILESIZE + 1;
1268     tiles_ny = (height - offset_y) / TILESIZE + 1;
1269    
1270     tile_x0 = floor((float)priv->map_x / (float)TILESIZE);
1271     tile_y0 = floor((float)priv->map_y / (float)TILESIZE);
1272    
1273     //TODO: implement wrap around
1274     for (i=tile_x0; i<(tile_x0+tiles_nx);i++)
1275     {
1276     for (j=tile_y0; j<(tile_y0+tiles_ny); j++)
1277     {
1278     if( j<0 || i<0 || i>=exp(priv->map_zoom * M_LN2) || j>=exp(priv->map_zoom * M_LN2))
1279     {
1280     gdk_draw_rectangle (priv->pixmap,
1281     GTK_WIDGET(map)->style->white_gc,
1282     TRUE,
1283     offset_xn, offset_yn,
1284     TILESIZE,TILESIZE);
1285     }
1286     else
1287     {
1288     osm_gps_map_load_tile(map,
1289     priv->map_zoom,
1290     i,j,
1291     offset_xn,offset_yn);
1292     }
1293     offset_yn += TILESIZE;
1294     }
1295     offset_xn += TILESIZE;
1296     offset_yn = offset_y + EXTRA_BORDER;
1297     }
1298     }
1299    
1300     static void
1301     osm_gps_map_print_track (OsmGpsMap *map, GSList *trackpoint_list)
1302     {
1303     OsmGpsMapPrivate *priv = map->priv;
1304    
1305     GSList *list;
1306     int x,y;
1307     int min_x = 0,min_y = 0,max_x = 0,max_y = 0;
1308     int lw = priv->ui_gps_track_width;
1309     int map_x0, map_y0;
1310     #ifdef USE_CAIRO
1311     cairo_t *cr;
1312     #else
1313     int last_x = 0, last_y = 0;
1314     GdkColor color;
1315     GdkGC *gc;
1316     #endif
1317    
1318     #ifdef USE_CAIRO
1319     cr = gdk_cairo_create(priv->pixmap);
1320     cairo_set_line_width (cr, lw);
1321     cairo_set_source_rgba (cr, 60000.0/65535.0, 0.0, 0.0, 0.6);
1322     cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1323     cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
1324     #else
1325     gc = gdk_gc_new(priv->pixmap);
1326     color.green = 0;
1327     color.blue = 0;
1328     color.red = 60000;
1329     gdk_gc_set_rgb_fg_color(gc, &color);
1330     gdk_gc_set_line_attributes(gc, lw, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
1331     #endif
1332    
1333     map_x0 = priv->map_x - EXTRA_BORDER;
1334     map_y0 = priv->map_y - EXTRA_BORDER;
1335     for(list = trackpoint_list; list != NULL; list = list->next)
1336     {
1337     coord_t *tp = list->data;
1338    
1339     x = lon2pixel(priv->map_zoom, tp->rlon) - map_x0;
1340     y = lat2pixel(priv->map_zoom, tp->rlat) - map_y0;
1341    
1342     // first time through loop
1343     if (list == trackpoint_list) {
1344     #ifdef USE_CAIRO
1345     cairo_move_to(cr, x, y);
1346     #else
1347     last_x = x;
1348     last_y = y;
1349     #endif
1350     }
1351    
1352     #ifdef USE_CAIRO
1353     cairo_line_to(cr, x, y);
1354     #else
1355     gdk_draw_line (priv->pixmap, gc, x, y, last_x, last_y);
1356     last_x = x;
1357     last_y = y;
1358     #endif
1359    
1360     max_x = MAX(x,max_x);
1361     min_x = MIN(x,min_x);
1362     max_y = MAX(y,max_y);
1363     min_y = MIN(y,min_y);
1364     }
1365    
1366     gtk_widget_queue_draw_area (
1367     GTK_WIDGET(map),
1368     min_x - lw,
1369     min_y - lw,
1370     max_x + (lw * 2),
1371     max_y + (lw * 2));
1372    
1373     #ifdef USE_CAIRO
1374     cairo_stroke(cr);
1375     cairo_destroy(cr);
1376     #else
1377     g_object_unref(gc);
1378     #endif
1379     }
1380    
1381     /* Prints the gps trip history, and any other tracks */
1382     static void
1383     osm_gps_map_print_tracks (OsmGpsMap *map)
1384     {
1385     OsmGpsMapPrivate *priv = map->priv;
1386    
1387     if (priv->show_trip_history)
1388     osm_gps_map_print_track (map, priv->trip_history);
1389 harbaum 55
1390 harbaum 32 if (priv->tracks)
1391     {
1392     GSList* tmp = priv->tracks;
1393     while (tmp != NULL)
1394     {
1395     osm_gps_map_print_track (map, tmp->data);
1396     tmp = g_slist_next(tmp);
1397     }
1398     }
1399     }
1400    
1401 harbaum 55 static gboolean
1402     osm_gps_map_purge_cache_check(gpointer key, gpointer value, gpointer user)
1403 harbaum 32 {
1404 harbaum 55 return (((OsmCachedTile*)value)->redraw_cycle != ((OsmGpsMapPrivate*)user)->redraw_cycle);
1405 harbaum 32 }
1406    
1407     static void
1408     osm_gps_map_purge_cache (OsmGpsMap *map)
1409     {
1410 harbaum 55 OsmGpsMapPrivate *priv = map->priv;
1411 harbaum 32
1412 harbaum 55 if (g_hash_table_size (priv->tile_cache) < priv->max_tile_cache_size)
1413     return;
1414 harbaum 32
1415 harbaum 55 /* run through the cache, and remove the tiles which have not been used
1416     * during the last redraw operation */
1417     g_hash_table_foreach_remove(priv->tile_cache, osm_gps_map_purge_cache_check, priv);
1418 harbaum 32 }
1419    
1420 harbaum 63 static gboolean
1421 harbaum 32 osm_gps_map_map_redraw (OsmGpsMap *map)
1422     {
1423     OsmGpsMapPrivate *priv = map->priv;
1424    
1425     priv->idle_map_redraw = 0;
1426    
1427     /* the motion_notify handler uses priv->pixmap to redraw the area; if we
1428     * change it while we are dragging, we will end up showing it in the wrong
1429     * place. This could be fixed by carefully recompute the coordinates, but
1430     * for now it's easier just to disable redrawing the map while dragging */
1431     if (priv->dragging)
1432     return FALSE;
1433    
1434     priv->redraw_cycle++;
1435    
1436     /* draw white background to initialise pixmap */
1437     gdk_draw_rectangle (
1438     priv->pixmap,
1439     GTK_WIDGET(map)->style->white_gc,
1440     TRUE,
1441     0, 0,
1442     GTK_WIDGET(map)->allocation.width + EXTRA_BORDER * 2,
1443     GTK_WIDGET(map)->allocation.height + EXTRA_BORDER * 2);
1444    
1445     osm_gps_map_fill_tiles_pixel(map);
1446    
1447     osm_gps_map_print_tracks(map);
1448     osm_gps_map_draw_gps_point(map);
1449     osm_gps_map_print_images(map);
1450 harbaum 65 #ifdef ENABLE_BALLOON
1451 harbaum 57 osm_gps_map_draw_balloon_int(map);
1452 harbaum 65 #endif
1453 harbaum 73
1454 harbaum 61 #ifdef ENABLE_OSD
1455 harbaum 73 if(priv->osd)
1456     priv->osd->draw(priv->osd, EXTRA_BORDER, EXTRA_BORDER);
1457 harbaum 61 #endif
1458 harbaum 32
1459     //osm_gps_map_osd_speed(map, 1.5);
1460     osm_gps_map_purge_cache(map);
1461     gtk_widget_queue_draw (GTK_WIDGET (map));
1462    
1463     return FALSE;
1464     }
1465    
1466     static void
1467     osm_gps_map_map_redraw_idle (OsmGpsMap *map)
1468     {
1469     OsmGpsMapPrivate *priv = map->priv;
1470    
1471     if (priv->idle_map_redraw == 0)
1472     priv->idle_map_redraw = g_idle_add ((GSourceFunc)osm_gps_map_map_redraw, map);
1473     }
1474    
1475     static void
1476     osm_gps_map_init (OsmGpsMap *object)
1477     {
1478     OsmGpsMapPrivate *priv;
1479    
1480     priv = G_TYPE_INSTANCE_GET_PRIVATE (object, OSM_TYPE_GPS_MAP, OsmGpsMapPrivate);
1481     object->priv = priv;
1482    
1483     priv->pixmap = NULL;
1484    
1485     priv->trip_history = NULL;
1486     priv->gps = g_new0(coord_t, 1);
1487     priv->gps_valid = FALSE;
1488    
1489 harbaum 65 #ifdef ENABLE_BALLOON
1490 harbaum 58 priv->balloon.coo = g_new0(coord_t, 1);
1491     priv->balloon.valid = FALSE;
1492     priv->balloon.cb = NULL;
1493 harbaum 65 #endif
1494 harbaum 57
1495 harbaum 61 #ifdef ENABLE_OSD
1496 harbaum 73 priv->osd = NULL;
1497 harbaum 61 #endif
1498    
1499 harbaum 32 priv->tracks = NULL;
1500     priv->images = NULL;
1501    
1502     priv->drag_counter = 0;
1503     priv->drag_mouse_dx = 0;
1504     priv->drag_mouse_dy = 0;
1505     priv->drag_start_mouse_x = 0;
1506     priv->drag_start_mouse_y = 0;
1507    
1508     priv->uri_format = 0;
1509     priv->the_google = FALSE;
1510    
1511 harbaum 55 priv->map_source = -1;
1512    
1513 harbaum 32 #ifndef LIBSOUP22
1514     //Change naumber of concurrent connections option?
1515     priv->soup_session = soup_session_async_new_with_options(
1516     SOUP_SESSION_USER_AGENT,
1517     "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11",
1518     NULL);
1519     #else
1520     /* libsoup-2.2 seems not to be able to set the user agent */
1521     priv->soup_session = soup_session_async_new();
1522     #endif
1523    
1524     //Hash table which maps tile d/l URIs to SoupMessage requests
1525     priv->tile_queue = g_hash_table_new (g_str_hash, g_str_equal);
1526    
1527     //Some mapping providers (Google) have varying degrees of tiles at multiple
1528     //zoom levels
1529     priv->missing_tiles = g_hash_table_new (g_str_hash, g_str_equal);
1530    
1531     /* memory cache for most recently used tiles */
1532     priv->tile_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
1533     g_free, (GDestroyNotify)cached_tile_free);
1534     priv->max_tile_cache_size = 20;
1535    
1536     gtk_widget_add_events (GTK_WIDGET (object),
1537     GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1538     GDK_POINTER_MOTION_MASK |
1539     GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
1540     GTK_WIDGET_SET_FLAGS (object, GTK_CAN_FOCUS);
1541    
1542     g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, my_log_handler, NULL);
1543     }
1544    
1545     #ifndef G_CHECKSUM_MD5
1546     /* simple hash algorithm hack if md5 is not present */
1547     static char *simple_hash(char *str) {
1548     union {
1549     char str[4];
1550     gulong val;
1551     } hash = { .val = 0x55555555 };
1552    
1553     while(*str) {
1554     hash.str[(int)str & 3] ^= *str;
1555     str++;
1556     }
1557     return g_strdup_printf("%08lX", hash.val);
1558     }
1559     #endif
1560    
1561     static GObject *
1562     osm_gps_map_constructor (GType gtype, guint n_properties, GObjectConstructParam *properties)
1563     {
1564     GObject *object;
1565     OsmGpsMapPrivate *priv;
1566 harbaum 55 OsmGpsMap *map;
1567     const char *uri;
1568 harbaum 32
1569     //Always chain up to the parent constructor
1570     object = G_OBJECT_CLASS(osm_gps_map_parent_class)->constructor(gtype, n_properties, properties);
1571 harbaum 55 map = OSM_GPS_MAP(object);
1572 harbaum 32 priv = OSM_GPS_MAP_PRIVATE(object);
1573    
1574 harbaum 55 //user can specify a map source ID, or a repo URI as the map source
1575     uri = osm_gps_map_source_get_repo_uri(OSM_GPS_MAP_SOURCE_NULL);
1576     if ( (priv->map_source == 0) || (strcmp(priv->repo_uri, uri) == 0) ) {
1577     g_debug("Using null source");
1578     priv->map_source = OSM_GPS_MAP_SOURCE_NULL;
1579    
1580     priv->null_tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 256, 256);
1581     gdk_pixbuf_fill(priv->null_tile, 0xcccccc00);
1582     }
1583     else if (priv->map_source >= 0) {
1584     //check if the source given is valid
1585     uri = osm_gps_map_source_get_repo_uri(priv->map_source);
1586     if (uri) {
1587     g_debug("Setting map source from ID");
1588     g_free(priv->repo_uri);
1589    
1590     priv->repo_uri = g_strdup(uri);
1591     priv->image_format = g_strdup(
1592     osm_gps_map_source_get_image_format(priv->map_source));
1593     priv->max_zoom = osm_gps_map_source_get_max_zoom(priv->map_source);
1594     priv->min_zoom = osm_gps_map_source_get_min_zoom(priv->map_source);
1595     }
1596     }
1597    
1598 harbaum 32 if (!priv->cache_dir_is_full_path) {
1599     #ifdef G_CHECKSUM_MD5
1600     char *md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, priv->repo_uri, -1);
1601     #else
1602     char *md5 = simple_hash(priv->repo_uri);
1603     #endif
1604    
1605     if (priv->cache_dir) {
1606     char *old = priv->cache_dir;
1607     //the new cachedir is the given cache dir + the md5 of the repo_uri
1608 harbaum 55 priv->cache_dir = g_strdup_printf("%s%c%s", old, G_DIR_SEPARATOR, md5);
1609 harbaum 32 g_debug("Adjusting cache dir %s -> %s", old, priv->cache_dir);
1610     g_free(old);
1611 harbaum 55 } else {
1612     //the new cachedir is the current dir + the md5 of the repo_uri
1613     priv->cache_dir = g_strdup(md5);
1614 harbaum 32 }
1615    
1616     g_free(md5);
1617     }
1618    
1619 harbaum 55 inspect_map_uri(map);
1620    
1621 harbaum 32 return object;
1622     }
1623    
1624     static void
1625     osm_gps_map_dispose (GObject *object)
1626     {
1627     OsmGpsMap *map = OSM_GPS_MAP(object);
1628     OsmGpsMapPrivate *priv = map->priv;
1629    
1630     if (priv->is_disposed)
1631     return;
1632    
1633     priv->is_disposed = TRUE;
1634    
1635     soup_session_abort(priv->soup_session);
1636     g_object_unref(priv->soup_session);
1637    
1638     g_hash_table_destroy(priv->tile_queue);
1639     g_hash_table_destroy(priv->missing_tiles);
1640     g_hash_table_destroy(priv->tile_cache);
1641    
1642     osm_gps_map_free_images(map);
1643    
1644     if(priv->pixmap)
1645     g_object_unref (priv->pixmap);
1646    
1647 harbaum 55 if (priv->null_tile)
1648     g_object_unref (priv->null_tile);
1649    
1650 harbaum 32 if(priv->gc_map)
1651     g_object_unref(priv->gc_map);
1652    
1653     if (priv->idle_map_redraw != 0)
1654     g_source_remove (priv->idle_map_redraw);
1655    
1656 harbaum 57 g_free(priv->gps);
1657 harbaum 65
1658     #ifdef ENABLE_BALLOON
1659 harbaum 58 g_free(priv->balloon.coo);
1660 harbaum 65 #endif
1661 harbaum 57
1662 harbaum 61 #ifdef ENABLE_OSD
1663 harbaum 73 if(priv->osd)
1664     priv->osd->free(priv->osd);
1665 harbaum 61 #endif
1666    
1667 harbaum 32 G_OBJECT_CLASS (osm_gps_map_parent_class)->dispose (object);
1668     }
1669    
1670     static void
1671     osm_gps_map_finalize (GObject *object)
1672     {
1673     OsmGpsMap *map = OSM_GPS_MAP(object);
1674     OsmGpsMapPrivate *priv = map->priv;
1675    
1676     g_free(priv->cache_dir);
1677     g_free(priv->repo_uri);
1678 harbaum 55 g_free(priv->image_format);
1679 harbaum 32
1680     osm_gps_map_free_trip(map);
1681     osm_gps_map_free_tracks(map);
1682    
1683     G_OBJECT_CLASS (osm_gps_map_parent_class)->finalize (object);
1684     }
1685    
1686     static void
1687     osm_gps_map_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1688     {
1689     g_return_if_fail (OSM_IS_GPS_MAP (object));
1690     OsmGpsMap *map = OSM_GPS_MAP(object);
1691     OsmGpsMapPrivate *priv = map->priv;
1692    
1693     switch (prop_id)
1694     {
1695     case PROP_AUTO_CENTER:
1696     priv->map_auto_center = g_value_get_boolean (value);
1697     break;
1698     case PROP_RECORD_TRIP_HISTORY:
1699     priv->record_trip_history = g_value_get_boolean (value);
1700     break;
1701     case PROP_SHOW_TRIP_HISTORY:
1702     priv->show_trip_history = g_value_get_boolean (value);
1703     break;
1704     case PROP_AUTO_DOWNLOAD:
1705     priv->map_auto_download = g_value_get_boolean (value);
1706     break;
1707     case PROP_REPO_URI:
1708     priv->repo_uri = g_value_dup_string (value);
1709     break;
1710     case PROP_PROXY_URI:
1711     if ( g_value_get_string(value) ) {
1712     priv->proxy_uri = g_value_dup_string (value);
1713     g_debug("Setting proxy server: %s", priv->proxy_uri);
1714 harbaum 55
1715 harbaum 32 #ifndef LIBSOUP22
1716     GValue val = {0};
1717 harbaum 55
1718 harbaum 32 SoupURI* uri = soup_uri_new(priv->proxy_uri);
1719     g_value_init(&val, SOUP_TYPE_URI);
1720     g_value_take_boxed(&val, uri);
1721    
1722     g_object_set_property(G_OBJECT(priv->soup_session),SOUP_SESSION_PROXY_URI,&val);
1723     #else
1724     SoupUri* uri = soup_uri_new(priv->proxy_uri);
1725     g_object_set(G_OBJECT(priv->soup_session), SOUP_SESSION_PROXY_URI, uri, NULL);
1726     #endif
1727     } else
1728     priv->proxy_uri = NULL;
1729    
1730     break;
1731     case PROP_TILE_CACHE_DIR:
1732 harbaum 55 if ( g_value_get_string(value) )
1733     priv->cache_dir = g_value_dup_string (value);
1734 harbaum 32 break;
1735     case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
1736     priv->cache_dir_is_full_path = g_value_get_boolean (value);
1737     break;
1738     case PROP_ZOOM:
1739     priv->map_zoom = g_value_get_int (value);
1740     break;
1741     case PROP_MAX_ZOOM:
1742     priv->max_zoom = g_value_get_int (value);
1743     break;
1744     case PROP_MIN_ZOOM:
1745     priv->min_zoom = g_value_get_int (value);
1746     break;
1747     case PROP_MAP_X:
1748     priv->map_x = g_value_get_int (value);
1749     priv->center_coord_set = FALSE;
1750     break;
1751     case PROP_MAP_Y:
1752     priv->map_y = g_value_get_int (value);
1753     priv->center_coord_set = FALSE;
1754     break;
1755     case PROP_GPS_TRACK_WIDTH:
1756     priv->ui_gps_track_width = g_value_get_int (value);
1757     break;
1758     case PROP_GPS_POINT_R1:
1759     priv->ui_gps_point_inner_radius = g_value_get_int (value);
1760     break;
1761     case PROP_GPS_POINT_R2:
1762     priv->ui_gps_point_outer_radius = g_value_get_int (value);
1763     break;
1764 harbaum 55 case PROP_MAP_SOURCE:
1765     priv->map_source = g_value_get_int (value);
1766     break;
1767     case PROP_IMAGE_FORMAT:
1768     priv->image_format = g_value_dup_string (value);
1769     break;
1770 harbaum 32 default:
1771     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1772     break;
1773     }
1774     }
1775    
1776     static void
1777     osm_gps_map_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
1778     {
1779     g_return_if_fail (OSM_IS_GPS_MAP (object));
1780     float lat,lon;
1781     OsmGpsMap *map = OSM_GPS_MAP(object);
1782     OsmGpsMapPrivate *priv = map->priv;
1783    
1784     switch (prop_id)
1785     {
1786     case PROP_AUTO_CENTER:
1787     g_value_set_boolean(value, priv->map_auto_center);
1788     break;
1789     case PROP_RECORD_TRIP_HISTORY:
1790     g_value_set_boolean(value, priv->record_trip_history);
1791     break;
1792     case PROP_SHOW_TRIP_HISTORY:
1793     g_value_set_boolean(value, priv->show_trip_history);
1794     break;
1795     case PROP_AUTO_DOWNLOAD:
1796     g_value_set_boolean(value, priv->map_auto_download);
1797     break;
1798     case PROP_REPO_URI:
1799     g_value_set_string(value, priv->repo_uri);
1800     break;
1801     case PROP_PROXY_URI:
1802     g_value_set_string(value, priv->proxy_uri);
1803     break;
1804     case PROP_TILE_CACHE_DIR:
1805     g_value_set_string(value, priv->cache_dir);
1806     break;
1807     case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
1808     g_value_set_boolean(value, priv->cache_dir_is_full_path);
1809     break;
1810     case PROP_ZOOM:
1811     g_value_set_int(value, priv->map_zoom);
1812     break;
1813     case PROP_MAX_ZOOM:
1814     g_value_set_int(value, priv->max_zoom);
1815     break;
1816     case PROP_MIN_ZOOM:
1817     g_value_set_int(value, priv->min_zoom);
1818     break;
1819     case PROP_LATITUDE:
1820     lat = pixel2lat(priv->map_zoom,
1821     priv->map_y + (GTK_WIDGET(map)->allocation.height / 2));
1822     g_value_set_float(value, rad2deg(lat));
1823     break;
1824     case PROP_LONGITUDE:
1825     lon = pixel2lon(priv->map_zoom,
1826     priv->map_x + (GTK_WIDGET(map)->allocation.width / 2));
1827     g_value_set_float(value, rad2deg(lon));
1828     break;
1829     case PROP_MAP_X:
1830     g_value_set_int(value, priv->map_x);
1831     break;
1832     case PROP_MAP_Y:
1833     g_value_set_int(value, priv->map_y);
1834     break;
1835     case PROP_TILES_QUEUED:
1836     g_value_set_int(value, g_hash_table_size(priv->tile_queue));
1837     break;
1838     case PROP_GPS_TRACK_WIDTH:
1839     g_value_set_int(value, priv->ui_gps_track_width);
1840     break;
1841     case PROP_GPS_POINT_R1:
1842     g_value_set_int(value, priv->ui_gps_point_inner_radius);
1843     break;
1844     case PROP_GPS_POINT_R2:
1845     g_value_set_int(value, priv->ui_gps_point_outer_radius);
1846     break;
1847 harbaum 55 case PROP_MAP_SOURCE:
1848     g_value_set_int(value, priv->map_source);
1849     break;
1850     case PROP_IMAGE_FORMAT:
1851     g_value_set_string(value, priv->image_format);
1852     break;
1853 harbaum 32 default:
1854     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1855     break;
1856     }
1857     }
1858    
1859     static gboolean
1860 harbaum 55 osm_gps_map_scroll_event (GtkWidget *widget, GdkEventScroll *event)
1861 harbaum 32 {
1862     OsmGpsMap *map = OSM_GPS_MAP(widget);
1863     OsmGpsMapPrivate *priv = map->priv;
1864    
1865     if (event->direction == GDK_SCROLL_UP)
1866     {
1867     osm_gps_map_set_zoom(map, priv->map_zoom+1);
1868     }
1869     else
1870     {
1871     osm_gps_map_set_zoom(map, priv->map_zoom-1);
1872     }
1873    
1874     return FALSE;
1875     }
1876    
1877     static gboolean
1878     osm_gps_map_button_press (GtkWidget *widget, GdkEventButton *event)
1879     {
1880     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1881    
1882 harbaum 65 #ifdef ENABLE_BALLOON
1883 harbaum 58 /* don't drag if the user clicked within the balloon */
1884     if (osm_gps_map_in_balloon(priv,
1885     event->x + EXTRA_BORDER,
1886     event->y + EXTRA_BORDER))
1887     {
1888     priv->drag_counter = -1;
1889     return FALSE;
1890     }
1891 harbaum 65 #endif
1892 harbaum 58
1893 harbaum 63 #ifdef ENABLE_OSD
1894 harbaum 66 #define SCROLL_STEP 10
1895 harbaum 63
1896 harbaum 66 /* pressed inside OSD control? */
1897 harbaum 73 if(priv->osd) {
1898     osd_button_t but = priv->osd->check(event->x, event->y);
1899     if(but != OSD_NONE)
1900     {
1901     priv->drag_counter = -1;
1902    
1903     switch(but) {
1904     case OSD_UP:
1905     priv->map_y -= GTK_WIDGET(widget)->allocation.height/SCROLL_STEP;
1906     priv->center_coord_set = FALSE;
1907     break;
1908 harbaum 66
1909 harbaum 73 case OSD_DOWN:
1910     priv->map_y += GTK_WIDGET(widget)->allocation.height/SCROLL_STEP;
1911     priv->center_coord_set = FALSE;
1912     break;
1913 harbaum 66
1914 harbaum 73 case OSD_LEFT:
1915     priv->map_x -= GTK_WIDGET(widget)->allocation.width/SCROLL_STEP;
1916     priv->center_coord_set = FALSE;
1917     break;
1918    
1919     case OSD_RIGHT:
1920     priv->map_x += GTK_WIDGET(widget)->allocation.width/SCROLL_STEP;
1921     priv->center_coord_set = FALSE;
1922     break;
1923    
1924     case OSD_IN:
1925     osm_gps_map_set_zoom(OSM_GPS_MAP(widget), priv->map_zoom+1);
1926     break;
1927    
1928     case OSD_OUT:
1929     osm_gps_map_set_zoom(OSM_GPS_MAP(widget), priv->map_zoom-1);
1930     break;
1931    
1932     default:
1933     /* all custom buttons are forwarded to the application */
1934     if(priv->osd->cb)
1935     priv->osd->cb(but, priv->osd->data);
1936     break;
1937     }
1938    
1939     osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
1940    
1941     return FALSE;
1942 harbaum 63 }
1943     }
1944     #endif
1945    
1946 harbaum 66 priv->drag_counter = 0;
1947     priv->drag_start_mouse_x = (int) event->x;
1948     priv->drag_start_mouse_y = (int) event->y;
1949     priv->drag_start_map_x = priv->map_x;
1950     priv->drag_start_map_y = priv->map_y;
1951    
1952     return FALSE;
1953     }
1954    
1955     static gboolean
1956     osm_gps_map_button_release (GtkWidget *widget, GdkEventButton *event)
1957     {
1958     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1959    
1960 harbaum 65 #ifdef ENABLE_BALLOON
1961 harbaum 58 /* released inside the balloon? */
1962     if (osm_gps_map_in_balloon(priv,
1963     event->x + EXTRA_BORDER,
1964     event->y + EXTRA_BORDER))
1965     {
1966     osm_gps_map_handle_balloon_click(OSM_GPS_MAP(widget),
1967     event->x - priv->balloon.rect.x + EXTRA_BORDER,
1968     event->y - priv->balloon.rect.y + EXTRA_BORDER);
1969     }
1970 harbaum 65 #endif
1971 harbaum 58
1972 harbaum 32 if (priv->dragging)
1973     {
1974     priv->dragging = FALSE;
1975    
1976     priv->map_x = priv->drag_start_map_x;
1977     priv->map_y = priv->drag_start_map_y;
1978    
1979     priv->map_x += (priv->drag_start_mouse_x - (int) event->x);
1980     priv->map_y += (priv->drag_start_mouse_y - (int) event->y);
1981    
1982     priv->center_coord_set = FALSE;
1983    
1984     osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
1985     }
1986    
1987     priv->drag_mouse_dx = 0;
1988     priv->drag_mouse_dy = 0;
1989 harbaum 58 priv->drag_counter = -1;
1990 harbaum 32
1991     return FALSE;
1992     }
1993    
1994     static gboolean
1995 harbaum 68 osm_gps_map_expose (GtkWidget *widget, GdkEventExpose *event);
1996    
1997     static gboolean
1998 harbaum 32 osm_gps_map_motion_notify (GtkWidget *widget, GdkEventMotion *event)
1999     {
2000     int x, y;
2001     GdkModifierType state;
2002     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
2003    
2004     if (event->is_hint)
2005     gdk_window_get_pointer (event->window, &x, &y, &state);
2006     else
2007     {
2008     x = event->x;
2009     y = event->y;
2010     state = event->state;
2011     }
2012    
2013     // are we being dragged
2014     if (!(state & GDK_BUTTON1_MASK))
2015     return FALSE;
2016    
2017 harbaum 58 if (priv->drag_counter < 0)
2018     return FALSE;
2019    
2020 harbaum 32 priv->drag_counter++;
2021    
2022     // we havent dragged more than 6 pixels
2023     if (priv->drag_counter < 6)
2024     return FALSE;
2025    
2026 harbaum 68 #ifdef USE_MAEMO
2027     /* reduce update frequency on maemo to keep screen update fluid */
2028     static guint32 last_time = 0;
2029    
2030     if(event->time - last_time < 100) return FALSE;
2031     last_time = event->time;
2032     #endif
2033    
2034 harbaum 32 priv->dragging = TRUE;
2035    
2036     if (priv->map_auto_center)
2037     g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
2038    
2039     priv->drag_mouse_dx = x - priv->drag_start_mouse_x;
2040     priv->drag_mouse_dy = y - priv->drag_start_mouse_y;
2041    
2042 harbaum 61 #ifdef ENABLE_OSD
2043 harbaum 73 if(priv->osd) {
2044     /* undo OSD */
2045     priv->osd->restore (priv->osd);
2046 harbaum 61
2047 harbaum 73 /* draw new OSD */
2048     priv->osd->draw (priv->osd,
2049     EXTRA_BORDER - priv->drag_mouse_dx,
2050     EXTRA_BORDER - priv->drag_mouse_dy);
2051     }
2052 harbaum 61 #endif
2053 harbaum 73
2054 harbaum 68 osm_gps_map_expose (widget, NULL);
2055    
2056    
2057     return FALSE;
2058     }
2059    
2060     static gboolean
2061     osm_gps_map_configure (GtkWidget *widget, GdkEventConfigure *event)
2062     {
2063     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
2064    
2065     /* create pixmap */
2066     if (priv->pixmap)
2067     g_object_unref (priv->pixmap);
2068    
2069     priv->pixmap = gdk_pixmap_new (
2070     widget->window,
2071     widget->allocation.width + EXTRA_BORDER * 2,
2072     widget->allocation.height + EXTRA_BORDER * 2,
2073     -1);
2074    
2075 harbaum 73 #ifdef ENABLE_OSD
2076     /* the osd needs some references to map internal objects */
2077     if(priv->osd)
2078     {
2079     priv->osd->pixmap = priv->pixmap;
2080     priv->osd->widget = widget;
2081     }
2082     #endif
2083    
2084 harbaum 68 /* and gc, used for clipping (I think......) */
2085     if(priv->gc_map)
2086     g_object_unref(priv->gc_map);
2087    
2088     priv->gc_map = gdk_gc_new(priv->pixmap);
2089    
2090     osm_gps_map_map_redraw(OSM_GPS_MAP(widget));
2091    
2092     return FALSE;
2093     }
2094    
2095     static gboolean
2096     osm_gps_map_expose (GtkWidget *widget, GdkEventExpose *event)
2097     {
2098     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
2099    
2100 harbaum 32 gdk_draw_drawable (
2101     widget->window,
2102     widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
2103     priv->pixmap,
2104     0,0,
2105 harbaum 68 priv->drag_mouse_dx - EXTRA_BORDER,
2106     priv->drag_mouse_dy - EXTRA_BORDER,
2107 harbaum 32 -1,-1);
2108    
2109     //Paint white outside of the map if dragging. Its less
2110     //ugly than painting the corrupted map
2111     if(priv->drag_mouse_dx>EXTRA_BORDER) {
2112     gdk_draw_rectangle (
2113     widget->window,
2114     widget->style->white_gc,
2115     TRUE,
2116     0, 0,
2117     priv->drag_mouse_dx - EXTRA_BORDER,
2118     widget->allocation.height);
2119     }
2120     else if (-priv->drag_mouse_dx > EXTRA_BORDER)
2121     {
2122     gdk_draw_rectangle (
2123     widget->window,
2124     widget->style->white_gc,
2125     TRUE,
2126     priv->drag_mouse_dx + widget->allocation.width + EXTRA_BORDER, 0,
2127     -priv->drag_mouse_dx - EXTRA_BORDER,
2128     widget->allocation.height);
2129     }
2130    
2131     if (priv->drag_mouse_dy>EXTRA_BORDER) {
2132     gdk_draw_rectangle (
2133     widget->window,
2134     widget->style->white_gc,
2135     TRUE,
2136     0, 0,
2137     widget->allocation.width,
2138     priv->drag_mouse_dy - EXTRA_BORDER);
2139     }
2140     else if (-priv->drag_mouse_dy > EXTRA_BORDER)
2141     {
2142     gdk_draw_rectangle (
2143     widget->window,
2144     widget->style->white_gc,
2145     TRUE,
2146     0, priv->drag_mouse_dy + widget->allocation.height + EXTRA_BORDER,
2147     widget->allocation.width,
2148     -priv->drag_mouse_dy - EXTRA_BORDER);
2149     }
2150 harbaum 68 #if 0
2151     if(!priv->dragging)
2152     gdk_draw_drawable (
2153 harbaum 32 widget->window,
2154     widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
2155     priv->pixmap,
2156 harbaum 58 event->area.x + EXTRA_BORDER,
2157     event->area.y + EXTRA_BORDER,
2158 harbaum 32 event->area.x, event->area.y,
2159     event->area.width, event->area.height);
2160 harbaum 68 #endif
2161 harbaum 32 return FALSE;
2162     }
2163    
2164     static void
2165     osm_gps_map_class_init (OsmGpsMapClass *klass)
2166     {
2167     GObjectClass* object_class = G_OBJECT_CLASS (klass);
2168     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
2169    
2170     g_type_class_add_private (klass, sizeof (OsmGpsMapPrivate));
2171    
2172     object_class->dispose = osm_gps_map_dispose;
2173     object_class->finalize = osm_gps_map_finalize;
2174     object_class->constructor = osm_gps_map_constructor;
2175     object_class->set_property = osm_gps_map_set_property;
2176     object_class->get_property = osm_gps_map_get_property;
2177    
2178     widget_class->expose_event = osm_gps_map_expose;
2179     widget_class->configure_event = osm_gps_map_configure;
2180     widget_class->button_press_event = osm_gps_map_button_press;
2181     widget_class->button_release_event = osm_gps_map_button_release;
2182     widget_class->motion_notify_event = osm_gps_map_motion_notify;
2183 harbaum 55 widget_class->scroll_event = osm_gps_map_scroll_event;
2184 harbaum 32
2185     g_object_class_install_property (object_class,
2186     PROP_AUTO_CENTER,
2187     g_param_spec_boolean ("auto-center",
2188     "auto center",
2189     "map auto center",
2190     TRUE,
2191     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2192    
2193     g_object_class_install_property (object_class,
2194     PROP_RECORD_TRIP_HISTORY,
2195     g_param_spec_boolean ("record-trip-history",
2196     "record trip history",
2197     "should all gps points be recorded in a trip history",
2198     TRUE,
2199     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2200    
2201     g_object_class_install_property (object_class,
2202     PROP_SHOW_TRIP_HISTORY,
2203     g_param_spec_boolean ("show-trip-history",
2204     "show trip history",
2205     "should the recorded trip history be shown on the map",
2206     TRUE,
2207     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2208    
2209     g_object_class_install_property (object_class,
2210     PROP_AUTO_DOWNLOAD,
2211     g_param_spec_boolean ("auto-download",
2212     "auto download",
2213     "map auto download",
2214     TRUE,
2215     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2216    
2217     g_object_class_install_property (object_class,
2218     PROP_REPO_URI,
2219     g_param_spec_string ("repo-uri",
2220     "repo uri",
2221 harbaum 55 "map source tile repository uri",
2222     OSM_REPO_URI,
2223 harbaum 32 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2224    
2225 harbaum 55 g_object_class_install_property (object_class,
2226 harbaum 32 PROP_PROXY_URI,
2227     g_param_spec_string ("proxy-uri",
2228     "proxy uri",
2229     "http proxy uri on NULL",
2230     NULL,
2231     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2232    
2233     g_object_class_install_property (object_class,
2234     PROP_TILE_CACHE_DIR,
2235     g_param_spec_string ("tile-cache",
2236     "tile cache",
2237     "osm local tile cache dir",
2238 harbaum 55 NULL,
2239 harbaum 32 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2240    
2241     g_object_class_install_property (object_class,
2242     PROP_TILE_CACHE_DIR_IS_FULL_PATH,
2243     g_param_spec_boolean ("tile-cache-is-full-path",
2244     "tile cache is full path",
2245     "if true, the path passed to tile-cache is interpreted as the full cache path",
2246     FALSE,
2247     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2248    
2249     g_object_class_install_property (object_class,
2250     PROP_ZOOM,
2251     g_param_spec_int ("zoom",
2252     "zoom",
2253     "zoom level",
2254     MIN_ZOOM, /* minimum property value */
2255     MAX_ZOOM, /* maximum property value */
2256     3,
2257     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2258    
2259     g_object_class_install_property (object_class,
2260     PROP_MAX_ZOOM,
2261     g_param_spec_int ("max-zoom",
2262     "max zoom",
2263     "maximum zoom level",
2264     MIN_ZOOM, /* minimum property value */
2265     MAX_ZOOM, /* maximum property value */
2266 harbaum 55 OSM_MAX_ZOOM,
2267 harbaum 32 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2268    
2269     g_object_class_install_property (object_class,
2270     PROP_MIN_ZOOM,
2271     g_param_spec_int ("min-zoom",
2272     "min zoom",
2273     "minimum zoom level",
2274     MIN_ZOOM, /* minimum property value */
2275     MAX_ZOOM, /* maximum property value */
2276 harbaum 55 OSM_MIN_ZOOM,
2277 harbaum 32 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2278    
2279     g_object_class_install_property (object_class,
2280     PROP_LATITUDE,
2281     g_param_spec_float ("latitude",
2282     "latitude",
2283     "latitude in degrees",
2284     -90.0, /* minimum property value */
2285     90.0, /* maximum property value */
2286     0,
2287     G_PARAM_READABLE));
2288    
2289     g_object_class_install_property (object_class,
2290     PROP_LONGITUDE,
2291     g_param_spec_float ("longitude",
2292     "longitude",
2293     "longitude in degrees",
2294     -180.0, /* minimum property value */
2295     180.0, /* maximum property value */
2296     0,
2297     G_PARAM_READABLE));
2298    
2299     g_object_class_install_property (object_class,
2300     PROP_MAP_X,
2301     g_param_spec_int ("map-x",
2302     "map-x",
2303     "initial map x location",
2304     G_MININT, /* minimum property value */
2305     G_MAXINT, /* maximum property value */
2306     890,
2307     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2308    
2309     g_object_class_install_property (object_class,
2310     PROP_MAP_Y,
2311     g_param_spec_int ("map-y",
2312     "map-y",
2313     "initial map y location",
2314     G_MININT, /* minimum property value */
2315     G_MAXINT, /* maximum property value */
2316     515,
2317     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2318    
2319     g_object_class_install_property (object_class,
2320     PROP_TILES_QUEUED,
2321     g_param_spec_int ("tiles-queued",
2322     "tiles-queued",
2323     "number of tiles currently waiting to download",
2324     G_MININT, /* minimum property value */
2325     G_MAXINT, /* maximum property value */
2326     0,
2327     G_PARAM_READABLE));
2328    
2329     g_object_class_install_property (object_class,
2330     PROP_GPS_TRACK_WIDTH,
2331     g_param_spec_int ("gps-track-width",
2332     "gps-track-width",
2333     "width of the lines drawn for the gps track",
2334     1, /* minimum property value */
2335     G_MAXINT, /* maximum property value */
2336     4,
2337     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2338    
2339     g_object_class_install_property (object_class,
2340     PROP_GPS_POINT_R1,
2341     g_param_spec_int ("gps-track-point-radius",
2342     "gps-track-point-radius",
2343     "radius of the gps point inner circle",
2344     0, /* minimum property value */
2345     G_MAXINT, /* maximum property value */
2346 harbaum 66 10,
2347 harbaum 32 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2348    
2349     g_object_class_install_property (object_class,
2350     PROP_GPS_POINT_R2,
2351     g_param_spec_int ("gps-track-highlight-radius",
2352     "gps-track-highlight-radius",
2353     "radius of the gps point highlight circle",
2354     0, /* minimum property value */
2355     G_MAXINT, /* maximum property value */
2356     20,
2357     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2358    
2359 harbaum 55 g_object_class_install_property (object_class,
2360     PROP_MAP_SOURCE,
2361     g_param_spec_int ("map-source",
2362     "map source",
2363     "map source ID",
2364     -1, /* minimum property value */
2365     G_MAXINT, /* maximum property value */
2366     -1,
2367     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2368    
2369     g_object_class_install_property (object_class,
2370     PROP_IMAGE_FORMAT,
2371     g_param_spec_string ("image-format",
2372     "image format",
2373     "map source tile repository image format (jpg, png)",
2374     OSM_IMAGE_FORMAT,
2375     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2376 harbaum 32 }
2377    
2378 harbaum 55 const char*
2379     osm_gps_map_source_get_friendly_name(OsmGpsMapSource_t source)
2380     {
2381     switch(source)
2382     {
2383     case OSM_GPS_MAP_SOURCE_NULL:
2384     return "None";
2385     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
2386     return "OpenStreetMap";
2387     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
2388     return "OpenStreetMap Renderer";
2389     case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
2390     return "OpenAerialMap";
2391     case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
2392     return "Maps-For-Free";
2393     case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
2394     return "Google Maps";
2395     case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
2396     return "Google Satellite";
2397     case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
2398     return "Google Hybrid";
2399     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
2400     return "Virtual Earth";
2401     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
2402     return "Virtual Earth Satellite";
2403     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
2404     return "Virtual Earth Hybrid";
2405     case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
2406     return "Yahoo Maps";
2407     case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
2408     return "Yahoo Satellite";
2409     case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
2410     return "Yahoo Hybrid";
2411     default:
2412     return NULL;
2413     }
2414     return NULL;
2415     }
2416    
2417     //http://www.internettablettalk.com/forums/showthread.php?t=5209
2418     //https://garage.maemo.org/plugins/scmsvn/viewcvs.php/trunk/src/maps.c?root=maemo-mapper&view=markup
2419     //http://www.ponies.me.uk/maps/GoogleTileUtils.java
2420     //http://www.mgmaps.com/cache/MapTileCacher.perl
2421     const char*
2422     osm_gps_map_source_get_repo_uri(OsmGpsMapSource_t source)
2423     {
2424     switch(source)
2425     {
2426     case OSM_GPS_MAP_SOURCE_NULL:
2427     return "none://";
2428     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
2429     return OSM_REPO_URI;
2430     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
2431     return "http://tah.openstreetmap.org/Tiles/tile/#Z/#X/#Y.png";
2432     case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
2433     return "http://tile.openaerialmap.org/tiles/1.0.0/openaerialmap-900913/#Z/#X/#Y.jpg";
2434     case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
2435     return "http://maps-for-free.com/layer/relief/z#Z/row#Y/#Z_#X-#Y.jpg";
2436     case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
2437     return "http://mt#R.google.com/vt/v=w2.97&x=#X&y=#Y&z=#Z";
2438     case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
2439     return "http://khm#R.google.com/kh?n=404&v=3&t=#Q";
2440     case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
2441     return NULL; /* No longer working "http://mt#R.google.com/mt?n=404&v=w2t.99&x=#X&y=#Y&zoom=#S" */
2442     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
2443     return "http://a#R.ortho.tiles.virtualearth.net/tiles/r#W.jpeg?g=50";
2444     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
2445     return "http://a#R.ortho.tiles.virtualearth.net/tiles/a#W.jpeg?g=50";
2446     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
2447     return "http://a#R.ortho.tiles.virtualearth.net/tiles/h#W.jpeg?g=50";
2448     case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
2449     case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
2450     case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
2451     /* TODO: Implement signed Y, aka U
2452     * http://us.maps3.yimg.com/aerial.maps.yimg.com/ximg?v=1.7&t=a&s=256&x=%d&y=%-d&z=%d
2453     * x = tilex,
2454     * y = (1 << (MAX_ZOOM - zoom)) - tiley - 1,
2455     * z = zoom - (MAX_ZOOM - 17));
2456     */
2457     return NULL;
2458     default:
2459     return NULL;
2460     }
2461     return NULL;
2462     }
2463    
2464     const char *
2465     osm_gps_map_source_get_image_format(OsmGpsMapSource_t source)
2466     {
2467     switch(source) {
2468     case OSM_GPS_MAP_SOURCE_NULL:
2469     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
2470     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
2471     return "png";
2472     case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
2473     case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
2474     case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
2475     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
2476     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
2477     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
2478     case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
2479     case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
2480     case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
2481     case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
2482     case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
2483     return "jpg";
2484     default:
2485     return "bin";
2486     }
2487     return "bin";
2488     }
2489    
2490    
2491     int
2492     osm_gps_map_source_get_min_zoom(OsmGpsMapSource_t source)
2493     {
2494     return 1;
2495     }
2496    
2497     int
2498     osm_gps_map_source_get_max_zoom(OsmGpsMapSource_t source)
2499     {
2500     switch(source) {
2501     case OSM_GPS_MAP_SOURCE_NULL:
2502     return 18;
2503     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
2504     return OSM_MAX_ZOOM;
2505     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
2506     case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
2507     case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
2508     case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
2509     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
2510     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
2511     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
2512     case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
2513     case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
2514     case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
2515     return 17;
2516     case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
2517     return 11;
2518     case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
2519     return 18;
2520     default:
2521     return 17;
2522     }
2523     return 17;
2524     }
2525    
2526 harbaum 32 void
2527     osm_gps_map_download_maps (OsmGpsMap *map, coord_t *pt1, coord_t *pt2, int zoom_start, int zoom_end)
2528     {
2529     int i,j,zoom,num_tiles;
2530     OsmGpsMapPrivate *priv = map->priv;
2531    
2532     if (pt1 && pt2)
2533     {
2534     gchar *filename;
2535     num_tiles = 0;
2536     zoom_end = CLAMP(zoom_end, priv->min_zoom, priv->max_zoom);
2537     g_debug("Download maps: z:%d->%d",zoom_start, zoom_end);
2538    
2539     for(zoom=zoom_start; zoom<=zoom_end; zoom++)
2540     {
2541     int x1,y1,x2,y2;
2542    
2543     x1 = (int)floor((float)lon2pixel(zoom, pt1->rlon) / (float)TILESIZE);
2544     y1 = (int)floor((float)lat2pixel(zoom, pt1->rlat) / (float)TILESIZE);
2545    
2546     x2 = (int)floor((float)lon2pixel(zoom, pt2->rlon) / (float)TILESIZE);
2547     y2 = (int)floor((float)lat2pixel(zoom, pt2->rlat) / (float)TILESIZE);
2548    
2549     // loop x1-x2
2550     for(i=x1; i<=x2; i++)
2551     {
2552     // loop y1 - y2
2553     for(j=y1; j<=y2; j++)
2554     {
2555     // x = i, y = j
2556 harbaum 55 filename = g_strdup_printf("%s%c%d%c%d%c%d.%s",
2557     priv->cache_dir, G_DIR_SEPARATOR,
2558     zoom, G_DIR_SEPARATOR,
2559     i, G_DIR_SEPARATOR,
2560     j,
2561     priv->image_format);
2562     if (!g_file_test(filename, G_FILE_TEST_EXISTS))
2563 harbaum 32 {
2564     osm_gps_map_download_tile(map, zoom, i, j, FALSE);
2565     num_tiles++;
2566     }
2567     g_free(filename);
2568     }
2569     }
2570     g_debug("DL @Z:%d = %d tiles",zoom,num_tiles);
2571     }
2572     }
2573     }
2574    
2575     void
2576     osm_gps_map_get_bbox (OsmGpsMap *map, coord_t *pt1, coord_t *pt2)
2577     {
2578     OsmGpsMapPrivate *priv = map->priv;
2579    
2580     if (pt1 && pt2) {
2581     pt1->rlat = pixel2lat(priv->map_zoom, priv->map_y);
2582     pt1->rlon = pixel2lon(priv->map_zoom, priv->map_x);
2583     pt2->rlat = pixel2lat(priv->map_zoom, priv->map_y + GTK_WIDGET(map)->allocation.height);
2584     pt2->rlon = pixel2lon(priv->map_zoom, priv->map_x + GTK_WIDGET(map)->allocation.width);
2585    
2586     g_debug("BBOX: %f %f %f %f", pt1->rlat, pt1->rlon, pt2->rlat, pt2->rlon);
2587     }
2588     }
2589    
2590     void
2591     osm_gps_map_set_mapcenter (OsmGpsMap *map, float latitude, float longitude, int zoom)
2592     {
2593     osm_gps_map_set_center (map, latitude, longitude);
2594     osm_gps_map_set_zoom (map, zoom);
2595     }
2596    
2597     void
2598     osm_gps_map_set_center (OsmGpsMap *map, float latitude, float longitude)
2599     {
2600     int pixel_x, pixel_y;
2601     OsmGpsMapPrivate *priv;
2602    
2603     g_return_if_fail (OSM_IS_GPS_MAP (map));
2604     priv = map->priv;
2605    
2606     priv->center_rlat = deg2rad(latitude);
2607     priv->center_rlon = deg2rad(longitude);
2608     priv->center_coord_set = TRUE;
2609    
2610     // pixel_x,y, offsets
2611     pixel_x = lon2pixel(priv->map_zoom, priv->center_rlon);
2612     pixel_y = lat2pixel(priv->map_zoom, priv->center_rlat);
2613    
2614     priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
2615     priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
2616    
2617     osm_gps_map_map_redraw_idle(map);
2618     }
2619    
2620     int
2621     osm_gps_map_set_zoom (OsmGpsMap *map, int zoom)
2622     {
2623     int zoom_old;
2624 harbaum 55 double factor = 0.0;
2625 harbaum 32 int width_center, height_center;
2626     OsmGpsMapPrivate *priv;
2627    
2628     g_return_val_if_fail (OSM_IS_GPS_MAP (map), 0);
2629     priv = map->priv;
2630    
2631     if (zoom != priv->map_zoom)
2632     {
2633     width_center = GTK_WIDGET(map)->allocation.width / 2;
2634     height_center = GTK_WIDGET(map)->allocation.height / 2;
2635    
2636     zoom_old = priv->map_zoom;
2637     //constrain zoom min_zoom -> max_zoom
2638     priv->map_zoom = CLAMP(zoom, priv->min_zoom, priv->max_zoom);
2639    
2640     if (priv->center_coord_set)
2641     {
2642     priv->map_x = lon2pixel(priv->map_zoom, priv->center_rlon) - width_center;
2643     priv->map_y = lat2pixel(priv->map_zoom, priv->center_rlat) - height_center;
2644     }
2645     else
2646     {
2647     factor = exp(priv->map_zoom * M_LN2)/exp(zoom_old * M_LN2);
2648     priv->map_x = ((priv->map_x + width_center) * factor) - width_center;
2649     priv->map_y = ((priv->map_y + height_center) * factor) - height_center;
2650     }
2651    
2652     g_debug("Zoom changed from %d to %d factor:%f x:%d",
2653     zoom_old, priv->map_zoom, factor, priv->map_x);
2654    
2655     osm_gps_map_map_redraw_idle(map);
2656     }
2657     return priv->map_zoom;
2658     }
2659    
2660     void
2661     osm_gps_map_add_track (OsmGpsMap *map, GSList *track)
2662     {
2663     OsmGpsMapPrivate *priv;
2664    
2665     g_return_if_fail (OSM_IS_GPS_MAP (map));
2666     priv = map->priv;
2667    
2668     if (track) {
2669     priv->tracks = g_slist_append(priv->tracks, track);
2670     osm_gps_map_map_redraw_idle(map);
2671     }
2672     }
2673    
2674     void
2675     osm_gps_map_clear_tracks (OsmGpsMap *map)
2676     {
2677     g_return_if_fail (OSM_IS_GPS_MAP (map));
2678    
2679     osm_gps_map_free_tracks(map);
2680     osm_gps_map_map_redraw_idle(map);
2681     }
2682    
2683     void
2684     osm_gps_map_add_image (OsmGpsMap *map, float latitude, float longitude, GdkPixbuf *image)
2685     {
2686     g_return_if_fail (OSM_IS_GPS_MAP (map));
2687    
2688     if (image) {
2689     OsmGpsMapPrivate *priv = map->priv;
2690     image_t *im;
2691    
2692     //cache w/h for speed, and add image to list
2693     im = g_new0(image_t,1);
2694     im->w = gdk_pixbuf_get_width(image);
2695 harbaum 55 im->h = gdk_pixbuf_get_height(image);
2696 harbaum 32 im->pt.rlat = deg2rad(latitude);
2697     im->pt.rlon = deg2rad(longitude);
2698    
2699     g_object_ref(image);
2700     im->image = image;
2701    
2702     priv->images = g_slist_append(priv->images, im);
2703    
2704     osm_gps_map_map_redraw_idle(map);
2705     }
2706     }
2707    
2708 harbaum 55 gboolean
2709     osm_gps_map_remove_image (OsmGpsMap *map, GdkPixbuf *image)
2710     {
2711     OsmGpsMapPrivate *priv = map->priv;
2712     if (priv->images) {
2713     GSList *list;
2714     for(list = priv->images; list != NULL; list = list->next)
2715     {
2716     image_t *im = list->data;
2717     if (im->image == image)
2718     {
2719     priv->images = g_slist_remove_link(priv->images, list);
2720     g_object_unref(im->image);
2721     g_free(im);
2722     osm_gps_map_map_redraw_idle(map);
2723     return TRUE;
2724     }
2725     }
2726     }
2727     return FALSE;
2728     }
2729    
2730 harbaum 32 void
2731     osm_gps_map_clear_images (OsmGpsMap *map)
2732     {
2733     g_return_if_fail (OSM_IS_GPS_MAP (map));
2734    
2735     osm_gps_map_free_images(map);
2736     osm_gps_map_map_redraw_idle(map);
2737     }
2738    
2739     void
2740     osm_gps_map_osd_speed (OsmGpsMap *map, float speed)
2741     {
2742     OsmGpsMapPrivate *priv;
2743    
2744     PangoContext *context = NULL;
2745     PangoLayout *layout = NULL;
2746     PangoFontDescription *desc = NULL;
2747    
2748     GdkColor color;
2749     GdkGC *gc;
2750    
2751     gchar *buffer;
2752     //static int x = 10, y = 10;
2753     static int width = 0, height = 0;
2754    
2755     g_return_if_fail (OSM_IS_GPS_MAP (map));
2756     priv = map->priv;
2757    
2758     buffer = g_strdup_printf("%.0f", speed);
2759    
2760     /* pango initialisation */
2761     context = gtk_widget_get_pango_context (GTK_WIDGET(map));
2762     layout = pango_layout_new (context);
2763     desc = pango_font_description_new();
2764    
2765     pango_font_description_set_size (desc, 40 * PANGO_SCALE);
2766     pango_layout_set_font_description (layout, desc);
2767     pango_layout_set_text (layout, buffer, strlen(buffer));
2768    
2769     gc = gdk_gc_new (GTK_WIDGET(map)->window);
2770    
2771     color.red = (0 > 50) ? 0xffff : 0;
2772     color.green = 0;
2773     color.blue = 0;
2774    
2775     gdk_gc_set_rgb_fg_color (gc, &color);
2776    
2777     /* faster / less flicker alternative:*/
2778     gdk_draw_drawable (
2779     GTK_WIDGET(map)->window,
2780     GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(map)],
2781     priv->pixmap,
2782     0,0,
2783     0,0,
2784     width+10,width+10);
2785    
2786     gdk_draw_layout(GTK_WIDGET(map)->window,
2787     gc,
2788     0, 0,
2789     layout);
2790    
2791     /* set width and height */
2792     pango_layout_get_pixel_size(layout, &width, &height);
2793    
2794     g_free(buffer);
2795     pango_font_description_free (desc);
2796     g_object_unref (layout);
2797     g_object_unref (gc);
2798     }
2799    
2800     void
2801     osm_gps_map_draw_gps (OsmGpsMap *map, float latitude, float longitude, float heading)
2802     {
2803     int pixel_x, pixel_y;
2804     OsmGpsMapPrivate *priv;
2805    
2806     g_return_if_fail (OSM_IS_GPS_MAP (map));
2807     priv = map->priv;
2808    
2809     priv->gps->rlat = deg2rad(latitude);
2810     priv->gps->rlon = deg2rad(longitude);
2811     priv->gps_valid = TRUE;
2812    
2813     // pixel_x,y, offsets
2814     pixel_x = lon2pixel(priv->map_zoom, priv->gps->rlon);
2815     pixel_y = lat2pixel(priv->map_zoom, priv->gps->rlat);
2816    
2817     //If trip marker add to list of gps points.
2818     if (priv->record_trip_history) {
2819     coord_t *tp = g_new0(coord_t,1);
2820     tp->rlat = priv->gps->rlat;
2821     tp->rlon = priv->gps->rlon;
2822     priv->trip_history = g_slist_append(priv->trip_history, tp);
2823     }
2824    
2825     // dont draw anything if we are dragging
2826     if (priv->dragging) {
2827     g_debug("Dragging");
2828     return;
2829     }
2830    
2831     //Automatically center the map if the track approaches the edge
2832     if(priv->map_auto_center) {
2833     int x = pixel_x - priv->map_x;
2834     int y = pixel_y - priv->map_y;
2835     int width = GTK_WIDGET(map)->allocation.width;
2836     int height = GTK_WIDGET(map)->allocation.height;
2837     if( x < (width/2 - width/8) || x > (width/2 + width/8) ||
2838     y < (height/2 - height/8) || y > (height/2 + height/8)) {
2839    
2840     priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
2841     priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
2842     priv->center_coord_set = FALSE;
2843     }
2844     }
2845    
2846     // this redraws the map (including the gps track, and adjusts the
2847     // map center if it was changed
2848     osm_gps_map_map_redraw_idle(map);
2849     }
2850    
2851     void
2852     osm_gps_map_clear_gps (OsmGpsMap *map)
2853     {
2854     osm_gps_map_free_trip(map);
2855     osm_gps_map_map_redraw_idle(map);
2856     }
2857    
2858     coord_t
2859     osm_gps_map_get_co_ordinates (OsmGpsMap *map, int pixel_x, int pixel_y)
2860     {
2861     coord_t coord;
2862     OsmGpsMapPrivate *priv = map->priv;
2863    
2864     coord.rlat = pixel2lat(priv->map_zoom, priv->map_y + pixel_y);
2865     coord.rlon = pixel2lon(priv->map_zoom, priv->map_x + pixel_x);
2866     return coord;
2867     }
2868    
2869     GtkWidget *
2870     osm_gps_map_new (void)
2871     {
2872     return g_object_new (OSM_TYPE_GPS_MAP, NULL);
2873     }
2874    
2875     void
2876     osm_gps_map_screen_to_geographic (OsmGpsMap *map, gint pixel_x, gint pixel_y,
2877     gfloat *latitude, gfloat *longitude)
2878     {
2879     OsmGpsMapPrivate *priv;
2880    
2881     g_return_if_fail (OSM_IS_GPS_MAP (map));
2882     priv = map->priv;
2883    
2884     if (latitude)
2885     *latitude = rad2deg(pixel2lat(priv->map_zoom, priv->map_y + pixel_y));
2886     if (longitude)
2887     *longitude = rad2deg(pixel2lon(priv->map_zoom, priv->map_x + pixel_x));
2888     }
2889    
2890     void
2891     osm_gps_map_geographic_to_screen (OsmGpsMap *map,
2892     gfloat latitude, gfloat longitude,
2893     gint *pixel_x, gint *pixel_y)
2894     {
2895     OsmGpsMapPrivate *priv;
2896    
2897     g_return_if_fail (OSM_IS_GPS_MAP (map));
2898     priv = map->priv;
2899    
2900     if (pixel_x)
2901     *pixel_x = lon2pixel(priv->map_zoom, deg2rad(longitude)) - priv->map_x;
2902     if (pixel_y)
2903     *pixel_y = lat2pixel(priv->map_zoom, deg2rad(latitude)) - priv->map_y;
2904     }
2905    
2906     void
2907     osm_gps_map_scroll (OsmGpsMap *map, gint dx, gint dy)
2908     {
2909     OsmGpsMapPrivate *priv;
2910    
2911     g_return_if_fail (OSM_IS_GPS_MAP (map));
2912     priv = map->priv;
2913    
2914     priv->center_coord_set = FALSE;
2915     priv->map_x += dx;
2916     priv->map_y += dy;
2917    
2918     osm_gps_map_map_redraw_idle (map);
2919     }
2920    
2921 harbaum 55 float
2922     osm_gps_map_get_scale(OsmGpsMap *map)
2923     {
2924     OsmGpsMapPrivate *priv;
2925    
2926     g_return_val_if_fail (OSM_IS_GPS_MAP (map), OSM_NAN);
2927     priv = map->priv;
2928    
2929     return osm_gps_map_get_scale_at_point(priv->map_zoom, priv->center_rlat, priv->center_rlon);
2930     }
2931    
2932 harbaum 65 #ifdef ENABLE_BALLOON
2933 harbaum 57 void
2934 harbaum 58 osm_gps_map_draw_balloon (OsmGpsMap *map, float latitude, float longitude,
2935     OsmGpsMapBalloonCallback cb, gpointer data)
2936 harbaum 57 {
2937     OsmGpsMapPrivate *priv;
2938    
2939     /* remove and previously installed balloon */
2940     osm_gps_map_clear_balloon (map);
2941    
2942     g_return_if_fail (OSM_IS_GPS_MAP (map));
2943     priv = map->priv;
2944    
2945 harbaum 58 priv->balloon.coo->rlat = deg2rad(latitude);
2946     priv->balloon.coo->rlon = deg2rad(longitude);
2947     priv->balloon.valid = TRUE;
2948 harbaum 57
2949 harbaum 58 priv->balloon.cb = cb;
2950     priv->balloon.data = data;
2951    
2952 harbaum 57 // this redraws the map
2953     osm_gps_map_map_redraw_idle(map);
2954     }
2955    
2956     void
2957     osm_gps_map_clear_balloon (OsmGpsMap *map)
2958     {
2959     OsmGpsMapPrivate *priv;
2960    
2961     g_return_if_fail (OSM_IS_GPS_MAP (map));
2962     priv = map->priv;
2963    
2964 harbaum 58 priv->balloon.valid = FALSE;
2965 harbaum 57
2966     osm_gps_map_map_redraw_idle(map);
2967     }
2968 harbaum 65 #endif
2969 harbaum 66
2970     #ifdef ENABLE_OSD
2971 harbaum 73
2972     void
2973     osm_gps_map_redraw (OsmGpsMap *map)
2974     {
2975     osm_gps_map_map_redraw_idle(map);
2976     }
2977    
2978     osm_gps_map_osd_t *osm_gps_map_osd_get(OsmGpsMap *map) {
2979     g_return_val_if_fail (OSM_IS_GPS_MAP (map), NULL);
2980     return map->priv->osd;
2981     }
2982    
2983     void osm_gps_map_register_osd(OsmGpsMap *map, osm_gps_map_osd_t *osd) {
2984 harbaum 66 OsmGpsMapPrivate *priv;
2985    
2986     g_return_if_fail (OSM_IS_GPS_MAP (map));
2987 harbaum 73
2988 harbaum 66 priv = map->priv;
2989 harbaum 73 g_return_if_fail (!priv->osd);
2990 harbaum 66
2991 harbaum 73 priv->osd = osd;
2992 harbaum 66 }
2993 harbaum 73
2994 harbaum 66 #endif