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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 215 - (hide annotations)
Sun Jul 12 20:01:14 2009 UTC (14 years, 10 months ago) by harbaum
File MIME type: text/plain
File size: 75496 byte(s)
Draw exact border in osm-gps-map
1 harbaum 200 /* -*- 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 harbaum 212 g_debug("Queing redraw @ %d,%d (w:%d h:%d)", offset_x,offset_y,
625     TILESIZE,TILESIZE);
626 harbaum 200
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 harbaum 214 /* libsoup-2.2 and libsoup-2.4 use different ways to store the body data */
638     #ifdef LIBSOUP22
639 harbaum 212 #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     #else
643     #define MSG_RESPONSE_BODY(a) ((a)->response_body->data)
644     #define MSG_RESPONSE_LEN(a) ((a)->response_body->length)
645     #endif
646    
647 harbaum 214 #ifdef LIBSOUP22
648 harbaum 200 static void
649 harbaum 212 osm_gps_map_tile_download_complete (SoupMessage *msg, gpointer user_data)
650     #else
651     static void
652 harbaum 200 osm_gps_map_tile_download_complete (SoupSession *session, SoupMessage *msg, gpointer user_data)
653 harbaum 212 #endif
654 harbaum 200 {
655     #ifndef NO_DISK_CACHE
656     int fd;
657     #endif
658     tile_download_t *dl = (tile_download_t *)user_data;
659     OsmGpsMap *map = OSM_GPS_MAP(dl->map);
660     OsmGpsMapPrivate *priv = map->priv;
661    
662     if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
663     {
664     #ifndef NO_DISK_CACHE
665     if (g_mkdir_with_parents(dl->folder,0700) == 0)
666     {
667     fd = open(dl->filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
668     if (fd != -1)
669     {
670 harbaum 212 write (fd, MSG_RESPONSE_BODY(msg), MSG_RESPONSE_LEN(msg));
671    
672     g_debug("Wrote %lld bytes to %s", MSG_RESPONSE_LEN(msg), dl->filename);
673 harbaum 200 close (fd);
674     #endif
675     if (dl->redraw)
676     {
677     #ifndef NO_DISK_CACHE
678     GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (dl->filename, NULL);
679     #else
680     GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_type("png", NULL);
681 harbaum 212 if(!gdk_pixbuf_loader_write(loader, (unsigned char*)MSG_RESPONSE_BODY(msg),
682     MSG_RESPONSE_LEN(msg), NULL))
683 harbaum 200 g_warning("Error: Decoding of image failed");
684    
685     gdk_pixbuf_loader_close(loader, NULL);
686    
687     GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
688    
689     /* give up loader but keep the pixbuf */
690     g_object_ref(pixbuf);
691     g_object_unref(loader);
692     #endif
693     /* Store the tile into the cache */
694     if (G_LIKELY (pixbuf))
695     {
696     OsmCachedTile *tile = g_slice_new (OsmCachedTile);
697     tile->pixbuf = pixbuf;
698     tile->redraw_cycle = priv->redraw_cycle;
699     /* if the tile is already in the cache (it could be one
700     * rendered from another zoom level), it will be
701     * overwritten */
702     g_hash_table_insert (priv->tile_cache, dl->filename, tile);
703     /* NULL-ify dl->filename so that it won't be freed, as
704     * we are using it as a key in the hash table */
705     dl->filename = NULL;
706     }
707    
708     osm_gps_map_map_redraw_idle (map);
709     }
710     #ifndef NO_DISK_CACHE
711     }
712     }
713     else
714     {
715     g_warning("Error creating tile download directory: %s", dl->folder);
716     }
717     #endif
718     g_hash_table_remove(priv->tile_queue, dl->uri);
719    
720     g_free(dl->uri);
721     g_free(dl->folder);
722     g_free(dl->filename);
723     g_free(dl);
724     }
725     else
726     {
727     g_warning("Error downloading tile: %d - %s", msg->status_code, msg->reason_phrase);
728     if (msg->status_code == SOUP_STATUS_NOT_FOUND)
729     {
730     g_hash_table_insert(priv->missing_tiles, dl->uri, NULL);
731     g_hash_table_remove(priv->tile_queue, dl->uri);
732     }
733     else if (msg->status_code == SOUP_STATUS_CANCELLED)
734     {
735     ;//application exiting
736     }
737     else
738     {
739 harbaum 214 #ifdef LIBSOUP22
740 harbaum 212 soup_session_requeue_message(dl->session, msg);
741     #else
742 harbaum 200 soup_session_requeue_message(session, msg);
743 harbaum 212 #endif
744 harbaum 200 return;
745     }
746     }
747    
748    
749     }
750    
751     static void
752     osm_gps_map_download_tile (OsmGpsMap *map, int zoom, int x, int y, gboolean redraw)
753     {
754     SoupMessage *msg;
755     OsmGpsMapPrivate *priv = map->priv;
756     tile_download_t *dl = g_new0(tile_download_t,1);
757    
758     //calculate the uri to download
759     dl->uri = replace_map_uri(map, priv->repo_uri, zoom, x, y);
760    
761 harbaum 214 #ifdef LIBSOUP22
762 harbaum 212 dl->session = priv->soup_session;
763     #endif
764    
765 harbaum 200 //check the tile has not already been queued for download,
766     //or has been attempted, and its missing
767     if (g_hash_table_lookup_extended(priv->tile_queue, dl->uri, NULL, NULL) ||
768     g_hash_table_lookup_extended(priv->missing_tiles, dl->uri, NULL, NULL) )
769     {
770     g_debug("Tile already downloading (or missing)");
771     g_free(dl->uri);
772     g_free(dl);
773     } else {
774     dl->folder = g_strdup_printf("%s/%d/%d/",priv->cache_dir, zoom, x);
775     dl->filename = g_strdup_printf("%s/%d/%d/%d.png",priv->cache_dir, zoom, x, y);
776     dl->map = map;
777     dl->redraw = redraw;
778    
779     g_debug("Download tile: %d,%d z:%d\n\t%s --> %s", x, y, zoom, dl->uri, dl->filename);
780    
781     msg = soup_message_new (SOUP_METHOD_GET, dl->uri);
782     if (msg) {
783     if (priv->the_google) {
784     //Set maps.google.com as the referrer
785     g_debug("Setting Google Referrer");
786     soup_message_headers_append(msg->request_headers, "Referer", "http://maps.google.com/");
787     //For google satelite also set the appropriate cookie value
788     if (priv->uri_format & URI_HAS_Q) {
789     const char *cookie = g_getenv("GOOGLE_COOKIE");
790     if (cookie) {
791     g_debug("Adding Google Cookie");
792     soup_message_headers_append(msg->request_headers, "Cookie", cookie);
793 harbaum 212
794 harbaum 200 }
795     }
796     }
797    
798     g_hash_table_insert (priv->tile_queue, dl->uri, msg);
799     soup_session_queue_message (priv->soup_session, msg, osm_gps_map_tile_download_complete, dl);
800     } else {
801     g_warning("Could not create soup message");
802     g_free(dl->uri);
803     g_free(dl->folder);
804     g_free(dl->filename);
805     g_free(dl);
806     }
807     }
808     }
809    
810     static GdkPixbuf *
811     osm_gps_map_load_cached_tile (OsmGpsMap *map, int zoom, int x, int y)
812     {
813     OsmGpsMapPrivate *priv = map->priv;
814     gchar *filename;
815     GdkPixbuf *pixbuf;
816     OsmCachedTile *tile;
817    
818     filename = g_strdup_printf("%s/%u/%u/%u.png",
819     priv->cache_dir,
820     zoom, x, y);
821    
822     tile = g_hash_table_lookup (priv->tile_cache, filename);
823     if (tile)
824     {
825     g_free (filename);
826     }
827     #ifdef NO_DISK_CACHE
828     else
829     {
830     pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
831     if (pixbuf)
832     {
833     tile = g_slice_new (OsmCachedTile);
834     tile->pixbuf = pixbuf;
835     g_hash_table_insert (priv->tile_cache, filename, tile);
836     }
837     }
838     #endif
839    
840     /* set/update the redraw_cycle timestamp on the tile */
841     if (tile)
842     {
843     tile->redraw_cycle = priv->redraw_cycle;
844     pixbuf = g_object_ref (tile->pixbuf);
845     } else
846     pixbuf = NULL;
847    
848     return pixbuf;
849     }
850    
851     static GdkPixbuf *
852     osm_gps_map_find_bigger_tile (OsmGpsMap *map, int zoom, int x, int y,
853     int *zoom_found)
854     {
855     GdkPixbuf *pixbuf;
856     int next_zoom, next_x, next_y;
857    
858     if (zoom == 0) return NULL;
859     next_zoom = zoom - 1;
860     next_x = x / 2;
861     next_y = y / 2;
862     pixbuf = osm_gps_map_load_cached_tile (map, next_zoom, next_x, next_y);
863     if (pixbuf)
864     *zoom_found = next_zoom;
865     else
866     pixbuf = osm_gps_map_find_bigger_tile (map, next_zoom, next_x, next_y,
867     zoom_found);
868     return pixbuf;
869     }
870    
871     static GdkPixbuf *
872     osm_gps_map_render_missing_tile_upscaled (OsmGpsMap *map, int zoom,
873     int x, int y)
874     {
875     GdkPixbuf *pixbuf, *big, *area;
876     int zoom_big, zoom_diff, area_size, area_x, area_y;
877     int modulo;
878    
879     big = osm_gps_map_find_bigger_tile (map, zoom, x, y, &zoom_big);
880     if (!big) return NULL;
881    
882     g_debug ("Found bigger tile (zoom = %d, wanted = %d)", zoom_big, zoom);
883    
884     /* get a Pixbuf for the area to magnify */
885     zoom_diff = zoom - zoom_big;
886     area_size = TILESIZE >> zoom_diff;
887     modulo = 1 << zoom_diff;
888     area_x = (x % modulo) * area_size;
889     area_y = (y % modulo) * area_size;
890     area = gdk_pixbuf_new_subpixbuf (big, area_x, area_y,
891     area_size, area_size);
892     g_object_unref (big);
893     pixbuf = gdk_pixbuf_scale_simple (area, TILESIZE, TILESIZE,
894     GDK_INTERP_NEAREST);
895     g_object_unref (area);
896     return pixbuf;
897     }
898    
899     static GdkPixbuf *
900     osm_gps_map_render_missing_tile (OsmGpsMap *map, int zoom, int x, int y)
901     {
902     /* maybe TODO: render from downscaled tiles, if the following fails */
903     return osm_gps_map_render_missing_tile_upscaled (map, zoom, x, y);
904     }
905    
906     static void
907     osm_gps_map_load_tile (OsmGpsMap *map, int zoom, int x, int y, int offset_x, int offset_y)
908     {
909     OsmGpsMapPrivate *priv = map->priv;
910     gchar *filename;
911     GdkPixbuf *pixbuf;
912    
913     g_debug("Load tile %d,%d (%d,%d) z:%d", x, y, offset_x, offset_y, zoom);
914    
915     filename = g_strdup_printf("%s/%u/%u/%u.png",
916     priv->cache_dir,
917     zoom, x, y);
918    
919     /* try to get file from internal cache */
920     pixbuf = osm_gps_map_load_cached_tile(map, zoom, x, y);
921    
922     #ifdef NO_DISK_CACHE
923     if(!pixbuf)
924     pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
925     #endif
926    
927     if(pixbuf)
928     {
929     g_debug("Found tile %s", filename);
930     osm_gps_map_blit_tile(map, pixbuf, offset_x,offset_y);
931     g_object_unref (pixbuf);
932     }
933     else
934     {
935     if (priv->map_auto_download)
936     osm_gps_map_download_tile(map, zoom, x, y, TRUE);
937    
938     /* try to render the tile by scaling cached tiles from other zoom
939     * levels */
940     pixbuf = osm_gps_map_render_missing_tile (map, zoom, x, y);
941     if (pixbuf)
942     {
943     gdk_draw_pixbuf (priv->pixmap,
944     priv->gc_map,
945     pixbuf,
946     0,0,
947     offset_x,offset_y,
948     TILESIZE,TILESIZE,
949     GDK_RGB_DITHER_NONE, 0, 0);
950     g_object_unref (pixbuf);
951     }
952     else
953     {
954     //prevent some artifacts when drawing not yet loaded areas.
955     gdk_draw_rectangle (priv->pixmap,
956     GTK_WIDGET(map)->style->white_gc,
957     TRUE, offset_x, offset_y, TILESIZE, TILESIZE);
958     }
959     }
960     g_free(filename);
961     }
962    
963     static void
964     osm_gps_map_fill_tiles_pixel (OsmGpsMap *map)
965     {
966     OsmGpsMapPrivate *priv = map->priv;
967     int i,j, width, height, tile_x0, tile_y0, tiles_nx, tiles_ny;
968     int offset_xn = 0;
969     int offset_yn = 0;
970     int offset_x;
971     int offset_y;
972    
973     g_debug("Fill tiles: %d,%d z:%d", priv->map_x, priv->map_y, priv->map_zoom);
974    
975     offset_x = - priv->map_x % TILESIZE;
976     offset_y = - priv->map_y % TILESIZE;
977     if (offset_x > 0) offset_x -= TILESIZE;
978     if (offset_y > 0) offset_y -= TILESIZE;
979    
980     offset_xn = offset_x + EXTRA_BORDER;
981     offset_yn = offset_y + EXTRA_BORDER;
982    
983     width = GTK_WIDGET(map)->allocation.width;
984     height = GTK_WIDGET(map)->allocation.height;
985    
986     tiles_nx = (width - offset_x) / TILESIZE + 1;
987     tiles_ny = (height - offset_y) / TILESIZE + 1;
988    
989     tile_x0 = floor((float)priv->map_x / (float)TILESIZE);
990     tile_y0 = floor((float)priv->map_y / (float)TILESIZE);
991    
992     //TODO: implement wrap around
993     for (i=tile_x0; i<(tile_x0+tiles_nx);i++)
994     {
995     for (j=tile_y0; j<(tile_y0+tiles_ny); j++)
996     {
997     if( j<0 || i<0 || i>=exp(priv->map_zoom * M_LN2) || j>=exp(priv->map_zoom * M_LN2))
998     {
999     gdk_draw_rectangle (priv->pixmap,
1000     GTK_WIDGET(map)->style->white_gc,
1001     TRUE,
1002     offset_xn, offset_yn,
1003     TILESIZE,TILESIZE);
1004     }
1005     else
1006     {
1007     osm_gps_map_load_tile(map,
1008     priv->map_zoom,
1009     i,j,
1010     offset_xn,offset_yn);
1011     }
1012     offset_yn += TILESIZE;
1013     }
1014     offset_xn += TILESIZE;
1015     offset_yn = offset_y + EXTRA_BORDER;
1016     }
1017     }
1018    
1019     static void
1020     osm_gps_map_print_track (OsmGpsMap *map, GSList *trackpoint_list)
1021     {
1022     OsmGpsMapPrivate *priv = map->priv;
1023    
1024     GSList *list;
1025     int x,y;
1026     int min_x = 0,min_y = 0,max_x = 0,max_y = 0;
1027     int lw = priv->ui_gps_track_width;
1028     int map_x0, map_y0;
1029     #ifdef USE_CAIRO
1030     cairo_t *cr;
1031     #else
1032     int last_x = 0, last_y = 0;
1033     GdkColor color;
1034     GdkGC *gc;
1035     #endif
1036    
1037     #ifdef USE_CAIRO
1038     cr = gdk_cairo_create(priv->pixmap);
1039     cairo_set_line_width (cr, lw);
1040     cairo_set_source_rgba (cr, 60000.0/65535.0, 0.0, 0.0, 0.6);
1041     cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1042     cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
1043     #else
1044     gc = gdk_gc_new(priv->pixmap);
1045     color.green = 0;
1046     color.blue = 0;
1047     color.red = 60000;
1048     gdk_gc_set_rgb_fg_color(gc, &color);
1049     gdk_gc_set_line_attributes(gc, lw, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
1050     #endif
1051    
1052     map_x0 = priv->map_x - EXTRA_BORDER;
1053     map_y0 = priv->map_y - EXTRA_BORDER;
1054 harbaum 215
1055 harbaum 200 for(list = trackpoint_list; list != NULL; list = list->next)
1056     {
1057     coord_t *tp = list->data;
1058    
1059     x = lon2pixel(priv->map_zoom, tp->rlon) - map_x0;
1060     y = lat2pixel(priv->map_zoom, tp->rlat) - map_y0;
1061    
1062     // first time through loop
1063     if (list == trackpoint_list) {
1064     #ifdef USE_CAIRO
1065     cairo_move_to(cr, x, y);
1066     #else
1067     last_x = x;
1068     last_y = y;
1069     #endif
1070     }
1071    
1072     #ifdef USE_CAIRO
1073     cairo_line_to(cr, x, y);
1074     #else
1075     gdk_draw_line (priv->pixmap, gc, x, y, last_x, last_y);
1076     last_x = x;
1077     last_y = y;
1078     #endif
1079    
1080     max_x = MAX(x,max_x);
1081     min_x = MIN(x,min_x);
1082     max_y = MAX(y,max_y);
1083     min_y = MIN(y,min_y);
1084     }
1085    
1086     gtk_widget_queue_draw_area (
1087     GTK_WIDGET(map),
1088     min_x - lw,
1089     min_y - lw,
1090     max_x + (lw * 2),
1091     max_y + (lw * 2));
1092    
1093     #ifdef USE_CAIRO
1094     cairo_stroke(cr);
1095     cairo_destroy(cr);
1096     #else
1097     g_object_unref(gc);
1098     #endif
1099     }
1100    
1101     /* Prints the gps trip history, and any other tracks */
1102     static void
1103     osm_gps_map_print_tracks (OsmGpsMap *map)
1104     {
1105     OsmGpsMapPrivate *priv = map->priv;
1106    
1107     if (priv->show_trip_history)
1108     osm_gps_map_print_track (map, priv->trip_history);
1109     if (priv->tracks)
1110     {
1111     g_debug("TRACK");
1112     GSList* tmp = priv->tracks;
1113     while (tmp != NULL)
1114     {
1115     osm_gps_map_print_track (map, tmp->data);
1116     tmp = g_slist_next(tmp);
1117     }
1118     }
1119     }
1120    
1121 harbaum 213 static gboolean
1122     osm_gps_map_purge_cache_check(gpointer key, gpointer value, gpointer user)
1123     {
1124     return(((OsmCachedTile*)value)->redraw_cycle != ((OsmGpsMapPrivate*)user)->redraw_cycle);
1125     }
1126    
1127 harbaum 200 static void
1128     osm_gps_map_purge_cache (OsmGpsMap *map)
1129     {
1130     OsmGpsMapPrivate *priv = map->priv;
1131    
1132     if (g_hash_table_size (priv->tile_cache) < priv->max_tile_cache_size)
1133     return;
1134    
1135     /* run through the cache, and remove the tiles which have not been used
1136     * during the last redraw operation */
1137 harbaum 213 g_hash_table_foreach_remove(priv->tile_cache, osm_gps_map_purge_cache_check, priv);
1138 harbaum 200 }
1139    
1140     static gboolean
1141     osm_gps_map_map_redraw (OsmGpsMap *map)
1142     {
1143     OsmGpsMapPrivate *priv = map->priv;
1144    
1145     priv->idle_map_redraw = 0;
1146    
1147     /* the motion_notify handler uses priv->pixmap to redraw the area; if we
1148     * change it while we are dragging, we will end up showing it in the wrong
1149     * place. This could be fixed by carefully recompute the coordinates, but
1150     * for now it's easier just to disable redrawing the map while dragging */
1151     if (priv->dragging)
1152     return FALSE;
1153    
1154     priv->redraw_cycle++;
1155    
1156     /* draw white background to initialise pixmap */
1157     gdk_draw_rectangle (
1158     priv->pixmap,
1159     GTK_WIDGET(map)->style->white_gc,
1160     TRUE,
1161     0, 0,
1162     GTK_WIDGET(map)->allocation.width + EXTRA_BORDER * 2,
1163     GTK_WIDGET(map)->allocation.height + EXTRA_BORDER * 2);
1164    
1165     osm_gps_map_fill_tiles_pixel(map);
1166    
1167     osm_gps_map_print_tracks(map);
1168     osm_gps_map_draw_gps_point(map);
1169     osm_gps_map_print_images(map);
1170    
1171     //osm_gps_map_osd_speed(map, 1.5);
1172     osm_gps_map_purge_cache(map);
1173     gtk_widget_queue_draw (GTK_WIDGET (map));
1174    
1175     return FALSE;
1176     }
1177    
1178     static void
1179     osm_gps_map_map_redraw_idle (OsmGpsMap *map)
1180     {
1181     OsmGpsMapPrivate *priv = map->priv;
1182    
1183     if (priv->idle_map_redraw == 0)
1184     priv->idle_map_redraw = g_idle_add ((GSourceFunc)osm_gps_map_map_redraw, map);
1185     }
1186    
1187     static void
1188     osm_gps_map_init (OsmGpsMap *object)
1189     {
1190     OsmGpsMapPrivate *priv;
1191    
1192     priv = G_TYPE_INSTANCE_GET_PRIVATE (object, OSM_TYPE_GPS_MAP, OsmGpsMapPrivate);
1193     object->priv = priv;
1194    
1195     priv->pixmap = NULL;
1196    
1197     priv->trip_history = NULL;
1198     priv->gps = g_new0(coord_t, 1);
1199     priv->gps_valid = FALSE;
1200    
1201     priv->tracks = NULL;
1202     priv->images = NULL;
1203    
1204     priv->drag_counter = 0;
1205     priv->drag_mouse_dx = 0;
1206     priv->drag_mouse_dy = 0;
1207     priv->drag_start_mouse_x = 0;
1208     priv->drag_start_mouse_y = 0;
1209    
1210     priv->uri_format = 0;
1211     priv->the_google = FALSE;
1212    
1213 harbaum 214 #ifndef LIBSOUP22
1214 harbaum 200 //Change naumber of concurrent connections option?
1215     priv->soup_session = soup_session_async_new_with_options(
1216     SOUP_SESSION_USER_AGENT,
1217     "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11",
1218     NULL);
1219 harbaum 214 #else
1220     /* libsoup-2.2 seems not to be able to set the user agent */
1221     priv->soup_session = soup_session_async_new();
1222 harbaum 212 #endif
1223 harbaum 200
1224     //Hash table which maps tile d/l URIs to SoupMessage requests
1225     priv->tile_queue = g_hash_table_new (g_str_hash, g_str_equal);
1226    
1227     //Some mapping providers (Google) have varying degrees of tiles at multiple
1228     //zoom levels
1229     priv->missing_tiles = g_hash_table_new (g_str_hash, g_str_equal);
1230    
1231     /* memory cache for most recently used tiles */
1232     priv->tile_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
1233     g_free, (GDestroyNotify)cached_tile_free);
1234     priv->max_tile_cache_size = 20;
1235    
1236     gtk_widget_add_events (GTK_WIDGET (object),
1237     GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1238     GDK_POINTER_MOTION_MASK |
1239     GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
1240     GTK_WIDGET_SET_FLAGS (object, GTK_CAN_FOCUS);
1241    
1242     g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, my_log_handler, NULL);
1243     }
1244    
1245 harbaum 213 #ifndef G_CHECKSUM_MD5
1246     /* simple hash algorithm hack if md5 is not present */
1247     static char *simple_hash(char *str) {
1248     union {
1249     char str[4];
1250     gulong val;
1251     } hash = { .val = 0x55555555 };
1252    
1253     while(*str) {
1254     hash.str[(int)str & 3] ^= *str;
1255     str++;
1256     }
1257     return g_strdup_printf("%08lX", hash.val);
1258     }
1259     #endif
1260    
1261 harbaum 200 static GObject *
1262     osm_gps_map_constructor (GType gtype, guint n_properties, GObjectConstructParam *properties)
1263     {
1264     GObject *object;
1265     OsmGpsMapPrivate *priv;
1266    
1267     //Always chain up to the parent constructor
1268     object = G_OBJECT_CLASS(osm_gps_map_parent_class)->constructor(gtype, n_properties, properties);
1269     priv = OSM_GPS_MAP_PRIVATE(object);
1270    
1271     if (!priv->cache_dir_is_full_path) {
1272 harbaum 212 #ifdef G_CHECKSUM_MD5
1273 harbaum 200 char *md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, priv->repo_uri, -1);
1274 harbaum 212 #else
1275 harbaum 213 char *md5 = simple_hash(priv->repo_uri);
1276 harbaum 212 #endif
1277 harbaum 200
1278     if (priv->cache_dir) {
1279     char *old = priv->cache_dir;
1280     //the new cachedir is the given cache dir + the md5 of the repo_uri
1281     priv->cache_dir = g_strdup_printf("%s/%s", old, md5);
1282     g_debug("Adjusting cache dir %s -> %s", old, priv->cache_dir);
1283     g_free(old);
1284     } else {
1285     //the new cachedir is the current dir + the md5 of the repo_uri
1286     priv->cache_dir = g_strdup(md5);
1287     }
1288    
1289     g_free(md5);
1290     }
1291    
1292     return object;
1293     }
1294    
1295     static void
1296     osm_gps_map_dispose (GObject *object)
1297     {
1298     OsmGpsMap *map = OSM_GPS_MAP(object);
1299     OsmGpsMapPrivate *priv = map->priv;
1300    
1301     if (priv->is_disposed)
1302     return;
1303    
1304     priv->is_disposed = TRUE;
1305    
1306     soup_session_abort(priv->soup_session);
1307     g_object_unref(priv->soup_session);
1308    
1309     g_hash_table_destroy(priv->tile_queue);
1310     g_hash_table_destroy(priv->missing_tiles);
1311     g_hash_table_destroy(priv->tile_cache);
1312    
1313     osm_gps_map_free_images(map);
1314    
1315     if(priv->pixmap)
1316     g_object_unref (priv->pixmap);
1317    
1318     if(priv->gc_map)
1319     g_object_unref(priv->gc_map);
1320    
1321     if (priv->idle_map_redraw != 0)
1322     g_source_remove (priv->idle_map_redraw);
1323    
1324     G_OBJECT_CLASS (osm_gps_map_parent_class)->dispose (object);
1325     }
1326    
1327     static void
1328     osm_gps_map_finalize (GObject *object)
1329     {
1330     OsmGpsMap *map = OSM_GPS_MAP(object);
1331     OsmGpsMapPrivate *priv = map->priv;
1332    
1333     g_free(priv->cache_dir);
1334     g_free(priv->repo_uri);
1335    
1336     osm_gps_map_free_trip(map);
1337     osm_gps_map_free_tracks(map);
1338    
1339     G_OBJECT_CLASS (osm_gps_map_parent_class)->finalize (object);
1340     }
1341    
1342     static void
1343     osm_gps_map_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1344     {
1345     g_return_if_fail (OSM_IS_GPS_MAP (object));
1346     OsmGpsMap *map = OSM_GPS_MAP(object);
1347     OsmGpsMapPrivate *priv = map->priv;
1348    
1349     switch (prop_id)
1350     {
1351     case PROP_AUTO_CENTER:
1352     priv->map_auto_center = g_value_get_boolean (value);
1353     break;
1354     case PROP_RECORD_TRIP_HISTORY:
1355     priv->record_trip_history = g_value_get_boolean (value);
1356     break;
1357     case PROP_SHOW_TRIP_HISTORY:
1358     priv->show_trip_history = g_value_get_boolean (value);
1359     break;
1360     case PROP_AUTO_DOWNLOAD:
1361     priv->map_auto_download = g_value_get_boolean (value);
1362     break;
1363     case PROP_REPO_URI:
1364     priv->repo_uri = g_value_dup_string (value);
1365     inspect_map_uri(map);
1366     break;
1367     case PROP_PROXY_URI:
1368     if ( g_value_get_string(value) ) {
1369    
1370     priv->proxy_uri = g_value_dup_string (value);
1371     g_debug("Setting proxy server: %s", priv->proxy_uri);
1372 harbaum 214 #ifndef LIBSOUP22
1373     GValue val = {0};
1374 harbaum 200 SoupURI* uri = soup_uri_new(priv->proxy_uri);
1375     g_value_init(&val, SOUP_TYPE_URI);
1376     g_value_take_boxed(&val, uri);
1377    
1378     g_object_set_property(G_OBJECT(priv->soup_session),SOUP_SESSION_PROXY_URI,&val);
1379 harbaum 212 #else
1380 harbaum 214 SoupUri* uri = soup_uri_new(priv->proxy_uri);
1381     g_object_set(G_OBJECT(priv->soup_session), SOUP_SESSION_PROXY_URI, uri, NULL);
1382 harbaum 212 #endif
1383 harbaum 214 soup_uri_free(uri);
1384 harbaum 200 } else
1385     priv->proxy_uri = NULL;
1386    
1387     break;
1388     case PROP_TILE_CACHE_DIR:
1389     priv->cache_dir = g_value_dup_string (value);
1390     break;
1391     case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
1392     priv->cache_dir_is_full_path = g_value_get_boolean (value);
1393     break;
1394     case PROP_ZOOM:
1395     priv->map_zoom = g_value_get_int (value);
1396     break;
1397     case PROP_MAX_ZOOM:
1398     priv->max_zoom = g_value_get_int (value);
1399     break;
1400     case PROP_MIN_ZOOM:
1401     priv->min_zoom = g_value_get_int (value);
1402     break;
1403     case PROP_MAP_X:
1404     priv->map_x = g_value_get_int (value);
1405     priv->center_coord_set = FALSE;
1406     break;
1407     case PROP_MAP_Y:
1408     priv->map_y = g_value_get_int (value);
1409     priv->center_coord_set = FALSE;
1410     break;
1411     case PROP_GPS_TRACK_WIDTH:
1412     priv->ui_gps_track_width = g_value_get_int (value);
1413     break;
1414     case PROP_GPS_POINT_R1:
1415     priv->ui_gps_point_inner_radius = g_value_get_int (value);
1416     break;
1417     case PROP_GPS_POINT_R2:
1418     priv->ui_gps_point_outer_radius = g_value_get_int (value);
1419     break;
1420     default:
1421     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1422     break;
1423     }
1424     }
1425    
1426     static void
1427     osm_gps_map_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
1428     {
1429     g_return_if_fail (OSM_IS_GPS_MAP (object));
1430     float lat,lon;
1431     OsmGpsMap *map = OSM_GPS_MAP(object);
1432     OsmGpsMapPrivate *priv = map->priv;
1433    
1434     switch (prop_id)
1435     {
1436     case PROP_AUTO_CENTER:
1437     g_value_set_boolean(value, priv->map_auto_center);
1438     break;
1439     case PROP_RECORD_TRIP_HISTORY:
1440     g_value_set_boolean(value, priv->record_trip_history);
1441     break;
1442     case PROP_SHOW_TRIP_HISTORY:
1443     g_value_set_boolean(value, priv->show_trip_history);
1444     break;
1445     case PROP_AUTO_DOWNLOAD:
1446     g_value_set_boolean(value, priv->map_auto_download);
1447     break;
1448     case PROP_REPO_URI:
1449     g_value_set_string(value, priv->repo_uri);
1450     break;
1451     case PROP_PROXY_URI:
1452     g_value_set_string(value, priv->proxy_uri);
1453     break;
1454     case PROP_TILE_CACHE_DIR:
1455     g_value_set_string(value, priv->cache_dir);
1456     break;
1457     case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
1458     g_value_set_boolean(value, priv->cache_dir_is_full_path);
1459     break;
1460     case PROP_ZOOM:
1461     g_value_set_int(value, priv->map_zoom);
1462     break;
1463     case PROP_MAX_ZOOM:
1464     g_value_set_int(value, priv->max_zoom);
1465     break;
1466     case PROP_MIN_ZOOM:
1467     g_value_set_int(value, priv->min_zoom);
1468     break;
1469     case PROP_LATITUDE:
1470     lat = pixel2lat(priv->map_zoom,
1471     priv->map_y + (GTK_WIDGET(map)->allocation.height / 2));
1472     g_value_set_float(value, rad2deg(lat));
1473     break;
1474     case PROP_LONGITUDE:
1475     lon = pixel2lon(priv->map_zoom,
1476     priv->map_x + (GTK_WIDGET(map)->allocation.width / 2));
1477     g_value_set_float(value, rad2deg(lon));
1478     break;
1479     case PROP_MAP_X:
1480     g_value_set_int(value, priv->map_x);
1481     break;
1482     case PROP_MAP_Y:
1483     g_value_set_int(value, priv->map_y);
1484     break;
1485     case PROP_TILES_QUEUED:
1486     g_value_set_int(value, g_hash_table_size(priv->tile_queue));
1487     break;
1488     case PROP_GPS_TRACK_WIDTH:
1489     g_value_set_int(value, priv->ui_gps_track_width);
1490     break;
1491     case PROP_GPS_POINT_R1:
1492     g_value_set_int(value, priv->ui_gps_point_inner_radius);
1493     break;
1494     case PROP_GPS_POINT_R2:
1495     g_value_set_int(value, priv->ui_gps_point_outer_radius);
1496     break;
1497     default:
1498     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1499     break;
1500     }
1501     }
1502    
1503     static gboolean
1504     _osm_gps_map_scroll (GtkWidget *widget, GdkEventScroll *event)
1505     {
1506     OsmGpsMap *map = OSM_GPS_MAP(widget);
1507     OsmGpsMapPrivate *priv = map->priv;
1508    
1509     if (event->direction == GDK_SCROLL_UP)
1510     {
1511     osm_gps_map_set_zoom(map, priv->map_zoom+1);
1512     }
1513     else
1514     {
1515     osm_gps_map_set_zoom(map, priv->map_zoom-1);
1516     }
1517    
1518     return FALSE;
1519     }
1520    
1521     static gboolean
1522     osm_gps_map_button_press (GtkWidget *widget, GdkEventButton *event)
1523     {
1524     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1525    
1526     priv->drag_counter = 0;
1527     priv->drag_start_mouse_x = (int) event->x;
1528     priv->drag_start_mouse_y = (int) event->y;
1529     priv->drag_start_map_x = priv->map_x;
1530     priv->drag_start_map_y = priv->map_y;
1531    
1532     return FALSE;
1533     }
1534    
1535     static gboolean
1536     osm_gps_map_button_release (GtkWidget *widget, GdkEventButton *event)
1537     {
1538     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1539    
1540     if (priv->dragging)
1541     {
1542     priv->dragging = FALSE;
1543    
1544     priv->map_x = priv->drag_start_map_x;
1545     priv->map_y = priv->drag_start_map_y;
1546    
1547     priv->map_x += (priv->drag_start_mouse_x - (int) event->x);
1548     priv->map_y += (priv->drag_start_mouse_y - (int) event->y);
1549    
1550     priv->center_coord_set = FALSE;
1551    
1552     osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
1553     }
1554    
1555     priv->drag_mouse_dx = 0;
1556     priv->drag_mouse_dy = 0;
1557     priv->drag_counter = 0;
1558    
1559     return FALSE;
1560     }
1561    
1562     static gboolean
1563     osm_gps_map_motion_notify (GtkWidget *widget, GdkEventMotion *event)
1564     {
1565     int x, y;
1566     GdkModifierType state;
1567     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1568    
1569     if (event->is_hint)
1570     gdk_window_get_pointer (event->window, &x, &y, &state);
1571     else
1572     {
1573     x = event->x;
1574     y = event->y;
1575     state = event->state;
1576     }
1577    
1578     // are we being dragged
1579     if (!(state & GDK_BUTTON1_MASK))
1580     return FALSE;
1581    
1582     priv->drag_counter++;
1583    
1584     // we havent dragged more than 6 pixels
1585     if (priv->drag_counter < 6)
1586     return FALSE;
1587    
1588     priv->dragging = TRUE;
1589    
1590     if (priv->map_auto_center)
1591     g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
1592    
1593     priv->drag_mouse_dx = x - priv->drag_start_mouse_x;
1594     priv->drag_mouse_dy = y - priv->drag_start_mouse_y;
1595    
1596     gdk_draw_drawable (
1597     widget->window,
1598     widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1599     priv->pixmap,
1600     0,0,
1601     priv->drag_mouse_dx - EXTRA_BORDER, priv->drag_mouse_dy - EXTRA_BORDER,
1602     -1,-1);
1603    
1604     //Paint white outside of the map if dragging. Its less
1605     //ugly than painting the corrupted map
1606     if(priv->drag_mouse_dx>EXTRA_BORDER) {
1607     gdk_draw_rectangle (
1608     widget->window,
1609     widget->style->white_gc,
1610     TRUE,
1611     0, 0,
1612     priv->drag_mouse_dx - EXTRA_BORDER,
1613     widget->allocation.height);
1614     }
1615     else if (-priv->drag_mouse_dx > EXTRA_BORDER)
1616     {
1617     gdk_draw_rectangle (
1618     widget->window,
1619     widget->style->white_gc,
1620     TRUE,
1621     priv->drag_mouse_dx + widget->allocation.width + EXTRA_BORDER, 0,
1622     -priv->drag_mouse_dx - EXTRA_BORDER,
1623     widget->allocation.height);
1624     }
1625    
1626     if (priv->drag_mouse_dy>EXTRA_BORDER) {
1627     gdk_draw_rectangle (
1628     widget->window,
1629     widget->style->white_gc,
1630     TRUE,
1631     0, 0,
1632     widget->allocation.width,
1633     priv->drag_mouse_dy - EXTRA_BORDER);
1634     }
1635     else if (-priv->drag_mouse_dy > EXTRA_BORDER)
1636     {
1637     gdk_draw_rectangle (
1638     widget->window,
1639     widget->style->white_gc,
1640     TRUE,
1641     0, priv->drag_mouse_dy + widget->allocation.height + EXTRA_BORDER,
1642     widget->allocation.width,
1643     -priv->drag_mouse_dy - EXTRA_BORDER);
1644     }
1645    
1646     return FALSE;
1647     }
1648    
1649     static gboolean
1650     osm_gps_map_configure (GtkWidget *widget, GdkEventConfigure *event)
1651     {
1652     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1653    
1654     /* create pixmap */
1655     if (priv->pixmap)
1656     g_object_unref (priv->pixmap);
1657    
1658     priv->pixmap = gdk_pixmap_new (
1659     widget->window,
1660     widget->allocation.width + EXTRA_BORDER * 2,
1661     widget->allocation.height + EXTRA_BORDER * 2,
1662     -1);
1663    
1664     /* and gc, used for clipping (I think......) */
1665     if(priv->gc_map)
1666     g_object_unref(priv->gc_map);
1667    
1668     priv->gc_map = gdk_gc_new(priv->pixmap);
1669    
1670     osm_gps_map_map_redraw(OSM_GPS_MAP(widget));
1671    
1672     return FALSE;
1673     }
1674    
1675     static gboolean
1676     osm_gps_map_expose (GtkWidget *widget, GdkEventExpose *event)
1677     {
1678     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1679    
1680     gdk_draw_drawable (
1681     widget->window,
1682     widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1683     priv->pixmap,
1684     event->area.x + EXTRA_BORDER, event->area.y + EXTRA_BORDER,
1685     event->area.x, event->area.y,
1686     event->area.width, event->area.height);
1687    
1688     return FALSE;
1689     }
1690    
1691     static void
1692     osm_gps_map_class_init (OsmGpsMapClass *klass)
1693     {
1694     GObjectClass* object_class = G_OBJECT_CLASS (klass);
1695     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1696    
1697     g_type_class_add_private (klass, sizeof (OsmGpsMapPrivate));
1698    
1699     object_class->dispose = osm_gps_map_dispose;
1700     object_class->finalize = osm_gps_map_finalize;
1701     object_class->constructor = osm_gps_map_constructor;
1702     object_class->set_property = osm_gps_map_set_property;
1703     object_class->get_property = osm_gps_map_get_property;
1704    
1705     widget_class->expose_event = osm_gps_map_expose;
1706     widget_class->configure_event = osm_gps_map_configure;
1707     widget_class->button_press_event = osm_gps_map_button_press;
1708     widget_class->button_release_event = osm_gps_map_button_release;
1709     widget_class->motion_notify_event = osm_gps_map_motion_notify;
1710     widget_class->scroll_event = _osm_gps_map_scroll;
1711    
1712     g_object_class_install_property (object_class,
1713     PROP_AUTO_CENTER,
1714     g_param_spec_boolean ("auto-center",
1715     "auto center",
1716     "map auto center",
1717     TRUE,
1718     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1719    
1720     g_object_class_install_property (object_class,
1721     PROP_RECORD_TRIP_HISTORY,
1722     g_param_spec_boolean ("record-trip-history",
1723     "record trip history",
1724     "should all gps points be recorded in a trip history",
1725     TRUE,
1726     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1727    
1728     g_object_class_install_property (object_class,
1729     PROP_SHOW_TRIP_HISTORY,
1730     g_param_spec_boolean ("show-trip-history",
1731     "show trip history",
1732     "should the recorded trip history be shown on the map",
1733     TRUE,
1734     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1735    
1736     g_object_class_install_property (object_class,
1737     PROP_AUTO_DOWNLOAD,
1738     g_param_spec_boolean ("auto-download",
1739     "auto download",
1740     "map auto download",
1741     TRUE,
1742     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1743    
1744     g_object_class_install_property (object_class,
1745     PROP_REPO_URI,
1746     g_param_spec_string ("repo-uri",
1747     "repo uri",
1748     "osm repo uri",
1749     "http://tile.openstreetmap.org/#Z/#X/#Y.png",
1750     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1751    
1752     g_object_class_install_property (object_class,
1753     PROP_PROXY_URI,
1754     g_param_spec_string ("proxy-uri",
1755     "proxy uri",
1756     "http proxy uri on NULL",
1757     NULL,
1758     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1759    
1760     g_object_class_install_property (object_class,
1761     PROP_TILE_CACHE_DIR,
1762     g_param_spec_string ("tile-cache",
1763     "tile cache",
1764     "osm local tile cache dir",
1765     "/tmp/Maps",
1766     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1767    
1768     g_object_class_install_property (object_class,
1769     PROP_TILE_CACHE_DIR_IS_FULL_PATH,
1770     g_param_spec_boolean ("tile-cache-is-full-path",
1771     "tile cache is full path",
1772     "if true, the path passed to tile-cache is interpreted as the full cache path",
1773     FALSE,
1774     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1775    
1776     g_object_class_install_property (object_class,
1777     PROP_ZOOM,
1778     g_param_spec_int ("zoom",
1779     "zoom",
1780     "zoom level",
1781     MIN_ZOOM, /* minimum property value */
1782     MAX_ZOOM, /* maximum property value */
1783     3,
1784     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1785    
1786     g_object_class_install_property (object_class,
1787     PROP_MAX_ZOOM,
1788     g_param_spec_int ("max-zoom",
1789     "max zoom",
1790     "maximum zoom level",
1791     MIN_ZOOM, /* minimum property value */
1792     MAX_ZOOM, /* maximum property value */
1793     17,
1794     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1795    
1796     g_object_class_install_property (object_class,
1797     PROP_MIN_ZOOM,
1798     g_param_spec_int ("min-zoom",
1799     "min zoom",
1800     "minimum zoom level",
1801     MIN_ZOOM, /* minimum property value */
1802     MAX_ZOOM, /* maximum property value */
1803     1,
1804     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1805    
1806     g_object_class_install_property (object_class,
1807     PROP_LATITUDE,
1808     g_param_spec_float ("latitude",
1809     "latitude",
1810     "latitude in degrees",
1811     -90.0, /* minimum property value */
1812     90.0, /* maximum property value */
1813     0,
1814     G_PARAM_READABLE));
1815    
1816     g_object_class_install_property (object_class,
1817     PROP_LONGITUDE,
1818     g_param_spec_float ("longitude",
1819     "longitude",
1820     "longitude in degrees",
1821     -180.0, /* minimum property value */
1822     180.0, /* maximum property value */
1823     0,
1824     G_PARAM_READABLE));
1825    
1826     g_object_class_install_property (object_class,
1827     PROP_MAP_X,
1828     g_param_spec_int ("map-x",
1829     "map-x",
1830     "initial map x location",
1831     G_MININT, /* minimum property value */
1832     G_MAXINT, /* maximum property value */
1833     890,
1834     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1835    
1836     g_object_class_install_property (object_class,
1837     PROP_MAP_Y,
1838     g_param_spec_int ("map-y",
1839     "map-y",
1840     "initial map y location",
1841     G_MININT, /* minimum property value */
1842     G_MAXINT, /* maximum property value */
1843     515,
1844     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1845    
1846     g_object_class_install_property (object_class,
1847     PROP_TILES_QUEUED,
1848     g_param_spec_int ("tiles-queued",
1849     "tiles-queued",
1850     "number of tiles currently waiting to download",
1851     G_MININT, /* minimum property value */
1852     G_MAXINT, /* maximum property value */
1853     0,
1854     G_PARAM_READABLE));
1855    
1856     g_object_class_install_property (object_class,
1857     PROP_GPS_TRACK_WIDTH,
1858     g_param_spec_int ("gps-track-width",
1859     "gps-track-width",
1860     "width of the lines drawn for the gps track",
1861     1, /* minimum property value */
1862     G_MAXINT, /* maximum property value */
1863     4,
1864     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1865    
1866     g_object_class_install_property (object_class,
1867     PROP_GPS_POINT_R1,
1868     g_param_spec_int ("gps-track-point-radius",
1869     "gps-track-point-radius",
1870     "radius of the gps point inner circle",
1871     0, /* minimum property value */
1872     G_MAXINT, /* maximum property value */
1873     5,
1874     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1875    
1876     g_object_class_install_property (object_class,
1877     PROP_GPS_POINT_R2,
1878     g_param_spec_int ("gps-track-highlight-radius",
1879     "gps-track-highlight-radius",
1880     "radius of the gps point highlight circle",
1881     0, /* minimum property value */
1882     G_MAXINT, /* maximum property value */
1883     20,
1884     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1885    
1886     }
1887    
1888     void
1889     osm_gps_map_download_maps (OsmGpsMap *map, coord_t *pt1, coord_t *pt2, int zoom_start, int zoom_end)
1890     {
1891     int i,j,zoom,num_tiles;
1892     OsmGpsMapPrivate *priv = map->priv;
1893    
1894     if (pt1 && pt2)
1895     {
1896     gchar *filename;
1897     num_tiles = 0;
1898     zoom_end = CLAMP(zoom_end, priv->min_zoom, priv->max_zoom);
1899     g_debug("Download maps: z:%d->%d",zoom_start, zoom_end);
1900    
1901     for(zoom=zoom_start; zoom<=zoom_end; zoom++)
1902     {
1903     int x1,y1,x2,y2;
1904    
1905     x1 = (int)floor((float)lon2pixel(zoom, pt1->rlon) / (float)TILESIZE);
1906     y1 = (int)floor((float)lat2pixel(zoom, pt1->rlat) / (float)TILESIZE);
1907    
1908     x2 = (int)floor((float)lon2pixel(zoom, pt2->rlon) / (float)TILESIZE);
1909     y2 = (int)floor((float)lat2pixel(zoom, pt2->rlat) / (float)TILESIZE);
1910    
1911     // loop x1-x2
1912     for(i=x1; i<=x2; i++)
1913     {
1914     // loop y1 - y2
1915     for(j=y1; j<=y2; j++)
1916     {
1917     // x = i, y = j
1918     filename = g_strdup_printf("%s/%u/%u/%u.png", priv->cache_dir, zoom, i, j);
1919     #ifndef NO_DISK_CACHE
1920     if (!g_file_test(filename, G_FILE_TEST_EXISTS))
1921     #endif
1922     {
1923     osm_gps_map_download_tile(map, zoom, i, j, FALSE);
1924     num_tiles++;
1925     }
1926     g_free(filename);
1927     }
1928     }
1929     g_debug("DL @Z:%d = %d tiles",zoom,num_tiles);
1930     }
1931     }
1932     }
1933    
1934     void
1935     osm_gps_map_get_bbox (OsmGpsMap *map, coord_t *pt1, coord_t *pt2)
1936     {
1937     OsmGpsMapPrivate *priv = map->priv;
1938    
1939     if (pt1 && pt2) {
1940     pt1->rlat = pixel2lat(priv->map_zoom, priv->map_y);
1941     pt1->rlon = pixel2lon(priv->map_zoom, priv->map_x);
1942     pt2->rlat = pixel2lat(priv->map_zoom, priv->map_y + GTK_WIDGET(map)->allocation.height);
1943     pt2->rlon = pixel2lon(priv->map_zoom, priv->map_x + GTK_WIDGET(map)->allocation.width);
1944    
1945     g_debug("BBOX: %f %f %f %f", pt1->rlat, pt1->rlon, pt2->rlat, pt2->rlon);
1946     }
1947     }
1948    
1949     void
1950     osm_gps_map_set_mapcenter (OsmGpsMap *map, float latitude, float longitude, int zoom)
1951     {
1952     osm_gps_map_set_center (map, latitude, longitude);
1953     osm_gps_map_set_zoom (map, zoom);
1954     }
1955    
1956     void
1957     osm_gps_map_set_center (OsmGpsMap *map, float latitude, float longitude)
1958     {
1959     int pixel_x, pixel_y;
1960     OsmGpsMapPrivate *priv;
1961    
1962     g_return_if_fail (OSM_IS_GPS_MAP (map));
1963     priv = map->priv;
1964    
1965     priv->center_rlat = deg2rad(latitude);
1966     priv->center_rlon = deg2rad(longitude);
1967     priv->center_coord_set = TRUE;
1968    
1969     // pixel_x,y, offsets
1970     pixel_x = lon2pixel(priv->map_zoom, priv->center_rlon);
1971     pixel_y = lat2pixel(priv->map_zoom, priv->center_rlat);
1972    
1973     priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
1974     priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
1975    
1976     osm_gps_map_map_redraw_idle(map);
1977     }
1978    
1979     int
1980     osm_gps_map_set_zoom (OsmGpsMap *map, int zoom)
1981     {
1982     int zoom_old;
1983     double factor = 1.0;
1984     int width_center, height_center;
1985     OsmGpsMapPrivate *priv;
1986    
1987     g_return_val_if_fail (OSM_IS_GPS_MAP (map), 0);
1988     priv = map->priv;
1989    
1990     if (zoom != priv->map_zoom)
1991     {
1992     width_center = GTK_WIDGET(map)->allocation.width / 2;
1993     height_center = GTK_WIDGET(map)->allocation.height / 2;
1994    
1995     zoom_old = priv->map_zoom;
1996     //constrain zoom min_zoom -> max_zoom
1997     priv->map_zoom = CLAMP(zoom, priv->min_zoom, priv->max_zoom);
1998    
1999     if (priv->center_coord_set)
2000     {
2001     priv->map_x = lon2pixel(priv->map_zoom, priv->center_rlon) - width_center;
2002     priv->map_y = lat2pixel(priv->map_zoom, priv->center_rlat) - height_center;
2003     }
2004     else
2005     {
2006     factor = exp(priv->map_zoom * M_LN2)/exp(zoom_old * M_LN2);
2007     priv->map_x = ((priv->map_x + width_center) * factor) - width_center;
2008     priv->map_y = ((priv->map_y + height_center) * factor) - height_center;
2009     }
2010    
2011     g_debug("Zoom changed from %d to %d factor:%f x:%d",
2012     zoom_old, priv->map_zoom, factor, priv->map_x);
2013    
2014     osm_gps_map_map_redraw_idle(map);
2015     }
2016     return priv->map_zoom;
2017     }
2018    
2019     void
2020     osm_gps_map_add_track (OsmGpsMap *map, GSList *track)
2021     {
2022     OsmGpsMapPrivate *priv;
2023    
2024     g_return_if_fail (OSM_IS_GPS_MAP (map));
2025     priv = map->priv;
2026    
2027     if (track) {
2028     priv->tracks = g_slist_append(priv->tracks, track);
2029     osm_gps_map_map_redraw_idle(map);
2030     }
2031     }
2032    
2033     void
2034     osm_gps_map_clear_tracks (OsmGpsMap *map)
2035     {
2036     g_return_if_fail (OSM_IS_GPS_MAP (map));
2037    
2038     osm_gps_map_free_tracks(map);
2039     osm_gps_map_map_redraw_idle(map);
2040     }
2041    
2042     void
2043     osm_gps_map_add_image (OsmGpsMap *map, float latitude, float longitude, GdkPixbuf *image)
2044     {
2045     g_return_if_fail (OSM_IS_GPS_MAP (map));
2046    
2047     if (image) {
2048     OsmGpsMapPrivate *priv = map->priv;
2049     image_t *im;
2050    
2051     //cache w/h for speed, and add image to list
2052     im = g_new0(image_t,1);
2053     im->w = gdk_pixbuf_get_width(image);
2054     im->h = gdk_pixbuf_get_width(image);
2055     im->pt.rlat = deg2rad(latitude);
2056     im->pt.rlon = deg2rad(longitude);
2057    
2058     g_object_ref(image);
2059     im->image = image;
2060    
2061     priv->images = g_slist_append(priv->images, im);
2062    
2063     osm_gps_map_map_redraw_idle(map);
2064     }
2065     }
2066    
2067     void
2068     osm_gps_map_clear_images (OsmGpsMap *map)
2069     {
2070     g_return_if_fail (OSM_IS_GPS_MAP (map));
2071    
2072     osm_gps_map_free_images(map);
2073     osm_gps_map_map_redraw_idle(map);
2074     }
2075    
2076     void
2077     osm_gps_map_osd_speed (OsmGpsMap *map, float speed)
2078     {
2079     OsmGpsMapPrivate *priv;
2080    
2081     PangoContext *context = NULL;
2082     PangoLayout *layout = NULL;
2083     PangoFontDescription *desc = NULL;
2084    
2085     GdkColor color;
2086     GdkGC *gc;
2087    
2088     gchar *buffer;
2089     //static int x = 10, y = 10;
2090     static int width = 0, height = 0;
2091    
2092     g_return_if_fail (OSM_IS_GPS_MAP (map));
2093     priv = map->priv;
2094    
2095     buffer = g_strdup_printf("%.0f", speed);
2096    
2097     /* pango initialisation */
2098     context = gtk_widget_get_pango_context (GTK_WIDGET(map));
2099     layout = pango_layout_new (context);
2100     desc = pango_font_description_new();
2101    
2102     pango_font_description_set_size (desc, 40 * PANGO_SCALE);
2103     pango_layout_set_font_description (layout, desc);
2104     pango_layout_set_text (layout, buffer, strlen(buffer));
2105    
2106     gc = gdk_gc_new (GTK_WIDGET(map)->window);
2107    
2108     color.red = (0 > 50) ? 0xffff : 0;
2109     color.green = 0;
2110     color.blue = 0;
2111    
2112     gdk_gc_set_rgb_fg_color (gc, &color);
2113    
2114     /* faster / less flicker alternative:*/
2115     gdk_draw_drawable (
2116     GTK_WIDGET(map)->window,
2117     GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(map)],
2118     priv->pixmap,
2119     0,0,
2120     0,0,
2121     width+10,width+10);
2122    
2123     gdk_draw_layout(GTK_WIDGET(map)->window,
2124     gc,
2125     0, 0,
2126     layout);
2127    
2128     /* set width and height */
2129     pango_layout_get_pixel_size(layout, &width, &height);
2130    
2131     g_free(buffer);
2132     pango_font_description_free (desc);
2133     g_object_unref (layout);
2134     g_object_unref (gc);
2135     }
2136    
2137     void
2138     osm_gps_map_draw_gps (OsmGpsMap *map, float latitude, float longitude, float heading)
2139     {
2140     int pixel_x, pixel_y;
2141     OsmGpsMapPrivate *priv;
2142    
2143     g_return_if_fail (OSM_IS_GPS_MAP (map));
2144     priv = map->priv;
2145    
2146     priv->gps->rlat = deg2rad(latitude);
2147     priv->gps->rlon = deg2rad(longitude);
2148     priv->gps_valid = TRUE;
2149    
2150     // pixel_x,y, offsets
2151     pixel_x = lon2pixel(priv->map_zoom, priv->gps->rlon);
2152     pixel_y = lat2pixel(priv->map_zoom, priv->gps->rlat);
2153    
2154     //If trip marker add to list of gps points.
2155     if (priv->record_trip_history) {
2156     coord_t *tp = g_new0(coord_t,1);
2157     tp->rlat = priv->gps->rlat;
2158     tp->rlon = priv->gps->rlon;
2159     priv->trip_history = g_slist_append(priv->trip_history, tp);
2160     }
2161    
2162     // dont draw anything if we are dragging
2163     if (priv->dragging) {
2164     g_debug("Dragging");
2165     return;
2166     }
2167    
2168     //Automatically center the map if the track approaches the edge
2169     if(priv->map_auto_center) {
2170     int x = pixel_x - priv->map_x;
2171     int y = pixel_y - priv->map_y;
2172     int width = GTK_WIDGET(map)->allocation.width;
2173     int height = GTK_WIDGET(map)->allocation.height;
2174     if( x < (width/2 - width/8) || x > (width/2 + width/8) ||
2175     y < (height/2 - height/8) || y > (height/2 + height/8)) {
2176    
2177     priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
2178     priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
2179     priv->center_coord_set = FALSE;
2180     }
2181     }
2182    
2183     // this redraws the map (including the gps track, and adjusts the
2184     // map center if it was changed
2185     osm_gps_map_map_redraw_idle(map);
2186     }
2187    
2188     void
2189     osm_gps_map_clear_gps (OsmGpsMap *map)
2190     {
2191     osm_gps_map_free_trip(map);
2192     osm_gps_map_map_redraw_idle(map);
2193     }
2194    
2195     coord_t
2196     osm_gps_map_get_co_ordinates (OsmGpsMap *map, int pixel_x, int pixel_y)
2197     {
2198     coord_t coord;
2199     OsmGpsMapPrivate *priv = map->priv;
2200    
2201     coord.rlat = pixel2lat(priv->map_zoom, priv->map_y + pixel_y);
2202     coord.rlon = pixel2lon(priv->map_zoom, priv->map_x + pixel_x);
2203     return coord;
2204     }
2205    
2206     GtkWidget *
2207     osm_gps_map_new (void)
2208     {
2209     return g_object_new (OSM_TYPE_GPS_MAP, NULL);
2210     }
2211    
2212     void
2213     osm_gps_map_screen_to_geographic (OsmGpsMap *map, gint pixel_x, gint pixel_y,
2214     gfloat *latitude, gfloat *longitude)
2215     {
2216     OsmGpsMapPrivate *priv;
2217    
2218     g_return_if_fail (OSM_IS_GPS_MAP (map));
2219     priv = map->priv;
2220    
2221     if (latitude)
2222     *latitude = rad2deg(pixel2lat(priv->map_zoom, priv->map_y + pixel_y));
2223     if (longitude)
2224     *longitude = rad2deg(pixel2lon(priv->map_zoom, priv->map_x + pixel_x));
2225     }
2226    
2227     void
2228     osm_gps_map_geographic_to_screen (OsmGpsMap *map,
2229     gfloat latitude, gfloat longitude,
2230     gint *pixel_x, gint *pixel_y)
2231     {
2232     OsmGpsMapPrivate *priv;
2233    
2234     g_return_if_fail (OSM_IS_GPS_MAP (map));
2235     priv = map->priv;
2236    
2237     if (pixel_x)
2238     *pixel_x = lon2pixel(priv->map_zoom, deg2rad(longitude)) - priv->map_x;
2239     if (pixel_y)
2240     *pixel_y = lat2pixel(priv->map_zoom, deg2rad(latitude)) - priv->map_y;
2241     }
2242    
2243     void
2244     osm_gps_map_scroll (OsmGpsMap *map, gint dx, gint dy)
2245     {
2246     OsmGpsMapPrivate *priv;
2247    
2248     g_return_if_fail (OSM_IS_GPS_MAP (map));
2249     priv = map->priv;
2250    
2251     priv->center_coord_set = FALSE;
2252     priv->map_x += dx;
2253     priv->map_y += dy;
2254    
2255     osm_gps_map_map_redraw_idle (map);
2256     }
2257