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

Parent Directory Parent Directory | Revision Log Revision Log


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