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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 32 - (hide annotations)
Tue Jul 28 13:21:22 2009 UTC (14 years, 9 months ago) by harbaum
File MIME type: text/plain
File size: 76118 byte(s)
osm-gps-map integration
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     #include <glib/gprintf.h>
37     #include <libsoup/soup.h>
38    
39     #include "converter.h"
40     #include "osm-gps-map-types.h"
41     #include "osm-gps-map.h"
42    
43     #ifdef USE_CAIRO
44     #include <cairo.h>
45     #endif
46    
47     #define ENABLE_DEBUG 0
48    
49     #define EXTRA_BORDER (TILESIZE / 2)
50    
51     struct _OsmGpsMapPrivate
52     {
53     GHashTable *tile_queue;
54     GHashTable *missing_tiles;
55     GHashTable *tile_cache;
56    
57     int map_zoom;
58     int max_zoom;
59     int min_zoom;
60     gboolean map_auto_center;
61     gboolean map_auto_download;
62     int map_x;
63     int map_y;
64    
65     /* Latitude and longitude of the center of the map, in radians */
66     gfloat center_rlat;
67     gfloat center_rlon;
68    
69     guint max_tile_cache_size;
70     /* Incremented at each redraw */
71     guint redraw_cycle;
72     /* ID of the idle redraw operation */
73     guint idle_map_redraw;
74    
75     //how we download tiles
76     SoupSession *soup_session;
77     char *proxy_uri;
78    
79     //where downloaded tiles are cached
80     char *cache_dir;
81     gboolean cache_dir_is_full_path;
82    
83     //contains flags indicating the various special characters
84     //the uri string contains, that will be replaced when calculating
85     //the uri to download.
86     char *repo_uri;
87     int uri_format;
88     //flag indicating if the map source is located on the google
89     gboolean the_google;
90    
91     //gps tracking state
92     gboolean record_trip_history;
93     gboolean show_trip_history;
94     GSList *trip_history;
95     coord_t *gps;
96     gboolean gps_valid;
97    
98     //additional images or tracks added to the map
99     GSList *tracks;
100     GSList *images;
101    
102     //Used for storing the joined tiles
103     GdkPixmap *pixmap;
104     GdkGC *gc_map;
105    
106     //The tile painted when one cannot be found
107     //GdkPixbuf *missing_tile;
108    
109     //For tracking click and drag
110     int drag_counter;
111     int drag_mouse_dx;
112     int drag_mouse_dy;
113     int drag_start_mouse_x;
114     int drag_start_mouse_y;
115     int drag_start_map_x;
116     int drag_start_map_y;
117    
118     //for customizing the redering of the gps track
119     int ui_gps_track_width;
120     int ui_gps_point_inner_radius;
121     int ui_gps_point_outer_radius;
122    
123     guint is_disposed : 1;
124     guint dragging : 1;
125     guint center_coord_set : 1;
126     };
127    
128     #define OSM_GPS_MAP_PRIVATE(o) (OSM_GPS_MAP (o)->priv)
129    
130     typedef struct
131     {
132     GdkPixbuf *pixbuf;
133     /* We keep track of the number of the redraw cycle this tile was last used,
134     * so that osm_gps_map_purge_cache() can remove the older ones */
135     guint redraw_cycle;
136     } OsmCachedTile;
137    
138     enum
139     {
140     PROP_0,
141    
142     PROP_AUTO_CENTER,
143     PROP_RECORD_TRIP_HISTORY,
144     PROP_SHOW_TRIP_HISTORY,
145     PROP_AUTO_DOWNLOAD,
146     PROP_REPO_URI,
147     PROP_PROXY_URI,
148     PROP_TILE_CACHE_DIR,
149     PROP_TILE_CACHE_DIR_IS_FULL_PATH,
150     PROP_ZOOM,
151     PROP_MAX_ZOOM,
152     PROP_MIN_ZOOM,
153     PROP_LATITUDE,
154     PROP_LONGITUDE,
155     PROP_MAP_X,
156     PROP_MAP_Y,
157     PROP_TILES_QUEUED,
158     PROP_GPS_TRACK_WIDTH,
159     PROP_GPS_POINT_R1,
160     PROP_GPS_POINT_R2
161     };
162    
163     G_DEFINE_TYPE (OsmGpsMap, osm_gps_map, GTK_TYPE_DRAWING_AREA);
164    
165     /*
166     * Drawing function forward defintions
167     */
168     static gchar *replace_string(const gchar *src, const gchar *from, const gchar *to);
169     static void inspect_map_uri(OsmGpsMap *map);
170     static gchar *replace_map_uri(OsmGpsMap *map, const gchar *uri, int zoom, int x, int y);
171     static void osm_gps_map_print_images (OsmGpsMap *map);
172     static void osm_gps_map_draw_gps_point (OsmGpsMap *map);
173     static void osm_gps_map_blit_tile(OsmGpsMap *map, GdkPixbuf *pixbuf, int offset_x, int offset_y);
174     static void osm_gps_map_download_tile (OsmGpsMap *map, int zoom, int x, int y, gboolean redraw);
175     static void osm_gps_map_load_tile (OsmGpsMap *map, int zoom, int x, int y, int offset_x, int offset_y);
176     static void osm_gps_map_fill_tiles_pixel (OsmGpsMap *map);
177     static gboolean osm_gps_map_map_redraw (OsmGpsMap *map);
178     static void osm_gps_map_map_redraw_idle (OsmGpsMap *map);
179    
180     static void
181     cached_tile_free (OsmCachedTile *tile)
182     {
183     g_object_unref (tile->pixbuf);
184     g_slice_free (OsmCachedTile, tile);
185     }
186    
187     /*
188     * Description:
189     * Find and replace text within a string.
190     *
191     * Parameters:
192     * src (in) - pointer to source string
193     * from (in) - pointer to search text
194     * to (in) - pointer to replacement text
195     *
196     * Returns:
197     * Returns a pointer to dynamically-allocated memory containing string
198     * with occurences of the text pointed to by 'from' replaced by with the
199     * text pointed to by 'to'.
200     */
201     static gchar *
202     replace_string(const gchar *src, const gchar *from, const gchar *to)
203     {
204     size_t size = strlen(src) + 1;
205     size_t fromlen = strlen(from);
206     size_t tolen = strlen(to);
207    
208     /* Allocate the first chunk with enough for the original string. */
209     gchar *value = g_malloc(size);
210    
211    
212     /* We need to return 'value', so let's make a copy to mess around with. */
213     gchar *dst = value;
214    
215     if ( value != NULL )
216     {
217     for ( ;; )
218     {
219     /* Try to find the search text. */
220     const gchar *match = g_strstr_len(src, size, from);
221     if ( match != NULL )
222     {
223     gchar *temp;
224     /* Find out how many characters to copy up to the 'match'. */
225     size_t count = match - src;
226    
227    
228     /* Calculate the total size the string will be after the
229     * replacement is performed. */
230     size += tolen - fromlen;
231    
232     temp = g_realloc(value, size);
233     if ( temp == NULL )
234     {
235     g_free(value);
236     return NULL;
237     }
238    
239     /* we'll want to return 'value' eventually, so let's point it
240     * to the memory that we are now working with.
241     * And let's not forget to point to the right location in
242     * the destination as well. */
243     dst = temp + (dst - value);
244     value = temp;
245    
246     /*
247     * Copy from the source to the point where we matched. Then
248     * move the source pointer ahead by the amount we copied. And
249     * move the destination pointer ahead by the same amount.
250     */
251     g_memmove(dst, src, count);
252     src += count;
253     dst += count;
254    
255     /* Now copy in the replacement text 'to' at the position of
256     * the match. Adjust the source pointer by the text we replaced.
257     * Adjust the destination pointer by the amount of replacement
258     * text. */
259     g_memmove(dst, to, tolen);
260     src += fromlen;
261     dst += tolen;
262     }
263     else
264     {
265     /*
266     * Copy any remaining part of the string. This includes the null
267     * termination character.
268     */
269     strcpy(dst, src);
270     break;
271     }
272     }
273     }
274     return value;
275     }
276    
277     static void
278     map_convert_coords_to_quadtree_string(OsmGpsMap *map, gint x, gint y, gint zoomlevel,
279     gchar *buffer, const gchar initial,
280     const gchar *const quadrant)
281     {
282     gchar *ptr = buffer;
283     gint n;
284    
285     if (initial)
286     *ptr++ = initial;
287    
288     for(n = zoomlevel-1; n >= 0; n--)
289     {
290     gint xbit = (x >> n) & 1;
291     gint ybit = (y >> n) & 1;
292     *ptr++ = quadrant[xbit + 2 * ybit];
293     }
294    
295     *ptr++ = '\0';
296     }
297    
298    
299     static void
300     inspect_map_uri(OsmGpsMap *map)
301     {
302     OsmGpsMapPrivate *priv = map->priv;
303    
304     if (g_strrstr(priv->repo_uri, URI_MARKER_X))
305     priv->uri_format |= URI_HAS_X;
306    
307     if (g_strrstr(priv->repo_uri, URI_MARKER_Y))
308     priv->uri_format |= URI_HAS_Y;
309    
310     if (g_strrstr(priv->repo_uri, URI_MARKER_Z))
311     priv->uri_format |= URI_HAS_Z;
312    
313     if (g_strrstr(priv->repo_uri, URI_MARKER_S))
314     priv->uri_format |= URI_HAS_S;
315    
316     if (g_strrstr(priv->repo_uri, URI_MARKER_Q))
317     priv->uri_format |= URI_HAS_Q;
318    
319     if (g_strrstr(priv->repo_uri, URI_MARKER_Q0))
320     priv->uri_format |= URI_HAS_Q0;
321    
322     if (g_strrstr(priv->repo_uri, URI_MARKER_YS))
323     priv->uri_format |= URI_HAS_YS;
324    
325     if (g_strrstr(priv->repo_uri, URI_MARKER_R))
326     priv->uri_format |= URI_HAS_R;
327    
328     if (g_strrstr(priv->repo_uri, "google.com"))
329     priv->the_google = TRUE;
330    
331     g_debug("URI Format: 0x%X (google: %X)", priv->uri_format, priv->the_google);
332    
333     }
334    
335     static gchar *
336     replace_map_uri(OsmGpsMap *map, const gchar *uri, int zoom, int x, int y)
337     {
338     OsmGpsMapPrivate *priv = map->priv;
339     char *url;
340     unsigned int i;
341     char location[22];
342    
343     i = 1;
344     url = g_strdup(uri);
345     while (i < URI_FLAG_END)
346     {
347     char *s = NULL;
348     char *old;
349    
350     old = url;
351     switch(i & priv->uri_format)
352     {
353     case URI_HAS_X:
354     s = g_strdup_printf("%d", x);
355     url = replace_string(url, URI_MARKER_X, s);
356     //g_debug("FOUND " URI_MARKER_X);
357     break;
358     case URI_HAS_Y:
359     s = g_strdup_printf("%d", y);
360     url = replace_string(url, URI_MARKER_Y, s);
361     //g_debug("FOUND " URI_MARKER_Y);
362     break;
363     case URI_HAS_Z:
364     s = g_strdup_printf("%d", zoom);
365     url = replace_string(url, URI_MARKER_Z, s);
366     //g_debug("FOUND " URI_MARKER_Z);
367     break;
368     case URI_HAS_S:
369     s = g_strdup_printf("%d", priv->max_zoom-zoom);
370     url = replace_string(url, URI_MARKER_S, s);
371     //g_debug("FOUND " URI_MARKER_S);
372     break;
373     case URI_HAS_Q:
374     map_convert_coords_to_quadtree_string(map,x,y,zoom,location,'t',"qrts");
375     s = g_strdup_printf("%s", location);
376     url = replace_string(url, URI_MARKER_Q, s);
377     //g_debug("FOUND " URI_MARKER_Q);
378     break;
379     case URI_HAS_Q0:
380     map_convert_coords_to_quadtree_string(map,x,y,zoom,location,'\0', "0123");
381     s = g_strdup_printf("%s", location);
382     url = replace_string(url, URI_MARKER_Q0, s);
383     //g_debug("FOUND " URI_MARKER_Q0);
384     break;
385     case URI_HAS_YS:
386     // s = g_strdup_printf("%d", y);
387     // url = replace_string(url, URI_MARKER_YS, s);
388     g_warning("FOUND " URI_MARKER_YS " NOT IMPLEMENTED");
389     // retval = g_strdup_printf(repo->url,
390     // tilex,
391     // (1 << (MAX_ZOOM - zoom)) - tiley - 1,
392     // zoom - (MAX_ZOOM - 17));
393     break;
394     case URI_HAS_R:
395     s = g_strdup_printf("%d", g_random_int_range(0,4));
396     url = replace_string(url, URI_MARKER_R, s);
397     //g_debug("FOUND " URI_MARKER_R);
398     break;
399     default:
400     s = NULL;
401     break;
402     }
403    
404     if (s) {
405     g_free(s);
406     g_free(old);
407     }
408    
409     i = (i << 1);
410    
411     }
412    
413     return url;
414     }
415    
416     static void
417     my_log_handler (const gchar * log_domain, GLogLevelFlags log_level, const gchar * message, gpointer user_data)
418     {
419     if (!(log_level & G_LOG_LEVEL_DEBUG) || ENABLE_DEBUG)
420     g_log_default_handler (log_domain, log_level, message, user_data);
421     }
422    
423     /* clears the trip list and all resources */
424     static void
425     osm_gps_map_free_trip (OsmGpsMap *map)
426     {
427     OsmGpsMapPrivate *priv = map->priv;
428     if (priv->trip_history) {
429     g_slist_foreach(priv->trip_history, (GFunc) g_free, NULL);
430     g_slist_free(priv->trip_history);
431     priv->trip_history = NULL;
432     }
433     }
434    
435     /* clears the tracks and all resources */
436     static void
437     osm_gps_map_free_tracks (OsmGpsMap *map)
438     {
439     OsmGpsMapPrivate *priv = map->priv;
440     if (priv->tracks)
441     {
442     GSList* tmp = priv->tracks;
443     while (tmp != NULL)
444     {
445     g_slist_foreach(tmp->data, (GFunc) g_free, NULL);
446     g_slist_free(tmp->data);
447     tmp = g_slist_next(tmp);
448     }
449     g_slist_free(priv->tracks);
450     priv->tracks = NULL;
451     }
452     }
453    
454     /* free the poi image lists */
455     static void
456     osm_gps_map_free_images (OsmGpsMap *map)
457     {
458     OsmGpsMapPrivate *priv = map->priv;
459     if (priv->images) {
460     GSList *list;
461     for(list = priv->images; list != NULL; list = list->next)
462     {
463     image_t *im = list->data;
464     g_object_unref(im->image);
465     g_free(im);
466     }
467     g_slist_free(priv->images);
468     priv->images = NULL;
469     }
470     }
471    
472     static void
473     osm_gps_map_print_images (OsmGpsMap *map)
474     {
475     GSList *list;
476     int x,y,pixel_x,pixel_y;
477     int min_x = 0,min_y = 0,max_x = 0,max_y = 0;
478     int map_x0, map_y0;
479     OsmGpsMapPrivate *priv = map->priv;
480    
481     map_x0 = priv->map_x - EXTRA_BORDER;
482     map_y0 = priv->map_y - EXTRA_BORDER;
483     for(list = priv->images; list != NULL; list = list->next)
484     {
485     image_t *im = list->data;
486    
487     // pixel_x,y, offsets
488     pixel_x = lon2pixel(priv->map_zoom, im->pt.rlon);
489     pixel_y = lat2pixel(priv->map_zoom, im->pt.rlat);
490    
491     g_debug("Image %dx%d @: %f,%f (%d,%d)",
492     im->w, im->h,
493     im->pt.rlat, im->pt.rlon,
494     pixel_x, pixel_y);
495    
496     x = pixel_x - map_x0;
497     y = pixel_y - map_y0;
498    
499     gdk_draw_pixbuf (
500     priv->pixmap,
501     priv->gc_map,
502     im->image,
503     0,0,
504     x-(im->w/2),y-(im->h/2),
505     im->w,im->h,
506     GDK_RGB_DITHER_NONE, 0, 0);
507    
508     max_x = MAX(x+im->w,max_x);
509     min_x = MIN(x-im->w,min_x);
510     max_y = MAX(y+im->h,max_y);
511     min_y = MIN(y-im->h,min_y);
512     }
513    
514     gtk_widget_queue_draw_area (
515     GTK_WIDGET(map),
516     min_x + EXTRA_BORDER, min_y + EXTRA_BORDER,
517     max_x + EXTRA_BORDER, max_y + EXTRA_BORDER);
518    
519     }
520    
521     static void
522     osm_gps_map_draw_gps_point (OsmGpsMap *map)
523     {
524     OsmGpsMapPrivate *priv = map->priv;
525    
526     //incase we get called before we have got a gps point
527     if (priv->gps_valid) {
528     int map_x0, map_y0;
529     int x, y;
530     int r = priv->ui_gps_point_inner_radius;
531     int r2 = priv->ui_gps_point_outer_radius;
532     int mr = MAX(r,r2);
533    
534     map_x0 = priv->map_x - EXTRA_BORDER;
535     map_y0 = priv->map_y - EXTRA_BORDER;
536     x = lon2pixel(priv->map_zoom, priv->gps->rlon) - map_x0;
537     y = lat2pixel(priv->map_zoom, priv->gps->rlat) - map_y0;
538     #ifdef USE_CAIRO
539     cairo_t *cr;
540     cairo_pattern_t *pat;
541     #else
542     int lw = priv->ui_gps_track_width;
543     GdkColor color;
544     GdkGC *marker;
545     #endif
546    
547     #ifdef USE_CAIRO
548     cr = gdk_cairo_create(priv->pixmap);
549    
550     // draw transparent area
551     if (r2 > 0) {
552     cairo_set_line_width (cr, 1.5);
553     cairo_set_source_rgba (cr, 0.75, 0.75, 0.75, 0.4);
554     cairo_arc (cr, x, y, r2, 0, 2 * M_PI);
555     cairo_fill (cr);
556     // draw transparent area border
557     cairo_set_source_rgba (cr, 0.55, 0.55, 0.55, 0.4);
558     cairo_arc (cr, x, y, r2, 0, 2 * M_PI);
559     cairo_stroke(cr);
560     }
561    
562     // draw ball gradient
563     if (r > 0) {
564     pat = cairo_pattern_create_radial (x-(r/5), y-(r/5), (r/5), x, y, r);
565     cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, 1.0);
566     cairo_pattern_add_color_stop_rgba (pat, 1, 0, 0, 1, 1.0);
567     cairo_set_source (cr, pat);
568     cairo_arc (cr, x, y, r, 0, 2 * M_PI);
569     cairo_fill (cr);
570     cairo_pattern_destroy (pat);
571     // draw ball border
572     cairo_set_line_width (cr, 1.0);
573     cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
574     cairo_arc (cr, x, y, r, 0, 2 * M_PI);
575     cairo_stroke(cr);
576     }
577    
578     cairo_destroy(cr);
579     gtk_widget_queue_draw_area (GTK_WIDGET(map),
580     x-mr,
581     y-mr,
582     mr*2,
583     mr*2);
584     #else
585     marker = gdk_gc_new(priv->pixmap);
586     color.red = 5000;
587     color.green = 5000;
588     color.blue = 55000;
589     gdk_gc_set_rgb_fg_color(marker, &color);
590     gdk_gc_set_line_attributes(marker, lw, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
591    
592     if (r2 > 0) {
593     gdk_draw_arc (priv->pixmap,
594     marker,
595     FALSE, //filled
596     x-r2, y-r2, // x,y
597     r2*2,r2*2, // width, height
598     0, 360*64); // start-end angle 64th, from 3h, anti clockwise
599     }
600     if (r > 0) {
601     gdk_draw_arc (priv->pixmap,
602     marker,
603     TRUE, //filled
604     x-r, y-r, // x,y
605     r*2,r*2, // width, height
606     0, 360*64); // start-end angle 64th, from 3h, anti clockwise
607     }
608    
609     g_object_unref(marker);
610     gtk_widget_queue_draw_area (GTK_WIDGET(map),
611     x-(mr+lw),
612     y-(mr+lw),
613     (mr*2)+lw+lw,
614     (mr*2)+lw+lw);
615     #endif
616     }
617     }
618    
619     static void
620     osm_gps_map_blit_tile(OsmGpsMap *map, GdkPixbuf *pixbuf, int offset_x, int offset_y)
621     {
622     OsmGpsMapPrivate *priv = map->priv;
623    
624     g_debug("Queing redraw @ %d,%d (w:%d h:%d)", offset_x,offset_y,
625     TILESIZE,TILESIZE);
626    
627     /* draw pixbuf onto pixmap */
628     gdk_draw_pixbuf (priv->pixmap,
629     priv->gc_map,
630     pixbuf,
631     0,0,
632     offset_x,offset_y,
633     TILESIZE,TILESIZE,
634     GDK_RGB_DITHER_NONE, 0, 0);
635     }
636    
637     /* libsoup-2.2 and libsoup-2.4 use different ways to store the body data */
638     #ifdef LIBSOUP22
639     #define soup_message_headers_append(a,b,c) soup_message_add_header(a,b,c)
640     #define MSG_RESPONSE_BODY(a) ((a)->response.body)
641     #define MSG_RESPONSE_LEN(a) ((a)->response.length)
642     #define MSG_RESPONSE_LEN_FORMAT "%u"
643     #else
644     #define MSG_RESPONSE_BODY(a) ((a)->response_body->data)
645     #define MSG_RESPONSE_LEN(a) ((a)->response_body->length)
646     #define MSG_RESPONSE_LEN_FORMAT "%lld"
647     #endif
648    
649     #ifdef LIBSOUP22
650     static void
651     osm_gps_map_tile_download_complete (SoupMessage *msg, gpointer user_data)
652     #else
653     static void
654     osm_gps_map_tile_download_complete (SoupSession *session, SoupMessage *msg, gpointer user_data)
655     #endif
656     {
657     int fd;
658     tile_download_t *dl = (tile_download_t *)user_data;
659     OsmGpsMap *map = OSM_GPS_MAP(dl->map);
660     OsmGpsMapPrivate *priv = map->priv;
661     gboolean file_saved = FALSE;
662    
663     if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
664     {
665     /* save tile into cachedir if one has been specified */
666     if (priv->cache_dir)
667     {
668     if (g_mkdir_with_parents(dl->folder,0700) == 0)
669     {
670     fd = open(dl->filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
671     if (fd != -1)
672     {
673     write (fd, MSG_RESPONSE_BODY(msg), MSG_RESPONSE_LEN(msg));
674     file_saved = TRUE;
675    
676     g_debug("Wrote " MSG_RESPONSE_LEN_FORMAT " bytes to %s", MSG_RESPONSE_LEN(msg), dl->filename);
677     close (fd);
678     }
679     }
680     else
681     {
682     g_warning("Error creating tile download directory: %s", dl->folder);
683     }
684     }
685    
686     if (dl->redraw)
687     {
688     GdkPixbuf *pixbuf = NULL;
689    
690     if (priv->cache_dir)
691     {
692     if (file_saved)
693     {
694     pixbuf = gdk_pixbuf_new_from_file (dl->filename, NULL);
695     }
696     }
697     else
698     {
699     GdkPixbufLoader *loader;
700     char *extension = strrchr (dl->filename, '.');
701    
702     /* parse file directly from memory */
703     if (extension)
704     {
705     loader = gdk_pixbuf_loader_new_with_type (extension+1, NULL);
706     if (!gdk_pixbuf_loader_write (loader, (unsigned char*)MSG_RESPONSE_BODY(msg), MSG_RESPONSE_LEN(msg), NULL))
707     {
708     g_warning("Error: Decoding of image failed");
709     }
710     gdk_pixbuf_loader_close(loader, NULL);
711    
712     pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
713    
714     /* give up loader but keep the pixbuf */
715     g_object_ref(pixbuf);
716     g_object_unref(loader);
717     }
718     else
719     {
720     g_warning("Error: Unable to determine image file format");
721     }
722     }
723    
724     /* Store the tile into the cache */
725     if (G_LIKELY (pixbuf))
726     {
727     OsmCachedTile *tile = g_slice_new (OsmCachedTile);
728     tile->pixbuf = pixbuf;
729     tile->redraw_cycle = priv->redraw_cycle;
730     /* if the tile is already in the cache (it could be one
731     * rendered from another zoom level), it will be
732     * overwritten */
733     g_hash_table_insert (priv->tile_cache, dl->filename, tile);
734     /* NULL-ify dl->filename so that it won't be freed, as
735     * we are using it as a key in the hash table */
736     dl->filename = NULL;
737     }
738    
739     osm_gps_map_map_redraw_idle (map);
740     }
741     g_hash_table_remove(priv->tile_queue, dl->uri);
742    
743     g_free(dl->uri);
744     g_free(dl->folder);
745     g_free(dl->filename);
746     g_free(dl);
747     }
748     else
749     {
750     g_warning("Error downloading tile: %d - %s", msg->status_code, msg->reason_phrase);
751     if (msg->status_code == SOUP_STATUS_NOT_FOUND)
752     {
753     g_hash_table_insert(priv->missing_tiles, dl->uri, NULL);
754     g_hash_table_remove(priv->tile_queue, dl->uri);
755     }
756     else if (msg->status_code == SOUP_STATUS_CANCELLED)
757     {
758     ;//application exiting
759     }
760     else
761     {
762     #ifdef LIBSOUP22
763     soup_session_requeue_message(dl->session, msg);
764     #else
765     soup_session_requeue_message(session, msg);
766     #endif
767     return;
768     }
769     }
770    
771    
772     }
773    
774     static void
775     osm_gps_map_download_tile (OsmGpsMap *map, int zoom, int x, int y, gboolean redraw)
776     {
777     SoupMessage *msg;
778     OsmGpsMapPrivate *priv = map->priv;
779     tile_download_t *dl = g_new0(tile_download_t,1);
780    
781     //calculate the uri to download
782     dl->uri = replace_map_uri(map, priv->repo_uri, zoom, x, y);
783    
784     #ifdef LIBSOUP22
785     dl->session = priv->soup_session;
786     #endif
787    
788     //check the tile has not already been queued for download,
789     //or has been attempted, and its missing
790     if (g_hash_table_lookup_extended(priv->tile_queue, dl->uri, NULL, NULL) ||
791     g_hash_table_lookup_extended(priv->missing_tiles, dl->uri, NULL, NULL) )
792     {
793     g_debug("Tile already downloading (or missing)");
794     g_free(dl->uri);
795     g_free(dl);
796     } else {
797     dl->folder = g_strdup_printf("%s/%d/%d/",priv->cache_dir, zoom, x);
798     dl->filename = g_strdup_printf("%s/%d/%d/%d.png",priv->cache_dir, zoom, x, y);
799     dl->map = map;
800     dl->redraw = redraw;
801    
802     g_debug("Download tile: %d,%d z:%d\n\t%s --> %s", x, y, zoom, dl->uri, dl->filename);
803    
804     msg = soup_message_new (SOUP_METHOD_GET, dl->uri);
805     if (msg) {
806     if (priv->the_google) {
807     //Set maps.google.com as the referrer
808     g_debug("Setting Google Referrer");
809     soup_message_headers_append(msg->request_headers, "Referer", "http://maps.google.com/");
810     //For google satelite also set the appropriate cookie value
811     if (priv->uri_format & URI_HAS_Q) {
812     const char *cookie = g_getenv("GOOGLE_COOKIE");
813     if (cookie) {
814     g_debug("Adding Google Cookie");
815     soup_message_headers_append(msg->request_headers, "Cookie", cookie);
816    
817     }
818     }
819     }
820    
821     g_hash_table_insert (priv->tile_queue, dl->uri, msg);
822     soup_session_queue_message (priv->soup_session, msg, osm_gps_map_tile_download_complete, dl);
823     } else {
824     g_warning("Could not create soup message");
825     g_free(dl->uri);
826     g_free(dl->folder);
827     g_free(dl->filename);
828     g_free(dl);
829     }
830     }
831     }
832    
833     static GdkPixbuf *
834     osm_gps_map_load_cached_tile (OsmGpsMap *map, int zoom, int x, int y)
835     {
836     OsmGpsMapPrivate *priv = map->priv;
837     gchar *filename;
838     GdkPixbuf *pixbuf;
839     OsmCachedTile *tile;
840    
841     filename = g_strdup_printf("%s/%u/%u/%u.png",
842     priv->cache_dir,
843     zoom, x, y);
844    
845     tile = g_hash_table_lookup (priv->tile_cache, filename);
846     if (tile)
847     {
848     g_free (filename);
849     }
850     else if (priv->cache_dir)
851     {
852     pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
853     if (pixbuf)
854     {
855     tile = g_slice_new (OsmCachedTile);
856     tile->pixbuf = pixbuf;
857     g_hash_table_insert (priv->tile_cache, filename, tile);
858     }
859     }
860    
861     /* set/update the redraw_cycle timestamp on the tile */
862     if (tile)
863     {
864     tile->redraw_cycle = priv->redraw_cycle;
865     pixbuf = g_object_ref (tile->pixbuf);
866     } else
867     pixbuf = NULL;
868    
869     return pixbuf;
870     }
871    
872     static GdkPixbuf *
873     osm_gps_map_find_bigger_tile (OsmGpsMap *map, int zoom, int x, int y,
874     int *zoom_found)
875     {
876     GdkPixbuf *pixbuf;
877     int next_zoom, next_x, next_y;
878    
879     if (zoom == 0) return NULL;
880     next_zoom = zoom - 1;
881     next_x = x / 2;
882     next_y = y / 2;
883     pixbuf = osm_gps_map_load_cached_tile (map, next_zoom, next_x, next_y);
884     if (pixbuf)
885     *zoom_found = next_zoom;
886     else
887     pixbuf = osm_gps_map_find_bigger_tile (map, next_zoom, next_x, next_y,
888     zoom_found);
889     return pixbuf;
890     }
891    
892     static GdkPixbuf *
893     osm_gps_map_render_missing_tile_upscaled (OsmGpsMap *map, int zoom,
894     int x, int y)
895     {
896     GdkPixbuf *pixbuf, *big, *area;
897     int zoom_big, zoom_diff, area_size, area_x, area_y;
898     int modulo;
899    
900     big = osm_gps_map_find_bigger_tile (map, zoom, x, y, &zoom_big);
901     if (!big) return NULL;
902    
903     g_debug ("Found bigger tile (zoom = %d, wanted = %d)", zoom_big, zoom);
904    
905     /* get a Pixbuf for the area to magnify */
906     zoom_diff = zoom - zoom_big;
907     area_size = TILESIZE >> zoom_diff;
908     modulo = 1 << zoom_diff;
909     area_x = (x % modulo) * area_size;
910     area_y = (y % modulo) * area_size;
911     area = gdk_pixbuf_new_subpixbuf (big, area_x, area_y,
912     area_size, area_size);
913     g_object_unref (big);
914     pixbuf = gdk_pixbuf_scale_simple (area, TILESIZE, TILESIZE,
915     GDK_INTERP_NEAREST);
916     g_object_unref (area);
917     return pixbuf;
918     }
919    
920     static GdkPixbuf *
921     osm_gps_map_render_missing_tile (OsmGpsMap *map, int zoom, int x, int y)
922     {
923     /* maybe TODO: render from downscaled tiles, if the following fails */
924     return osm_gps_map_render_missing_tile_upscaled (map, zoom, x, y);
925     }
926    
927     static void
928     osm_gps_map_load_tile (OsmGpsMap *map, int zoom, int x, int y, int offset_x, int offset_y)
929     {
930     OsmGpsMapPrivate *priv = map->priv;
931     GdkPixbuf *pixbuf;
932    
933     g_debug("Load tile %d,%d (%d,%d) z:%d", x, y, offset_x, offset_y, zoom);
934    
935     /* try to get file from internal cache */
936     pixbuf = osm_gps_map_load_cached_tile(map, zoom, x, y);
937    
938     /* if a disk cache is being used, try to read the file from there */
939     if (priv->cache_dir && !pixbuf)
940     {
941     gchar *filename;
942     filename = g_strdup_printf("%s/%u/%u/%u.png",
943     priv->cache_dir,
944     zoom, x, y);
945    
946     pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
947     if (pixbuf)
948     {
949     g_debug("Found tile %s", filename);
950     }
951    
952     g_free(filename);
953     }
954    
955     if (G_LIKELY(pixbuf))
956     {
957     osm_gps_map_blit_tile(map, pixbuf, offset_x,offset_y);
958     g_object_unref (pixbuf);
959     }
960     else
961     {
962     if (priv->map_auto_download)
963     osm_gps_map_download_tile(map, zoom, x, y, TRUE);
964    
965     /* try to render the tile by scaling cached tiles from other zoom
966     * levels */
967     pixbuf = osm_gps_map_render_missing_tile (map, zoom, x, y);
968     if (pixbuf)
969     {
970     gdk_draw_pixbuf (priv->pixmap,
971     priv->gc_map,
972     pixbuf,
973     0,0,
974     offset_x,offset_y,
975     TILESIZE,TILESIZE,
976     GDK_RGB_DITHER_NONE, 0, 0);
977     g_object_unref (pixbuf);
978     }
979     else
980     {
981     //prevent some artifacts when drawing not yet loaded areas.
982     gdk_draw_rectangle (priv->pixmap,
983     GTK_WIDGET(map)->style->white_gc,
984     TRUE, offset_x, offset_y, TILESIZE, TILESIZE);
985     }
986     }
987     }
988    
989     static void
990     osm_gps_map_fill_tiles_pixel (OsmGpsMap *map)
991     {
992     OsmGpsMapPrivate *priv = map->priv;
993     int i,j, width, height, tile_x0, tile_y0, tiles_nx, tiles_ny;
994     int offset_xn = 0;
995     int offset_yn = 0;
996     int offset_x;
997     int offset_y;
998    
999     g_debug("Fill tiles: %d,%d z:%d", priv->map_x, priv->map_y, priv->map_zoom);
1000    
1001     offset_x = - priv->map_x % TILESIZE;
1002     offset_y = - priv->map_y % TILESIZE;
1003     if (offset_x > 0) offset_x -= TILESIZE;
1004     if (offset_y > 0) offset_y -= TILESIZE;
1005    
1006     offset_xn = offset_x + EXTRA_BORDER;
1007     offset_yn = offset_y + EXTRA_BORDER;
1008    
1009     width = GTK_WIDGET(map)->allocation.width;
1010     height = GTK_WIDGET(map)->allocation.height;
1011    
1012     tiles_nx = (width - offset_x) / TILESIZE + 1;
1013     tiles_ny = (height - offset_y) / TILESIZE + 1;
1014    
1015     tile_x0 = floor((float)priv->map_x / (float)TILESIZE);
1016     tile_y0 = floor((float)priv->map_y / (float)TILESIZE);
1017    
1018     //TODO: implement wrap around
1019     for (i=tile_x0; i<(tile_x0+tiles_nx);i++)
1020     {
1021     for (j=tile_y0; j<(tile_y0+tiles_ny); j++)
1022     {
1023     if( j<0 || i<0 || i>=exp(priv->map_zoom * M_LN2) || j>=exp(priv->map_zoom * M_LN2))
1024     {
1025     gdk_draw_rectangle (priv->pixmap,
1026     GTK_WIDGET(map)->style->white_gc,
1027     TRUE,
1028     offset_xn, offset_yn,
1029     TILESIZE,TILESIZE);
1030     }
1031     else
1032     {
1033     osm_gps_map_load_tile(map,
1034     priv->map_zoom,
1035     i,j,
1036     offset_xn,offset_yn);
1037     }
1038     offset_yn += TILESIZE;
1039     }
1040     offset_xn += TILESIZE;
1041     offset_yn = offset_y + EXTRA_BORDER;
1042     }
1043     }
1044    
1045     static void
1046     osm_gps_map_print_track (OsmGpsMap *map, GSList *trackpoint_list)
1047     {
1048     OsmGpsMapPrivate *priv = map->priv;
1049    
1050     GSList *list;
1051     int x,y;
1052     int min_x = 0,min_y = 0,max_x = 0,max_y = 0;
1053     int lw = priv->ui_gps_track_width;
1054     int map_x0, map_y0;
1055     #ifdef USE_CAIRO
1056     cairo_t *cr;
1057     #else
1058     int last_x = 0, last_y = 0;
1059     GdkColor color;
1060     GdkGC *gc;
1061     #endif
1062    
1063     #ifdef USE_CAIRO
1064     cr = gdk_cairo_create(priv->pixmap);
1065     cairo_set_line_width (cr, lw);
1066     cairo_set_source_rgba (cr, 60000.0/65535.0, 0.0, 0.0, 0.6);
1067     cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1068     cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
1069     #else
1070     gc = gdk_gc_new(priv->pixmap);
1071     color.green = 0;
1072     color.blue = 0;
1073     color.red = 60000;
1074     gdk_gc_set_rgb_fg_color(gc, &color);
1075     gdk_gc_set_line_attributes(gc, lw, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
1076     #endif
1077    
1078     map_x0 = priv->map_x - EXTRA_BORDER;
1079     map_y0 = priv->map_y - EXTRA_BORDER;
1080    
1081     for(list = trackpoint_list; list != NULL; list = list->next)
1082     {
1083     coord_t *tp = list->data;
1084    
1085     x = lon2pixel(priv->map_zoom, tp->rlon) - map_x0;
1086     y = lat2pixel(priv->map_zoom, tp->rlat) - map_y0;
1087    
1088     // first time through loop
1089     if (list == trackpoint_list) {
1090     #ifdef USE_CAIRO
1091     cairo_move_to(cr, x, y);
1092     #else
1093     last_x = x;
1094     last_y = y;
1095     #endif
1096     }
1097    
1098     #ifdef USE_CAIRO
1099     cairo_line_to(cr, x, y);
1100     #else
1101     gdk_draw_line (priv->pixmap, gc, x, y, last_x, last_y);
1102     last_x = x;
1103     last_y = y;
1104     #endif
1105    
1106     max_x = MAX(x,max_x);
1107     min_x = MIN(x,min_x);
1108     max_y = MAX(y,max_y);
1109     min_y = MIN(y,min_y);
1110     }
1111    
1112     gtk_widget_queue_draw_area (
1113     GTK_WIDGET(map),
1114     min_x - lw,
1115     min_y - lw,
1116     max_x + (lw * 2),
1117     max_y + (lw * 2));
1118    
1119     #ifdef USE_CAIRO
1120     cairo_stroke(cr);
1121     cairo_destroy(cr);
1122     #else
1123     g_object_unref(gc);
1124     #endif
1125     }
1126    
1127     /* Prints the gps trip history, and any other tracks */
1128     static void
1129     osm_gps_map_print_tracks (OsmGpsMap *map)
1130     {
1131     OsmGpsMapPrivate *priv = map->priv;
1132    
1133     if (priv->show_trip_history)
1134     osm_gps_map_print_track (map, priv->trip_history);
1135     if (priv->tracks)
1136     {
1137     g_debug("TRACK");
1138     GSList* tmp = priv->tracks;
1139     while (tmp != NULL)
1140     {
1141     osm_gps_map_print_track (map, tmp->data);
1142     tmp = g_slist_next(tmp);
1143     }
1144     }
1145     }
1146    
1147     static gboolean
1148     osm_gps_map_purge_cache_check(gpointer key, gpointer value, gpointer user)
1149     {
1150     return(((OsmCachedTile*)value)->redraw_cycle != ((OsmGpsMapPrivate*)user)->redraw_cycle);
1151     }
1152    
1153     static void
1154     osm_gps_map_purge_cache (OsmGpsMap *map)
1155     {
1156     OsmGpsMapPrivate *priv = map->priv;
1157    
1158     if (g_hash_table_size (priv->tile_cache) < priv->max_tile_cache_size)
1159     return;
1160    
1161     /* run through the cache, and remove the tiles which have not been used
1162     * during the last redraw operation */
1163     g_hash_table_foreach_remove(priv->tile_cache, osm_gps_map_purge_cache_check, priv);
1164     }
1165    
1166     static gboolean
1167     osm_gps_map_map_redraw (OsmGpsMap *map)
1168     {
1169     OsmGpsMapPrivate *priv = map->priv;
1170    
1171     priv->idle_map_redraw = 0;
1172    
1173     /* the motion_notify handler uses priv->pixmap to redraw the area; if we
1174     * change it while we are dragging, we will end up showing it in the wrong
1175     * place. This could be fixed by carefully recompute the coordinates, but
1176     * for now it's easier just to disable redrawing the map while dragging */
1177     if (priv->dragging)
1178     return FALSE;
1179    
1180     priv->redraw_cycle++;
1181    
1182     /* draw white background to initialise pixmap */
1183     gdk_draw_rectangle (
1184     priv->pixmap,
1185     GTK_WIDGET(map)->style->white_gc,
1186     TRUE,
1187     0, 0,
1188     GTK_WIDGET(map)->allocation.width + EXTRA_BORDER * 2,
1189     GTK_WIDGET(map)->allocation.height + EXTRA_BORDER * 2);
1190    
1191     osm_gps_map_fill_tiles_pixel(map);
1192    
1193     osm_gps_map_print_tracks(map);
1194     osm_gps_map_draw_gps_point(map);
1195     osm_gps_map_print_images(map);
1196    
1197     //osm_gps_map_osd_speed(map, 1.5);
1198     osm_gps_map_purge_cache(map);
1199     gtk_widget_queue_draw (GTK_WIDGET (map));
1200    
1201     return FALSE;
1202     }
1203    
1204     static void
1205     osm_gps_map_map_redraw_idle (OsmGpsMap *map)
1206     {
1207     OsmGpsMapPrivate *priv = map->priv;
1208    
1209     if (priv->idle_map_redraw == 0)
1210     priv->idle_map_redraw = g_idle_add ((GSourceFunc)osm_gps_map_map_redraw, map);
1211     }
1212    
1213     static void
1214     osm_gps_map_init (OsmGpsMap *object)
1215     {
1216     OsmGpsMapPrivate *priv;
1217    
1218     priv = G_TYPE_INSTANCE_GET_PRIVATE (object, OSM_TYPE_GPS_MAP, OsmGpsMapPrivate);
1219     object->priv = priv;
1220    
1221     priv->pixmap = NULL;
1222    
1223     priv->trip_history = NULL;
1224     priv->gps = g_new0(coord_t, 1);
1225     priv->gps_valid = FALSE;
1226    
1227     priv->tracks = NULL;
1228     priv->images = NULL;
1229    
1230     priv->drag_counter = 0;
1231     priv->drag_mouse_dx = 0;
1232     priv->drag_mouse_dy = 0;
1233     priv->drag_start_mouse_x = 0;
1234     priv->drag_start_mouse_y = 0;
1235    
1236     priv->uri_format = 0;
1237     priv->the_google = FALSE;
1238    
1239     #ifndef LIBSOUP22
1240     //Change naumber of concurrent connections option?
1241     priv->soup_session = soup_session_async_new_with_options(
1242     SOUP_SESSION_USER_AGENT,
1243     "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11",
1244     NULL);
1245     #else
1246     /* libsoup-2.2 seems not to be able to set the user agent */
1247     priv->soup_session = soup_session_async_new();
1248     #endif
1249    
1250     //Hash table which maps tile d/l URIs to SoupMessage requests
1251     priv->tile_queue = g_hash_table_new (g_str_hash, g_str_equal);
1252    
1253     //Some mapping providers (Google) have varying degrees of tiles at multiple
1254     //zoom levels
1255     priv->missing_tiles = g_hash_table_new (g_str_hash, g_str_equal);
1256    
1257     /* memory cache for most recently used tiles */
1258     priv->tile_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
1259     g_free, (GDestroyNotify)cached_tile_free);
1260     priv->max_tile_cache_size = 20;
1261    
1262     gtk_widget_add_events (GTK_WIDGET (object),
1263     GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1264     GDK_POINTER_MOTION_MASK |
1265     GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
1266     GTK_WIDGET_SET_FLAGS (object, GTK_CAN_FOCUS);
1267    
1268     g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, my_log_handler, NULL);
1269     }
1270    
1271     #ifndef G_CHECKSUM_MD5
1272     /* simple hash algorithm hack if md5 is not present */
1273     static char *simple_hash(char *str) {
1274     union {
1275     char str[4];
1276     gulong val;
1277     } hash = { .val = 0x55555555 };
1278    
1279     while(*str) {
1280     hash.str[(int)str & 3] ^= *str;
1281     str++;
1282     }
1283     return g_strdup_printf("%08lX", hash.val);
1284     }
1285     #endif
1286    
1287     static GObject *
1288     osm_gps_map_constructor (GType gtype, guint n_properties, GObjectConstructParam *properties)
1289     {
1290     GObject *object;
1291     OsmGpsMapPrivate *priv;
1292    
1293     //Always chain up to the parent constructor
1294     object = G_OBJECT_CLASS(osm_gps_map_parent_class)->constructor(gtype, n_properties, properties);
1295     priv = OSM_GPS_MAP_PRIVATE(object);
1296    
1297     if (!priv->cache_dir_is_full_path) {
1298     #ifdef G_CHECKSUM_MD5
1299     char *md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, priv->repo_uri, -1);
1300     #else
1301     char *md5 = simple_hash(priv->repo_uri);
1302     #endif
1303    
1304     if (priv->cache_dir) {
1305     char *old = priv->cache_dir;
1306     //the new cachedir is the given cache dir + the md5 of the repo_uri
1307     priv->cache_dir = g_strdup_printf("%s/%s", old, md5);
1308     g_debug("Adjusting cache dir %s -> %s", old, priv->cache_dir);
1309     g_free(old);
1310     }
1311    
1312     g_free(md5);
1313     }
1314    
1315     return object;
1316     }
1317    
1318     static void
1319     osm_gps_map_dispose (GObject *object)
1320     {
1321     OsmGpsMap *map = OSM_GPS_MAP(object);
1322     OsmGpsMapPrivate *priv = map->priv;
1323    
1324     if (priv->is_disposed)
1325     return;
1326    
1327     priv->is_disposed = TRUE;
1328    
1329     soup_session_abort(priv->soup_session);
1330     g_object_unref(priv->soup_session);
1331    
1332     g_hash_table_destroy(priv->tile_queue);
1333     g_hash_table_destroy(priv->missing_tiles);
1334     g_hash_table_destroy(priv->tile_cache);
1335    
1336     osm_gps_map_free_images(map);
1337    
1338     if(priv->pixmap)
1339     g_object_unref (priv->pixmap);
1340    
1341     if(priv->gc_map)
1342     g_object_unref(priv->gc_map);
1343    
1344     if (priv->idle_map_redraw != 0)
1345     g_source_remove (priv->idle_map_redraw);
1346    
1347     G_OBJECT_CLASS (osm_gps_map_parent_class)->dispose (object);
1348     }
1349    
1350     static void
1351     osm_gps_map_finalize (GObject *object)
1352     {
1353     OsmGpsMap *map = OSM_GPS_MAP(object);
1354     OsmGpsMapPrivate *priv = map->priv;
1355    
1356     g_free(priv->cache_dir);
1357     g_free(priv->repo_uri);
1358    
1359     osm_gps_map_free_trip(map);
1360     osm_gps_map_free_tracks(map);
1361    
1362     G_OBJECT_CLASS (osm_gps_map_parent_class)->finalize (object);
1363     }
1364    
1365     static void
1366     osm_gps_map_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1367     {
1368     g_return_if_fail (OSM_IS_GPS_MAP (object));
1369     OsmGpsMap *map = OSM_GPS_MAP(object);
1370     OsmGpsMapPrivate *priv = map->priv;
1371    
1372     switch (prop_id)
1373     {
1374     case PROP_AUTO_CENTER:
1375     priv->map_auto_center = g_value_get_boolean (value);
1376     break;
1377     case PROP_RECORD_TRIP_HISTORY:
1378     priv->record_trip_history = g_value_get_boolean (value);
1379     break;
1380     case PROP_SHOW_TRIP_HISTORY:
1381     priv->show_trip_history = g_value_get_boolean (value);
1382     break;
1383     case PROP_AUTO_DOWNLOAD:
1384     priv->map_auto_download = g_value_get_boolean (value);
1385     break;
1386     case PROP_REPO_URI:
1387     priv->repo_uri = g_value_dup_string (value);
1388     inspect_map_uri(map);
1389     break;
1390     case PROP_PROXY_URI:
1391     if ( g_value_get_string(value) ) {
1392    
1393     priv->proxy_uri = g_value_dup_string (value);
1394     g_debug("Setting proxy server: %s", priv->proxy_uri);
1395     #ifndef LIBSOUP22
1396     GValue val = {0};
1397     SoupURI* uri = soup_uri_new(priv->proxy_uri);
1398     g_value_init(&val, SOUP_TYPE_URI);
1399     g_value_take_boxed(&val, uri);
1400    
1401     g_object_set_property(G_OBJECT(priv->soup_session),SOUP_SESSION_PROXY_URI,&val);
1402     #else
1403     SoupUri* uri = soup_uri_new(priv->proxy_uri);
1404     g_object_set(G_OBJECT(priv->soup_session), SOUP_SESSION_PROXY_URI, uri, NULL);
1405     #endif
1406     soup_uri_free(uri);
1407     } else
1408     priv->proxy_uri = NULL;
1409    
1410     break;
1411     case PROP_TILE_CACHE_DIR:
1412     priv->cache_dir = g_value_dup_string (value);
1413     break;
1414     case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
1415     priv->cache_dir_is_full_path = g_value_get_boolean (value);
1416     break;
1417     case PROP_ZOOM:
1418     priv->map_zoom = g_value_get_int (value);
1419     break;
1420     case PROP_MAX_ZOOM:
1421     priv->max_zoom = g_value_get_int (value);
1422     break;
1423     case PROP_MIN_ZOOM:
1424     priv->min_zoom = g_value_get_int (value);
1425     break;
1426     case PROP_MAP_X:
1427     priv->map_x = g_value_get_int (value);
1428     priv->center_coord_set = FALSE;
1429     break;
1430     case PROP_MAP_Y:
1431     priv->map_y = g_value_get_int (value);
1432     priv->center_coord_set = FALSE;
1433     break;
1434     case PROP_GPS_TRACK_WIDTH:
1435     priv->ui_gps_track_width = g_value_get_int (value);
1436     break;
1437     case PROP_GPS_POINT_R1:
1438     priv->ui_gps_point_inner_radius = g_value_get_int (value);
1439     break;
1440     case PROP_GPS_POINT_R2:
1441     priv->ui_gps_point_outer_radius = g_value_get_int (value);
1442     break;
1443     default:
1444     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1445     break;
1446     }
1447     }
1448    
1449     static void
1450     osm_gps_map_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
1451     {
1452     g_return_if_fail (OSM_IS_GPS_MAP (object));
1453     float lat,lon;
1454     OsmGpsMap *map = OSM_GPS_MAP(object);
1455     OsmGpsMapPrivate *priv = map->priv;
1456    
1457     switch (prop_id)
1458     {
1459     case PROP_AUTO_CENTER:
1460     g_value_set_boolean(value, priv->map_auto_center);
1461     break;
1462     case PROP_RECORD_TRIP_HISTORY:
1463     g_value_set_boolean(value, priv->record_trip_history);
1464     break;
1465     case PROP_SHOW_TRIP_HISTORY:
1466     g_value_set_boolean(value, priv->show_trip_history);
1467     break;
1468     case PROP_AUTO_DOWNLOAD:
1469     g_value_set_boolean(value, priv->map_auto_download);
1470     break;
1471     case PROP_REPO_URI:
1472     g_value_set_string(value, priv->repo_uri);
1473     break;
1474     case PROP_PROXY_URI:
1475     g_value_set_string(value, priv->proxy_uri);
1476     break;
1477     case PROP_TILE_CACHE_DIR:
1478     g_value_set_string(value, priv->cache_dir);
1479     break;
1480     case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
1481     g_value_set_boolean(value, priv->cache_dir_is_full_path);
1482     break;
1483     case PROP_ZOOM:
1484     g_value_set_int(value, priv->map_zoom);
1485     break;
1486     case PROP_MAX_ZOOM:
1487     g_value_set_int(value, priv->max_zoom);
1488     break;
1489     case PROP_MIN_ZOOM:
1490     g_value_set_int(value, priv->min_zoom);
1491     break;
1492     case PROP_LATITUDE:
1493     lat = pixel2lat(priv->map_zoom,
1494     priv->map_y + (GTK_WIDGET(map)->allocation.height / 2));
1495     g_value_set_float(value, rad2deg(lat));
1496     break;
1497     case PROP_LONGITUDE:
1498     lon = pixel2lon(priv->map_zoom,
1499     priv->map_x + (GTK_WIDGET(map)->allocation.width / 2));
1500     g_value_set_float(value, rad2deg(lon));
1501     break;
1502     case PROP_MAP_X:
1503     g_value_set_int(value, priv->map_x);
1504     break;
1505     case PROP_MAP_Y:
1506     g_value_set_int(value, priv->map_y);
1507     break;
1508     case PROP_TILES_QUEUED:
1509     g_value_set_int(value, g_hash_table_size(priv->tile_queue));
1510     break;
1511     case PROP_GPS_TRACK_WIDTH:
1512     g_value_set_int(value, priv->ui_gps_track_width);
1513     break;
1514     case PROP_GPS_POINT_R1:
1515     g_value_set_int(value, priv->ui_gps_point_inner_radius);
1516     break;
1517     case PROP_GPS_POINT_R2:
1518     g_value_set_int(value, priv->ui_gps_point_outer_radius);
1519     break;
1520     default:
1521     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1522     break;
1523     }
1524     }
1525    
1526     static gboolean
1527     _osm_gps_map_scroll (GtkWidget *widget, GdkEventScroll *event)
1528     {
1529     OsmGpsMap *map = OSM_GPS_MAP(widget);
1530     OsmGpsMapPrivate *priv = map->priv;
1531    
1532     if (event->direction == GDK_SCROLL_UP)
1533     {
1534     osm_gps_map_set_zoom(map, priv->map_zoom+1);
1535     }
1536     else
1537     {
1538     osm_gps_map_set_zoom(map, priv->map_zoom-1);
1539     }
1540    
1541     return FALSE;
1542     }
1543    
1544     static gboolean
1545     osm_gps_map_button_press (GtkWidget *widget, GdkEventButton *event)
1546     {
1547     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1548    
1549     priv->drag_counter = 0;
1550     priv->drag_start_mouse_x = (int) event->x;
1551     priv->drag_start_mouse_y = (int) event->y;
1552     priv->drag_start_map_x = priv->map_x;
1553     priv->drag_start_map_y = priv->map_y;
1554    
1555     return FALSE;
1556     }
1557    
1558     static gboolean
1559     osm_gps_map_button_release (GtkWidget *widget, GdkEventButton *event)
1560     {
1561     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1562    
1563     if (priv->dragging)
1564     {
1565     priv->dragging = FALSE;
1566    
1567     priv->map_x = priv->drag_start_map_x;
1568     priv->map_y = priv->drag_start_map_y;
1569    
1570     priv->map_x += (priv->drag_start_mouse_x - (int) event->x);
1571     priv->map_y += (priv->drag_start_mouse_y - (int) event->y);
1572    
1573     priv->center_coord_set = FALSE;
1574    
1575     osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
1576     }
1577    
1578     priv->drag_mouse_dx = 0;
1579     priv->drag_mouse_dy = 0;
1580     priv->drag_counter = 0;
1581    
1582     return FALSE;
1583     }
1584    
1585     static gboolean
1586     osm_gps_map_motion_notify (GtkWidget *widget, GdkEventMotion *event)
1587     {
1588     int x, y;
1589     GdkModifierType state;
1590     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1591    
1592     if (event->is_hint)
1593     gdk_window_get_pointer (event->window, &x, &y, &state);
1594     else
1595     {
1596     x = event->x;
1597     y = event->y;
1598     state = event->state;
1599     }
1600    
1601     // are we being dragged
1602     if (!(state & GDK_BUTTON1_MASK))
1603     return FALSE;
1604    
1605     priv->drag_counter++;
1606    
1607     // we havent dragged more than 6 pixels
1608     if (priv->drag_counter < 6)
1609     return FALSE;
1610    
1611     priv->dragging = TRUE;
1612    
1613     if (priv->map_auto_center)
1614     g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
1615    
1616     priv->drag_mouse_dx = x - priv->drag_start_mouse_x;
1617     priv->drag_mouse_dy = y - priv->drag_start_mouse_y;
1618    
1619     gdk_draw_drawable (
1620     widget->window,
1621     widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1622     priv->pixmap,
1623     0,0,
1624     priv->drag_mouse_dx - EXTRA_BORDER, priv->drag_mouse_dy - EXTRA_BORDER,
1625     -1,-1);
1626    
1627     //Paint white outside of the map if dragging. Its less
1628     //ugly than painting the corrupted map
1629     if(priv->drag_mouse_dx>EXTRA_BORDER) {
1630     gdk_draw_rectangle (
1631     widget->window,
1632     widget->style->white_gc,
1633     TRUE,
1634     0, 0,
1635     priv->drag_mouse_dx - EXTRA_BORDER,
1636     widget->allocation.height);
1637     }
1638     else if (-priv->drag_mouse_dx > EXTRA_BORDER)
1639     {
1640     gdk_draw_rectangle (
1641     widget->window,
1642     widget->style->white_gc,
1643     TRUE,
1644     priv->drag_mouse_dx + widget->allocation.width + EXTRA_BORDER, 0,
1645     -priv->drag_mouse_dx - EXTRA_BORDER,
1646     widget->allocation.height);
1647     }
1648    
1649     if (priv->drag_mouse_dy>EXTRA_BORDER) {
1650     gdk_draw_rectangle (
1651     widget->window,
1652     widget->style->white_gc,
1653     TRUE,
1654     0, 0,
1655     widget->allocation.width,
1656     priv->drag_mouse_dy - EXTRA_BORDER);
1657     }
1658     else if (-priv->drag_mouse_dy > EXTRA_BORDER)
1659     {
1660     gdk_draw_rectangle (
1661     widget->window,
1662     widget->style->white_gc,
1663     TRUE,
1664     0, priv->drag_mouse_dy + widget->allocation.height + EXTRA_BORDER,
1665     widget->allocation.width,
1666     -priv->drag_mouse_dy - EXTRA_BORDER);
1667     }
1668    
1669     return FALSE;
1670     }
1671    
1672     static gboolean
1673     osm_gps_map_configure (GtkWidget *widget, GdkEventConfigure *event)
1674     {
1675     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1676    
1677     /* create pixmap */
1678     if (priv->pixmap)
1679     g_object_unref (priv->pixmap);
1680    
1681     priv->pixmap = gdk_pixmap_new (
1682     widget->window,
1683     widget->allocation.width + EXTRA_BORDER * 2,
1684     widget->allocation.height + EXTRA_BORDER * 2,
1685     -1);
1686    
1687     /* and gc, used for clipping (I think......) */
1688     if(priv->gc_map)
1689     g_object_unref(priv->gc_map);
1690    
1691     priv->gc_map = gdk_gc_new(priv->pixmap);
1692    
1693     osm_gps_map_map_redraw(OSM_GPS_MAP(widget));
1694    
1695     return FALSE;
1696     }
1697    
1698     static gboolean
1699     osm_gps_map_expose (GtkWidget *widget, GdkEventExpose *event)
1700     {
1701     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1702    
1703     gdk_draw_drawable (
1704     widget->window,
1705     widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1706     priv->pixmap,
1707     event->area.x + EXTRA_BORDER, event->area.y + EXTRA_BORDER,
1708     event->area.x, event->area.y,
1709     event->area.width, event->area.height);
1710    
1711     return FALSE;
1712     }
1713    
1714     static void
1715     osm_gps_map_class_init (OsmGpsMapClass *klass)
1716     {
1717     GObjectClass* object_class = G_OBJECT_CLASS (klass);
1718     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1719    
1720     g_type_class_add_private (klass, sizeof (OsmGpsMapPrivate));
1721    
1722     object_class->dispose = osm_gps_map_dispose;
1723     object_class->finalize = osm_gps_map_finalize;
1724     object_class->constructor = osm_gps_map_constructor;
1725     object_class->set_property = osm_gps_map_set_property;
1726     object_class->get_property = osm_gps_map_get_property;
1727    
1728     widget_class->expose_event = osm_gps_map_expose;
1729     widget_class->configure_event = osm_gps_map_configure;
1730     widget_class->button_press_event = osm_gps_map_button_press;
1731     widget_class->button_release_event = osm_gps_map_button_release;
1732     widget_class->motion_notify_event = osm_gps_map_motion_notify;
1733     widget_class->scroll_event = _osm_gps_map_scroll;
1734    
1735     g_object_class_install_property (object_class,
1736     PROP_AUTO_CENTER,
1737     g_param_spec_boolean ("auto-center",
1738     "auto center",
1739     "map auto center",
1740     TRUE,
1741     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1742    
1743     g_object_class_install_property (object_class,
1744     PROP_RECORD_TRIP_HISTORY,
1745     g_param_spec_boolean ("record-trip-history",
1746     "record trip history",
1747     "should all gps points be recorded in a trip history",
1748     TRUE,
1749     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1750    
1751     g_object_class_install_property (object_class,
1752     PROP_SHOW_TRIP_HISTORY,
1753     g_param_spec_boolean ("show-trip-history",
1754     "show trip history",
1755     "should the recorded trip history be shown on the map",
1756     TRUE,
1757     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1758    
1759     g_object_class_install_property (object_class,
1760     PROP_AUTO_DOWNLOAD,
1761     g_param_spec_boolean ("auto-download",
1762     "auto download",
1763     "map auto download",
1764     TRUE,
1765     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1766    
1767     g_object_class_install_property (object_class,
1768     PROP_REPO_URI,
1769     g_param_spec_string ("repo-uri",
1770     "repo uri",
1771     "osm repo uri",
1772     "http://tile.openstreetmap.org/#Z/#X/#Y.png",
1773     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1774    
1775     g_object_class_install_property (object_class,
1776     PROP_PROXY_URI,
1777     g_param_spec_string ("proxy-uri",
1778     "proxy uri",
1779     "http proxy uri on NULL",
1780     NULL,
1781     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1782    
1783     g_object_class_install_property (object_class,
1784     PROP_TILE_CACHE_DIR,
1785     g_param_spec_string ("tile-cache",
1786     "tile cache",
1787     "osm local tile cache dir",
1788     "/tmp/Maps",
1789     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1790    
1791     g_object_class_install_property (object_class,
1792     PROP_TILE_CACHE_DIR_IS_FULL_PATH,
1793     g_param_spec_boolean ("tile-cache-is-full-path",
1794     "tile cache is full path",
1795     "if true, the path passed to tile-cache is interpreted as the full cache path",
1796     FALSE,
1797     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1798    
1799     g_object_class_install_property (object_class,
1800     PROP_ZOOM,
1801     g_param_spec_int ("zoom",
1802     "zoom",
1803     "zoom level",
1804     MIN_ZOOM, /* minimum property value */
1805     MAX_ZOOM, /* maximum property value */
1806     3,
1807     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1808    
1809     g_object_class_install_property (object_class,
1810     PROP_MAX_ZOOM,
1811     g_param_spec_int ("max-zoom",
1812     "max zoom",
1813     "maximum zoom level",
1814     MIN_ZOOM, /* minimum property value */
1815     MAX_ZOOM, /* maximum property value */
1816     17,
1817     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1818    
1819     g_object_class_install_property (object_class,
1820     PROP_MIN_ZOOM,
1821     g_param_spec_int ("min-zoom",
1822     "min zoom",
1823     "minimum zoom level",
1824     MIN_ZOOM, /* minimum property value */
1825     MAX_ZOOM, /* maximum property value */
1826     1,
1827     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1828    
1829     g_object_class_install_property (object_class,
1830     PROP_LATITUDE,
1831     g_param_spec_float ("latitude",
1832     "latitude",
1833     "latitude in degrees",
1834     -90.0, /* minimum property value */
1835     90.0, /* maximum property value */
1836     0,
1837     G_PARAM_READABLE));
1838    
1839     g_object_class_install_property (object_class,
1840     PROP_LONGITUDE,
1841     g_param_spec_float ("longitude",
1842     "longitude",
1843     "longitude in degrees",
1844     -180.0, /* minimum property value */
1845     180.0, /* maximum property value */
1846     0,
1847     G_PARAM_READABLE));
1848    
1849     g_object_class_install_property (object_class,
1850     PROP_MAP_X,
1851     g_param_spec_int ("map-x",
1852     "map-x",
1853     "initial map x location",
1854     G_MININT, /* minimum property value */
1855     G_MAXINT, /* maximum property value */
1856     890,
1857     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1858    
1859     g_object_class_install_property (object_class,
1860     PROP_MAP_Y,
1861     g_param_spec_int ("map-y",
1862     "map-y",
1863     "initial map y location",
1864     G_MININT, /* minimum property value */
1865     G_MAXINT, /* maximum property value */
1866     515,
1867     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1868    
1869     g_object_class_install_property (object_class,
1870     PROP_TILES_QUEUED,
1871     g_param_spec_int ("tiles-queued",
1872     "tiles-queued",
1873     "number of tiles currently waiting to download",
1874     G_MININT, /* minimum property value */
1875     G_MAXINT, /* maximum property value */
1876     0,
1877     G_PARAM_READABLE));
1878    
1879     g_object_class_install_property (object_class,
1880     PROP_GPS_TRACK_WIDTH,
1881     g_param_spec_int ("gps-track-width",
1882     "gps-track-width",
1883     "width of the lines drawn for the gps track",
1884     1, /* minimum property value */
1885     G_MAXINT, /* maximum property value */
1886     4,
1887     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1888    
1889     g_object_class_install_property (object_class,
1890     PROP_GPS_POINT_R1,
1891     g_param_spec_int ("gps-track-point-radius",
1892     "gps-track-point-radius",
1893     "radius of the gps point inner circle",
1894     0, /* minimum property value */
1895     G_MAXINT, /* maximum property value */
1896     5,
1897     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1898    
1899     g_object_class_install_property (object_class,
1900     PROP_GPS_POINT_R2,
1901     g_param_spec_int ("gps-track-highlight-radius",
1902     "gps-track-highlight-radius",
1903     "radius of the gps point highlight circle",
1904     0, /* minimum property value */
1905     G_MAXINT, /* maximum property value */
1906     20,
1907     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1908    
1909     }
1910    
1911     void
1912     osm_gps_map_download_maps (OsmGpsMap *map, coord_t *pt1, coord_t *pt2, int zoom_start, int zoom_end)
1913     {
1914     int i,j,zoom,num_tiles;
1915     OsmGpsMapPrivate *priv = map->priv;
1916    
1917     if (pt1 && pt2)
1918     {
1919     gchar *filename;
1920     num_tiles = 0;
1921     zoom_end = CLAMP(zoom_end, priv->min_zoom, priv->max_zoom);
1922     g_debug("Download maps: z:%d->%d",zoom_start, zoom_end);
1923    
1924     for(zoom=zoom_start; zoom<=zoom_end; zoom++)
1925     {
1926     int x1,y1,x2,y2;
1927    
1928     x1 = (int)floor((float)lon2pixel(zoom, pt1->rlon) / (float)TILESIZE);
1929     y1 = (int)floor((float)lat2pixel(zoom, pt1->rlat) / (float)TILESIZE);
1930    
1931     x2 = (int)floor((float)lon2pixel(zoom, pt2->rlon) / (float)TILESIZE);
1932     y2 = (int)floor((float)lat2pixel(zoom, pt2->rlat) / (float)TILESIZE);
1933    
1934     // loop x1-x2
1935     for(i=x1; i<=x2; i++)
1936     {
1937     // loop y1 - y2
1938     for(j=y1; j<=y2; j++)
1939     {
1940     // x = i, y = j
1941     filename = g_strdup_printf("%s/%u/%u/%u.png", priv->cache_dir, zoom, i, j);
1942     if (!priv->cache_dir || !g_file_test(filename, G_FILE_TEST_EXISTS))
1943     {
1944     osm_gps_map_download_tile(map, zoom, i, j, FALSE);
1945     num_tiles++;
1946     }
1947     g_free(filename);
1948     }
1949     }
1950     g_debug("DL @Z:%d = %d tiles",zoom,num_tiles);
1951     }
1952     }
1953     }
1954    
1955     void
1956     osm_gps_map_get_bbox (OsmGpsMap *map, coord_t *pt1, coord_t *pt2)
1957     {
1958     OsmGpsMapPrivate *priv = map->priv;
1959    
1960     if (pt1 && pt2) {
1961     pt1->rlat = pixel2lat(priv->map_zoom, priv->map_y);
1962     pt1->rlon = pixel2lon(priv->map_zoom, priv->map_x);
1963     pt2->rlat = pixel2lat(priv->map_zoom, priv->map_y + GTK_WIDGET(map)->allocation.height);
1964     pt2->rlon = pixel2lon(priv->map_zoom, priv->map_x + GTK_WIDGET(map)->allocation.width);
1965    
1966     g_debug("BBOX: %f %f %f %f", pt1->rlat, pt1->rlon, pt2->rlat, pt2->rlon);
1967     }
1968     }
1969    
1970     void
1971     osm_gps_map_set_mapcenter (OsmGpsMap *map, float latitude, float longitude, int zoom)
1972     {
1973     osm_gps_map_set_center (map, latitude, longitude);
1974     osm_gps_map_set_zoom (map, zoom);
1975     }
1976    
1977     void
1978     osm_gps_map_set_center (OsmGpsMap *map, float latitude, float longitude)
1979     {
1980     int pixel_x, pixel_y;
1981     OsmGpsMapPrivate *priv;
1982    
1983     g_return_if_fail (OSM_IS_GPS_MAP (map));
1984     priv = map->priv;
1985    
1986     priv->center_rlat = deg2rad(latitude);
1987     priv->center_rlon = deg2rad(longitude);
1988     priv->center_coord_set = TRUE;
1989    
1990     // pixel_x,y, offsets
1991     pixel_x = lon2pixel(priv->map_zoom, priv->center_rlon);
1992     pixel_y = lat2pixel(priv->map_zoom, priv->center_rlat);
1993    
1994     priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
1995     priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
1996    
1997     osm_gps_map_map_redraw_idle(map);
1998     }
1999    
2000     int
2001     osm_gps_map_set_zoom (OsmGpsMap *map, int zoom)
2002     {
2003     int zoom_old;
2004     double factor = 1.0;
2005     int width_center, height_center;
2006     OsmGpsMapPrivate *priv;
2007    
2008     g_return_val_if_fail (OSM_IS_GPS_MAP (map), 0);
2009     priv = map->priv;
2010    
2011     if (zoom != priv->map_zoom)
2012     {
2013     width_center = GTK_WIDGET(map)->allocation.width / 2;
2014     height_center = GTK_WIDGET(map)->allocation.height / 2;
2015    
2016     zoom_old = priv->map_zoom;
2017     //constrain zoom min_zoom -> max_zoom
2018     priv->map_zoom = CLAMP(zoom, priv->min_zoom, priv->max_zoom);
2019    
2020     if (priv->center_coord_set)
2021     {
2022     priv->map_x = lon2pixel(priv->map_zoom, priv->center_rlon) - width_center;
2023     priv->map_y = lat2pixel(priv->map_zoom, priv->center_rlat) - height_center;
2024     }
2025     else
2026     {
2027     factor = exp(priv->map_zoom * M_LN2)/exp(zoom_old * M_LN2);
2028     priv->map_x = ((priv->map_x + width_center) * factor) - width_center;
2029     priv->map_y = ((priv->map_y + height_center) * factor) - height_center;
2030     }
2031    
2032     g_debug("Zoom changed from %d to %d factor:%f x:%d",
2033     zoom_old, priv->map_zoom, factor, priv->map_x);
2034    
2035     osm_gps_map_map_redraw_idle(map);
2036     }
2037     return priv->map_zoom;
2038     }
2039    
2040     void
2041     osm_gps_map_add_track (OsmGpsMap *map, GSList *track)
2042     {
2043     OsmGpsMapPrivate *priv;
2044    
2045     g_return_if_fail (OSM_IS_GPS_MAP (map));
2046     priv = map->priv;
2047    
2048     if (track) {
2049     priv->tracks = g_slist_append(priv->tracks, track);
2050     osm_gps_map_map_redraw_idle(map);
2051     }
2052     }
2053    
2054     void
2055     osm_gps_map_clear_tracks (OsmGpsMap *map)
2056     {
2057     g_return_if_fail (OSM_IS_GPS_MAP (map));
2058    
2059     osm_gps_map_free_tracks(map);
2060     osm_gps_map_map_redraw_idle(map);
2061     }
2062    
2063     void
2064     osm_gps_map_add_image (OsmGpsMap *map, float latitude, float longitude, GdkPixbuf *image)
2065     {
2066     g_return_if_fail (OSM_IS_GPS_MAP (map));
2067    
2068     if (image) {
2069     OsmGpsMapPrivate *priv = map->priv;
2070     image_t *im;
2071    
2072     //cache w/h for speed, and add image to list
2073     im = g_new0(image_t,1);
2074     im->w = gdk_pixbuf_get_width(image);
2075     im->h = gdk_pixbuf_get_width(image);
2076     im->pt.rlat = deg2rad(latitude);
2077     im->pt.rlon = deg2rad(longitude);
2078    
2079     g_object_ref(image);
2080     im->image = image;
2081    
2082     priv->images = g_slist_append(priv->images, im);
2083    
2084     osm_gps_map_map_redraw_idle(map);
2085     }
2086     }
2087    
2088     void
2089     osm_gps_map_clear_images (OsmGpsMap *map)
2090     {
2091     g_return_if_fail (OSM_IS_GPS_MAP (map));
2092    
2093     osm_gps_map_free_images(map);
2094     osm_gps_map_map_redraw_idle(map);
2095     }
2096    
2097     void
2098     osm_gps_map_osd_speed (OsmGpsMap *map, float speed)
2099     {
2100     OsmGpsMapPrivate *priv;
2101    
2102     PangoContext *context = NULL;
2103     PangoLayout *layout = NULL;
2104     PangoFontDescription *desc = NULL;
2105    
2106     GdkColor color;
2107     GdkGC *gc;
2108    
2109     gchar *buffer;
2110     //static int x = 10, y = 10;
2111     static int width = 0, height = 0;
2112    
2113     g_return_if_fail (OSM_IS_GPS_MAP (map));
2114     priv = map->priv;
2115    
2116     buffer = g_strdup_printf("%.0f", speed);
2117    
2118     /* pango initialisation */
2119     context = gtk_widget_get_pango_context (GTK_WIDGET(map));
2120     layout = pango_layout_new (context);
2121     desc = pango_font_description_new();
2122    
2123     pango_font_description_set_size (desc, 40 * PANGO_SCALE);
2124     pango_layout_set_font_description (layout, desc);
2125     pango_layout_set_text (layout, buffer, strlen(buffer));
2126    
2127     gc = gdk_gc_new (GTK_WIDGET(map)->window);
2128    
2129     color.red = (0 > 50) ? 0xffff : 0;
2130     color.green = 0;
2131     color.blue = 0;
2132    
2133     gdk_gc_set_rgb_fg_color (gc, &color);
2134    
2135     /* faster / less flicker alternative:*/
2136     gdk_draw_drawable (
2137     GTK_WIDGET(map)->window,
2138     GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(map)],
2139     priv->pixmap,
2140     0,0,
2141     0,0,
2142     width+10,width+10);
2143    
2144     gdk_draw_layout(GTK_WIDGET(map)->window,
2145     gc,
2146     0, 0,
2147     layout);
2148    
2149     /* set width and height */
2150     pango_layout_get_pixel_size(layout, &width, &height);
2151    
2152     g_free(buffer);
2153     pango_font_description_free (desc);
2154     g_object_unref (layout);
2155     g_object_unref (gc);
2156     }
2157    
2158     void
2159     osm_gps_map_draw_gps (OsmGpsMap *map, float latitude, float longitude, float heading)
2160     {
2161     int pixel_x, pixel_y;
2162     OsmGpsMapPrivate *priv;
2163    
2164     g_return_if_fail (OSM_IS_GPS_MAP (map));
2165     priv = map->priv;
2166    
2167     priv->gps->rlat = deg2rad(latitude);
2168     priv->gps->rlon = deg2rad(longitude);
2169     priv->gps_valid = TRUE;
2170    
2171     // pixel_x,y, offsets
2172     pixel_x = lon2pixel(priv->map_zoom, priv->gps->rlon);
2173     pixel_y = lat2pixel(priv->map_zoom, priv->gps->rlat);
2174    
2175     //If trip marker add to list of gps points.
2176     if (priv->record_trip_history) {
2177     coord_t *tp = g_new0(coord_t,1);
2178     tp->rlat = priv->gps->rlat;
2179     tp->rlon = priv->gps->rlon;
2180     priv->trip_history = g_slist_append(priv->trip_history, tp);
2181     }
2182    
2183     // dont draw anything if we are dragging
2184     if (priv->dragging) {
2185     g_debug("Dragging");
2186     return;
2187     }
2188    
2189     //Automatically center the map if the track approaches the edge
2190     if(priv->map_auto_center) {
2191     int x = pixel_x - priv->map_x;
2192     int y = pixel_y - priv->map_y;
2193     int width = GTK_WIDGET(map)->allocation.width;
2194     int height = GTK_WIDGET(map)->allocation.height;
2195     if( x < (width/2 - width/8) || x > (width/2 + width/8) ||
2196     y < (height/2 - height/8) || y > (height/2 + height/8)) {
2197    
2198     priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
2199     priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
2200     priv->center_coord_set = FALSE;
2201     }
2202     }
2203    
2204     // this redraws the map (including the gps track, and adjusts the
2205     // map center if it was changed
2206     osm_gps_map_map_redraw_idle(map);
2207     }
2208    
2209     void
2210     osm_gps_map_clear_gps (OsmGpsMap *map)
2211     {
2212     osm_gps_map_free_trip(map);
2213     osm_gps_map_map_redraw_idle(map);
2214     }
2215    
2216     coord_t
2217     osm_gps_map_get_co_ordinates (OsmGpsMap *map, int pixel_x, int pixel_y)
2218     {
2219     coord_t coord;
2220     OsmGpsMapPrivate *priv = map->priv;
2221    
2222     coord.rlat = pixel2lat(priv->map_zoom, priv->map_y + pixel_y);
2223     coord.rlon = pixel2lon(priv->map_zoom, priv->map_x + pixel_x);
2224     return coord;
2225     }
2226    
2227     GtkWidget *
2228     osm_gps_map_new (void)
2229     {
2230     return g_object_new (OSM_TYPE_GPS_MAP, NULL);
2231     }
2232    
2233     void
2234     osm_gps_map_screen_to_geographic (OsmGpsMap *map, gint pixel_x, gint pixel_y,
2235     gfloat *latitude, gfloat *longitude)
2236     {
2237     OsmGpsMapPrivate *priv;
2238    
2239     g_return_if_fail (OSM_IS_GPS_MAP (map));
2240     priv = map->priv;
2241    
2242     if (latitude)
2243     *latitude = rad2deg(pixel2lat(priv->map_zoom, priv->map_y + pixel_y));
2244     if (longitude)
2245     *longitude = rad2deg(pixel2lon(priv->map_zoom, priv->map_x + pixel_x));
2246     }
2247    
2248     void
2249     osm_gps_map_geographic_to_screen (OsmGpsMap *map,
2250     gfloat latitude, gfloat longitude,
2251     gint *pixel_x, gint *pixel_y)
2252     {
2253     OsmGpsMapPrivate *priv;
2254    
2255     g_return_if_fail (OSM_IS_GPS_MAP (map));
2256     priv = map->priv;
2257    
2258     if (pixel_x)
2259     *pixel_x = lon2pixel(priv->map_zoom, deg2rad(longitude)) - priv->map_x;
2260     if (pixel_y)
2261     *pixel_y = lat2pixel(priv->map_zoom, deg2rad(latitude)) - priv->map_y;
2262     }
2263    
2264     void
2265     osm_gps_map_scroll (OsmGpsMap *map, gint dx, gint dy)
2266     {
2267     OsmGpsMapPrivate *priv;
2268    
2269     g_return_if_fail (OSM_IS_GPS_MAP (map));
2270     priv = map->priv;
2271    
2272     priv->center_coord_set = FALSE;
2273     priv->map_x += dx;
2274     priv->map_y += dy;
2275    
2276     osm_gps_map_map_redraw_idle (map);
2277     }
2278