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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 58 - (hide annotations)
Mon Aug 17 10:51:56 2009 UTC (14 years, 8 months ago) by harbaum
File MIME type: text/plain
File size: 96404 byte(s)
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     printf("clocked at %d %d\n", x, y);
820    
821     if (!priv->balloon.valid)
822     return;
823    
824     /* check if the close button was clicked */
825     if ((x > priv->balloon.rect.w - 2*CLOSE_BUTTON_RADIUS) &&
826     (x < priv->balloon.rect.w) &&
827     (y > 0) && (y < 2*CLOSE_BUTTON_RADIUS)) {
828    
829     priv->balloon.valid = FALSE;
830     osm_gps_map_map_redraw_idle(map);
831     }
832     }
833    
834     /* return true if balloon is being displayed and if */
835     /* the given coordinate is within this balloon */
836     static gboolean
837     osm_gps_map_in_balloon(OsmGpsMapPrivate *priv, gint x, gint y)
838     {
839     return (priv->balloon.valid &&
840     (x > priv->balloon.rect.x) &&
841     (x < priv->balloon.rect.x + priv->balloon.rect.w) &&
842     (y > priv->balloon.rect.y) &&
843     (y < priv->balloon.rect.y + priv->balloon.rect.h));
844     }
845    
846 harbaum 56 static void
847 harbaum 32 osm_gps_map_blit_tile(OsmGpsMap *map, GdkPixbuf *pixbuf, int offset_x, int offset_y)
848     {
849     OsmGpsMapPrivate *priv = map->priv;
850    
851 harbaum 55 g_debug("Queing redraw @ %d,%d (w:%d h:%d)", offset_x,offset_y, TILESIZE,TILESIZE);
852 harbaum 32
853     /* draw pixbuf onto pixmap */
854     gdk_draw_pixbuf (priv->pixmap,
855     priv->gc_map,
856     pixbuf,
857     0,0,
858     offset_x,offset_y,
859     TILESIZE,TILESIZE,
860     GDK_RGB_DITHER_NONE, 0, 0);
861     }
862    
863     /* libsoup-2.2 and libsoup-2.4 use different ways to store the body data */
864     #ifdef LIBSOUP22
865     #define soup_message_headers_append(a,b,c) soup_message_add_header(a,b,c)
866     #define MSG_RESPONSE_BODY(a) ((a)->response.body)
867     #define MSG_RESPONSE_LEN(a) ((a)->response.length)
868     #define MSG_RESPONSE_LEN_FORMAT "%u"
869     #else
870     #define MSG_RESPONSE_BODY(a) ((a)->response_body->data)
871     #define MSG_RESPONSE_LEN(a) ((a)->response_body->length)
872     #define MSG_RESPONSE_LEN_FORMAT "%lld"
873     #endif
874    
875     #ifdef LIBSOUP22
876     static void
877     osm_gps_map_tile_download_complete (SoupMessage *msg, gpointer user_data)
878     #else
879     static void
880     osm_gps_map_tile_download_complete (SoupSession *session, SoupMessage *msg, gpointer user_data)
881     #endif
882     {
883 harbaum 55 FILE *file;
884 harbaum 32 tile_download_t *dl = (tile_download_t *)user_data;
885     OsmGpsMap *map = OSM_GPS_MAP(dl->map);
886     OsmGpsMapPrivate *priv = map->priv;
887     gboolean file_saved = FALSE;
888    
889     if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
890     {
891     /* save tile into cachedir if one has been specified */
892     if (priv->cache_dir)
893     {
894     if (g_mkdir_with_parents(dl->folder,0700) == 0)
895     {
896 harbaum 55 file = g_fopen(dl->filename, "wb");
897     if (file != NULL)
898 harbaum 32 {
899 harbaum 55 fwrite (MSG_RESPONSE_BODY(msg), 1, MSG_RESPONSE_LEN(msg), file);
900 harbaum 32 file_saved = TRUE;
901 harbaum 55 g_debug("Wrote "MSG_RESPONSE_LEN_FORMAT" bytes to %s", MSG_RESPONSE_LEN(msg), dl->filename);
902     fclose (file);
903 harbaum 32
904     }
905     }
906     else
907     {
908     g_warning("Error creating tile download directory: %s", dl->folder);
909     }
910     }
911    
912     if (dl->redraw)
913     {
914     GdkPixbuf *pixbuf = NULL;
915    
916 harbaum 55 /* if the file was actually stored on disk, we can simply */
917     /* load and decode it from that file */
918 harbaum 32 if (priv->cache_dir)
919     {
920     if (file_saved)
921     {
922     pixbuf = gdk_pixbuf_new_from_file (dl->filename, NULL);
923     }
924     }
925     else
926     {
927     GdkPixbufLoader *loader;
928     char *extension = strrchr (dl->filename, '.');
929    
930     /* parse file directly from memory */
931     if (extension)
932     {
933     loader = gdk_pixbuf_loader_new_with_type (extension+1, NULL);
934     if (!gdk_pixbuf_loader_write (loader, (unsigned char*)MSG_RESPONSE_BODY(msg), MSG_RESPONSE_LEN(msg), NULL))
935     {
936     g_warning("Error: Decoding of image failed");
937     }
938     gdk_pixbuf_loader_close(loader, NULL);
939    
940     pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
941    
942     /* give up loader but keep the pixbuf */
943     g_object_ref(pixbuf);
944     g_object_unref(loader);
945     }
946     else
947     {
948     g_warning("Error: Unable to determine image file format");
949     }
950     }
951 harbaum 55
952 harbaum 32 /* Store the tile into the cache */
953     if (G_LIKELY (pixbuf))
954     {
955     OsmCachedTile *tile = g_slice_new (OsmCachedTile);
956     tile->pixbuf = pixbuf;
957     tile->redraw_cycle = priv->redraw_cycle;
958     /* if the tile is already in the cache (it could be one
959     * rendered from another zoom level), it will be
960     * overwritten */
961     g_hash_table_insert (priv->tile_cache, dl->filename, tile);
962     /* NULL-ify dl->filename so that it won't be freed, as
963     * we are using it as a key in the hash table */
964     dl->filename = NULL;
965     }
966     osm_gps_map_map_redraw_idle (map);
967     }
968     g_hash_table_remove(priv->tile_queue, dl->uri);
969    
970     g_free(dl->uri);
971     g_free(dl->folder);
972     g_free(dl->filename);
973     g_free(dl);
974     }
975     else
976     {
977     g_warning("Error downloading tile: %d - %s", msg->status_code, msg->reason_phrase);
978     if (msg->status_code == SOUP_STATUS_NOT_FOUND)
979     {
980     g_hash_table_insert(priv->missing_tiles, dl->uri, NULL);
981     g_hash_table_remove(priv->tile_queue, dl->uri);
982     }
983     else if (msg->status_code == SOUP_STATUS_CANCELLED)
984     {
985     ;//application exiting
986     }
987     else
988     {
989     #ifdef LIBSOUP22
990     soup_session_requeue_message(dl->session, msg);
991     #else
992     soup_session_requeue_message(session, msg);
993     #endif
994     return;
995     }
996     }
997    
998    
999     }
1000    
1001     static void
1002     osm_gps_map_download_tile (OsmGpsMap *map, int zoom, int x, int y, gboolean redraw)
1003     {
1004     SoupMessage *msg;
1005     OsmGpsMapPrivate *priv = map->priv;
1006     tile_download_t *dl = g_new0(tile_download_t,1);
1007    
1008     //calculate the uri to download
1009     dl->uri = replace_map_uri(map, priv->repo_uri, zoom, x, y);
1010    
1011     #ifdef LIBSOUP22
1012     dl->session = priv->soup_session;
1013     #endif
1014    
1015     //check the tile has not already been queued for download,
1016     //or has been attempted, and its missing
1017     if (g_hash_table_lookup_extended(priv->tile_queue, dl->uri, NULL, NULL) ||
1018     g_hash_table_lookup_extended(priv->missing_tiles, dl->uri, NULL, NULL) )
1019     {
1020     g_debug("Tile already downloading (or missing)");
1021     g_free(dl->uri);
1022     g_free(dl);
1023     } else {
1024 harbaum 55 dl->folder = g_strdup_printf("%s%c%d%c%d%c",
1025     priv->cache_dir, G_DIR_SEPARATOR,
1026     zoom, G_DIR_SEPARATOR,
1027     x, G_DIR_SEPARATOR);
1028     dl->filename = g_strdup_printf("%s%c%d%c%d%c%d.%s",
1029     priv->cache_dir, G_DIR_SEPARATOR,
1030     zoom, G_DIR_SEPARATOR,
1031     x, G_DIR_SEPARATOR,
1032     y,
1033     priv->image_format);
1034 harbaum 32 dl->map = map;
1035     dl->redraw = redraw;
1036    
1037     g_debug("Download tile: %d,%d z:%d\n\t%s --> %s", x, y, zoom, dl->uri, dl->filename);
1038    
1039     msg = soup_message_new (SOUP_METHOD_GET, dl->uri);
1040     if (msg) {
1041     if (priv->the_google) {
1042     //Set maps.google.com as the referrer
1043     g_debug("Setting Google Referrer");
1044     soup_message_headers_append(msg->request_headers, "Referer", "http://maps.google.com/");
1045     //For google satelite also set the appropriate cookie value
1046     if (priv->uri_format & URI_HAS_Q) {
1047     const char *cookie = g_getenv("GOOGLE_COOKIE");
1048     if (cookie) {
1049     g_debug("Adding Google Cookie");
1050     soup_message_headers_append(msg->request_headers, "Cookie", cookie);
1051     }
1052     }
1053     }
1054    
1055     g_hash_table_insert (priv->tile_queue, dl->uri, msg);
1056     soup_session_queue_message (priv->soup_session, msg, osm_gps_map_tile_download_complete, dl);
1057     } else {
1058     g_warning("Could not create soup message");
1059     g_free(dl->uri);
1060     g_free(dl->folder);
1061     g_free(dl->filename);
1062     g_free(dl);
1063     }
1064     }
1065     }
1066    
1067     static GdkPixbuf *
1068     osm_gps_map_load_cached_tile (OsmGpsMap *map, int zoom, int x, int y)
1069     {
1070     OsmGpsMapPrivate *priv = map->priv;
1071     gchar *filename;
1072 harbaum 55 GdkPixbuf *pixbuf = NULL;
1073 harbaum 32 OsmCachedTile *tile;
1074    
1075 harbaum 55 filename = g_strdup_printf("%s%c%d%c%d%c%d.%s",
1076     priv->cache_dir, G_DIR_SEPARATOR,
1077     zoom, G_DIR_SEPARATOR,
1078     x, G_DIR_SEPARATOR,
1079     y,
1080     priv->image_format);
1081 harbaum 32
1082     tile = g_hash_table_lookup (priv->tile_cache, filename);
1083     if (tile)
1084     {
1085     g_free (filename);
1086     }
1087 harbaum 55 else
1088 harbaum 32 {
1089     pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
1090     if (pixbuf)
1091     {
1092     tile = g_slice_new (OsmCachedTile);
1093     tile->pixbuf = pixbuf;
1094     g_hash_table_insert (priv->tile_cache, filename, tile);
1095     }
1096     }
1097    
1098     /* set/update the redraw_cycle timestamp on the tile */
1099     if (tile)
1100     {
1101     tile->redraw_cycle = priv->redraw_cycle;
1102     pixbuf = g_object_ref (tile->pixbuf);
1103 harbaum 55 }
1104 harbaum 32
1105     return pixbuf;
1106     }
1107    
1108     static GdkPixbuf *
1109     osm_gps_map_find_bigger_tile (OsmGpsMap *map, int zoom, int x, int y,
1110     int *zoom_found)
1111     {
1112     GdkPixbuf *pixbuf;
1113     int next_zoom, next_x, next_y;
1114    
1115     if (zoom == 0) return NULL;
1116     next_zoom = zoom - 1;
1117     next_x = x / 2;
1118     next_y = y / 2;
1119     pixbuf = osm_gps_map_load_cached_tile (map, next_zoom, next_x, next_y);
1120     if (pixbuf)
1121     *zoom_found = next_zoom;
1122     else
1123     pixbuf = osm_gps_map_find_bigger_tile (map, next_zoom, next_x, next_y,
1124     zoom_found);
1125     return pixbuf;
1126     }
1127    
1128     static GdkPixbuf *
1129     osm_gps_map_render_missing_tile_upscaled (OsmGpsMap *map, int zoom,
1130     int x, int y)
1131     {
1132     GdkPixbuf *pixbuf, *big, *area;
1133     int zoom_big, zoom_diff, area_size, area_x, area_y;
1134     int modulo;
1135    
1136     big = osm_gps_map_find_bigger_tile (map, zoom, x, y, &zoom_big);
1137     if (!big) return NULL;
1138    
1139     g_debug ("Found bigger tile (zoom = %d, wanted = %d)", zoom_big, zoom);
1140    
1141     /* get a Pixbuf for the area to magnify */
1142     zoom_diff = zoom - zoom_big;
1143     area_size = TILESIZE >> zoom_diff;
1144     modulo = 1 << zoom_diff;
1145     area_x = (x % modulo) * area_size;
1146     area_y = (y % modulo) * area_size;
1147     area = gdk_pixbuf_new_subpixbuf (big, area_x, area_y,
1148     area_size, area_size);
1149     g_object_unref (big);
1150     pixbuf = gdk_pixbuf_scale_simple (area, TILESIZE, TILESIZE,
1151     GDK_INTERP_NEAREST);
1152     g_object_unref (area);
1153     return pixbuf;
1154     }
1155    
1156     static GdkPixbuf *
1157     osm_gps_map_render_missing_tile (OsmGpsMap *map, int zoom, int x, int y)
1158     {
1159     /* maybe TODO: render from downscaled tiles, if the following fails */
1160     return osm_gps_map_render_missing_tile_upscaled (map, zoom, x, y);
1161     }
1162    
1163     static void
1164     osm_gps_map_load_tile (OsmGpsMap *map, int zoom, int x, int y, int offset_x, int offset_y)
1165     {
1166     OsmGpsMapPrivate *priv = map->priv;
1167 harbaum 55 gchar *filename;
1168 harbaum 32 GdkPixbuf *pixbuf;
1169    
1170     g_debug("Load tile %d,%d (%d,%d) z:%d", x, y, offset_x, offset_y, zoom);
1171    
1172 harbaum 55 if (priv->map_source == OSM_GPS_MAP_SOURCE_NULL) {
1173     osm_gps_map_blit_tile(map, priv->null_tile, offset_x,offset_y);
1174     return;
1175     }
1176 harbaum 32
1177 harbaum 55 filename = g_strdup_printf("%s%c%d%c%d%c%d.%s",
1178     priv->cache_dir, G_DIR_SEPARATOR,
1179     zoom, G_DIR_SEPARATOR,
1180     x, G_DIR_SEPARATOR,
1181     y,
1182     priv->image_format);
1183    
1184     /* try to get file from internal cache first */
1185     if(!(pixbuf = osm_gps_map_load_cached_tile(map, zoom, x, y)))
1186 harbaum 32 pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
1187    
1188 harbaum 55 if(pixbuf)
1189 harbaum 32 {
1190 harbaum 55 g_debug("Found tile %s", filename);
1191 harbaum 32 osm_gps_map_blit_tile(map, pixbuf, offset_x,offset_y);
1192     g_object_unref (pixbuf);
1193     }
1194     else
1195     {
1196     if (priv->map_auto_download)
1197     osm_gps_map_download_tile(map, zoom, x, y, TRUE);
1198    
1199     /* try to render the tile by scaling cached tiles from other zoom
1200     * levels */
1201     pixbuf = osm_gps_map_render_missing_tile (map, zoom, x, y);
1202     if (pixbuf)
1203     {
1204     gdk_draw_pixbuf (priv->pixmap,
1205     priv->gc_map,
1206     pixbuf,
1207     0,0,
1208     offset_x,offset_y,
1209     TILESIZE,TILESIZE,
1210     GDK_RGB_DITHER_NONE, 0, 0);
1211     g_object_unref (pixbuf);
1212     }
1213     else
1214     {
1215     //prevent some artifacts when drawing not yet loaded areas.
1216     gdk_draw_rectangle (priv->pixmap,
1217     GTK_WIDGET(map)->style->white_gc,
1218     TRUE, offset_x, offset_y, TILESIZE, TILESIZE);
1219     }
1220     }
1221 harbaum 55 g_free(filename);
1222 harbaum 32 }
1223    
1224     static void
1225     osm_gps_map_fill_tiles_pixel (OsmGpsMap *map)
1226     {
1227     OsmGpsMapPrivate *priv = map->priv;
1228     int i,j, width, height, tile_x0, tile_y0, tiles_nx, tiles_ny;
1229     int offset_xn = 0;
1230     int offset_yn = 0;
1231     int offset_x;
1232     int offset_y;
1233    
1234     g_debug("Fill tiles: %d,%d z:%d", priv->map_x, priv->map_y, priv->map_zoom);
1235    
1236     offset_x = - priv->map_x % TILESIZE;
1237     offset_y = - priv->map_y % TILESIZE;
1238     if (offset_x > 0) offset_x -= TILESIZE;
1239     if (offset_y > 0) offset_y -= TILESIZE;
1240    
1241     offset_xn = offset_x + EXTRA_BORDER;
1242     offset_yn = offset_y + EXTRA_BORDER;
1243    
1244     width = GTK_WIDGET(map)->allocation.width;
1245     height = GTK_WIDGET(map)->allocation.height;
1246    
1247     tiles_nx = (width - offset_x) / TILESIZE + 1;
1248     tiles_ny = (height - offset_y) / TILESIZE + 1;
1249    
1250     tile_x0 = floor((float)priv->map_x / (float)TILESIZE);
1251     tile_y0 = floor((float)priv->map_y / (float)TILESIZE);
1252    
1253     //TODO: implement wrap around
1254     for (i=tile_x0; i<(tile_x0+tiles_nx);i++)
1255     {
1256     for (j=tile_y0; j<(tile_y0+tiles_ny); j++)
1257     {
1258     if( j<0 || i<0 || i>=exp(priv->map_zoom * M_LN2) || j>=exp(priv->map_zoom * M_LN2))
1259     {
1260     gdk_draw_rectangle (priv->pixmap,
1261     GTK_WIDGET(map)->style->white_gc,
1262     TRUE,
1263     offset_xn, offset_yn,
1264     TILESIZE,TILESIZE);
1265     }
1266     else
1267     {
1268     osm_gps_map_load_tile(map,
1269     priv->map_zoom,
1270     i,j,
1271     offset_xn,offset_yn);
1272     }
1273     offset_yn += TILESIZE;
1274     }
1275     offset_xn += TILESIZE;
1276     offset_yn = offset_y + EXTRA_BORDER;
1277     }
1278     }
1279    
1280     static void
1281     osm_gps_map_print_track (OsmGpsMap *map, GSList *trackpoint_list)
1282     {
1283     OsmGpsMapPrivate *priv = map->priv;
1284    
1285     GSList *list;
1286     int x,y;
1287     int min_x = 0,min_y = 0,max_x = 0,max_y = 0;
1288     int lw = priv->ui_gps_track_width;
1289     int map_x0, map_y0;
1290     #ifdef USE_CAIRO
1291     cairo_t *cr;
1292     #else
1293     int last_x = 0, last_y = 0;
1294     GdkColor color;
1295     GdkGC *gc;
1296     #endif
1297    
1298     #ifdef USE_CAIRO
1299     cr = gdk_cairo_create(priv->pixmap);
1300     cairo_set_line_width (cr, lw);
1301     cairo_set_source_rgba (cr, 60000.0/65535.0, 0.0, 0.0, 0.6);
1302     cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1303     cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
1304     #else
1305     gc = gdk_gc_new(priv->pixmap);
1306     color.green = 0;
1307     color.blue = 0;
1308     color.red = 60000;
1309     gdk_gc_set_rgb_fg_color(gc, &color);
1310     gdk_gc_set_line_attributes(gc, lw, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
1311     #endif
1312    
1313     map_x0 = priv->map_x - EXTRA_BORDER;
1314     map_y0 = priv->map_y - EXTRA_BORDER;
1315     for(list = trackpoint_list; list != NULL; list = list->next)
1316     {
1317     coord_t *tp = list->data;
1318    
1319     x = lon2pixel(priv->map_zoom, tp->rlon) - map_x0;
1320     y = lat2pixel(priv->map_zoom, tp->rlat) - map_y0;
1321    
1322     // first time through loop
1323     if (list == trackpoint_list) {
1324     #ifdef USE_CAIRO
1325     cairo_move_to(cr, x, y);
1326     #else
1327     last_x = x;
1328     last_y = y;
1329     #endif
1330     }
1331    
1332     #ifdef USE_CAIRO
1333     cairo_line_to(cr, x, y);
1334     #else
1335     gdk_draw_line (priv->pixmap, gc, x, y, last_x, last_y);
1336     last_x = x;
1337     last_y = y;
1338     #endif
1339    
1340     max_x = MAX(x,max_x);
1341     min_x = MIN(x,min_x);
1342     max_y = MAX(y,max_y);
1343     min_y = MIN(y,min_y);
1344     }
1345    
1346     gtk_widget_queue_draw_area (
1347     GTK_WIDGET(map),
1348     min_x - lw,
1349     min_y - lw,
1350     max_x + (lw * 2),
1351     max_y + (lw * 2));
1352    
1353     #ifdef USE_CAIRO
1354     cairo_stroke(cr);
1355     cairo_destroy(cr);
1356     #else
1357     g_object_unref(gc);
1358     #endif
1359     }
1360    
1361     /* Prints the gps trip history, and any other tracks */
1362     static void
1363     osm_gps_map_print_tracks (OsmGpsMap *map)
1364     {
1365     OsmGpsMapPrivate *priv = map->priv;
1366    
1367     if (priv->show_trip_history)
1368     osm_gps_map_print_track (map, priv->trip_history);
1369 harbaum 55
1370 harbaum 32 if (priv->tracks)
1371     {
1372     GSList* tmp = priv->tracks;
1373     while (tmp != NULL)
1374     {
1375     osm_gps_map_print_track (map, tmp->data);
1376     tmp = g_slist_next(tmp);
1377     }
1378     }
1379     }
1380    
1381 harbaum 55 static gboolean
1382     osm_gps_map_purge_cache_check(gpointer key, gpointer value, gpointer user)
1383 harbaum 32 {
1384 harbaum 55 return (((OsmCachedTile*)value)->redraw_cycle != ((OsmGpsMapPrivate*)user)->redraw_cycle);
1385 harbaum 32 }
1386    
1387     static void
1388     osm_gps_map_purge_cache (OsmGpsMap *map)
1389     {
1390 harbaum 55 OsmGpsMapPrivate *priv = map->priv;
1391 harbaum 32
1392 harbaum 55 if (g_hash_table_size (priv->tile_cache) < priv->max_tile_cache_size)
1393     return;
1394 harbaum 32
1395 harbaum 55 /* run through the cache, and remove the tiles which have not been used
1396     * during the last redraw operation */
1397     g_hash_table_foreach_remove(priv->tile_cache, osm_gps_map_purge_cache_check, priv);
1398 harbaum 32 }
1399    
1400     static gboolean
1401     osm_gps_map_map_redraw (OsmGpsMap *map)
1402     {
1403     OsmGpsMapPrivate *priv = map->priv;
1404    
1405     priv->idle_map_redraw = 0;
1406    
1407     /* the motion_notify handler uses priv->pixmap to redraw the area; if we
1408     * change it while we are dragging, we will end up showing it in the wrong
1409     * place. This could be fixed by carefully recompute the coordinates, but
1410     * for now it's easier just to disable redrawing the map while dragging */
1411     if (priv->dragging)
1412     return FALSE;
1413    
1414     priv->redraw_cycle++;
1415    
1416     /* draw white background to initialise pixmap */
1417     gdk_draw_rectangle (
1418     priv->pixmap,
1419     GTK_WIDGET(map)->style->white_gc,
1420     TRUE,
1421     0, 0,
1422     GTK_WIDGET(map)->allocation.width + EXTRA_BORDER * 2,
1423     GTK_WIDGET(map)->allocation.height + EXTRA_BORDER * 2);
1424    
1425     osm_gps_map_fill_tiles_pixel(map);
1426    
1427     osm_gps_map_print_tracks(map);
1428     osm_gps_map_draw_gps_point(map);
1429     osm_gps_map_print_images(map);
1430 harbaum 57 osm_gps_map_draw_balloon_int(map);
1431 harbaum 32
1432     //osm_gps_map_osd_speed(map, 1.5);
1433     osm_gps_map_purge_cache(map);
1434     gtk_widget_queue_draw (GTK_WIDGET (map));
1435    
1436     return FALSE;
1437     }
1438    
1439     static void
1440     osm_gps_map_map_redraw_idle (OsmGpsMap *map)
1441     {
1442     OsmGpsMapPrivate *priv = map->priv;
1443    
1444     if (priv->idle_map_redraw == 0)
1445     priv->idle_map_redraw = g_idle_add ((GSourceFunc)osm_gps_map_map_redraw, map);
1446     }
1447    
1448     static void
1449     osm_gps_map_init (OsmGpsMap *object)
1450     {
1451     OsmGpsMapPrivate *priv;
1452    
1453     priv = G_TYPE_INSTANCE_GET_PRIVATE (object, OSM_TYPE_GPS_MAP, OsmGpsMapPrivate);
1454     object->priv = priv;
1455    
1456     priv->pixmap = NULL;
1457    
1458     priv->trip_history = NULL;
1459     priv->gps = g_new0(coord_t, 1);
1460     priv->gps_valid = FALSE;
1461    
1462 harbaum 58 priv->balloon.coo = g_new0(coord_t, 1);
1463     priv->balloon.valid = FALSE;
1464     priv->balloon.cb = NULL;
1465 harbaum 57
1466 harbaum 32 priv->tracks = NULL;
1467     priv->images = NULL;
1468    
1469     priv->drag_counter = 0;
1470     priv->drag_mouse_dx = 0;
1471     priv->drag_mouse_dy = 0;
1472     priv->drag_start_mouse_x = 0;
1473     priv->drag_start_mouse_y = 0;
1474    
1475     priv->uri_format = 0;
1476     priv->the_google = FALSE;
1477    
1478 harbaum 55 priv->map_source = -1;
1479    
1480 harbaum 32 #ifndef LIBSOUP22
1481     //Change naumber of concurrent connections option?
1482     priv->soup_session = soup_session_async_new_with_options(
1483     SOUP_SESSION_USER_AGENT,
1484     "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11",
1485     NULL);
1486     #else
1487     /* libsoup-2.2 seems not to be able to set the user agent */
1488     priv->soup_session = soup_session_async_new();
1489     #endif
1490    
1491     //Hash table which maps tile d/l URIs to SoupMessage requests
1492     priv->tile_queue = g_hash_table_new (g_str_hash, g_str_equal);
1493    
1494     //Some mapping providers (Google) have varying degrees of tiles at multiple
1495     //zoom levels
1496     priv->missing_tiles = g_hash_table_new (g_str_hash, g_str_equal);
1497    
1498     /* memory cache for most recently used tiles */
1499     priv->tile_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
1500     g_free, (GDestroyNotify)cached_tile_free);
1501     priv->max_tile_cache_size = 20;
1502    
1503     gtk_widget_add_events (GTK_WIDGET (object),
1504     GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1505     GDK_POINTER_MOTION_MASK |
1506     GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
1507     GTK_WIDGET_SET_FLAGS (object, GTK_CAN_FOCUS);
1508    
1509     g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, my_log_handler, NULL);
1510     }
1511    
1512     #ifndef G_CHECKSUM_MD5
1513     /* simple hash algorithm hack if md5 is not present */
1514     static char *simple_hash(char *str) {
1515     union {
1516     char str[4];
1517     gulong val;
1518     } hash = { .val = 0x55555555 };
1519    
1520     while(*str) {
1521     hash.str[(int)str & 3] ^= *str;
1522     str++;
1523     }
1524     return g_strdup_printf("%08lX", hash.val);
1525     }
1526     #endif
1527    
1528     static GObject *
1529     osm_gps_map_constructor (GType gtype, guint n_properties, GObjectConstructParam *properties)
1530     {
1531     GObject *object;
1532     OsmGpsMapPrivate *priv;
1533 harbaum 55 OsmGpsMap *map;
1534     const char *uri;
1535 harbaum 32
1536     //Always chain up to the parent constructor
1537     object = G_OBJECT_CLASS(osm_gps_map_parent_class)->constructor(gtype, n_properties, properties);
1538 harbaum 55 map = OSM_GPS_MAP(object);
1539 harbaum 32 priv = OSM_GPS_MAP_PRIVATE(object);
1540    
1541 harbaum 55 //user can specify a map source ID, or a repo URI as the map source
1542     uri = osm_gps_map_source_get_repo_uri(OSM_GPS_MAP_SOURCE_NULL);
1543     if ( (priv->map_source == 0) || (strcmp(priv->repo_uri, uri) == 0) ) {
1544     g_debug("Using null source");
1545     priv->map_source = OSM_GPS_MAP_SOURCE_NULL;
1546    
1547     priv->null_tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 256, 256);
1548     gdk_pixbuf_fill(priv->null_tile, 0xcccccc00);
1549     }
1550     else if (priv->map_source >= 0) {
1551     //check if the source given is valid
1552     uri = osm_gps_map_source_get_repo_uri(priv->map_source);
1553     if (uri) {
1554     g_debug("Setting map source from ID");
1555     g_free(priv->repo_uri);
1556    
1557     priv->repo_uri = g_strdup(uri);
1558     priv->image_format = g_strdup(
1559     osm_gps_map_source_get_image_format(priv->map_source));
1560     priv->max_zoom = osm_gps_map_source_get_max_zoom(priv->map_source);
1561     priv->min_zoom = osm_gps_map_source_get_min_zoom(priv->map_source);
1562     }
1563     }
1564    
1565 harbaum 32 if (!priv->cache_dir_is_full_path) {
1566     #ifdef G_CHECKSUM_MD5
1567     char *md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, priv->repo_uri, -1);
1568     #else
1569     char *md5 = simple_hash(priv->repo_uri);
1570     #endif
1571    
1572     if (priv->cache_dir) {
1573     char *old = priv->cache_dir;
1574     //the new cachedir is the given cache dir + the md5 of the repo_uri
1575 harbaum 55 priv->cache_dir = g_strdup_printf("%s%c%s", old, G_DIR_SEPARATOR, md5);
1576 harbaum 32 g_debug("Adjusting cache dir %s -> %s", old, priv->cache_dir);
1577     g_free(old);
1578 harbaum 55 } else {
1579     //the new cachedir is the current dir + the md5 of the repo_uri
1580     priv->cache_dir = g_strdup(md5);
1581 harbaum 32 }
1582    
1583     g_free(md5);
1584     }
1585    
1586 harbaum 55 inspect_map_uri(map);
1587    
1588 harbaum 32 return object;
1589     }
1590    
1591     static void
1592     osm_gps_map_dispose (GObject *object)
1593     {
1594     OsmGpsMap *map = OSM_GPS_MAP(object);
1595     OsmGpsMapPrivate *priv = map->priv;
1596    
1597     if (priv->is_disposed)
1598     return;
1599    
1600     priv->is_disposed = TRUE;
1601    
1602     soup_session_abort(priv->soup_session);
1603     g_object_unref(priv->soup_session);
1604    
1605     g_hash_table_destroy(priv->tile_queue);
1606     g_hash_table_destroy(priv->missing_tiles);
1607     g_hash_table_destroy(priv->tile_cache);
1608    
1609     osm_gps_map_free_images(map);
1610    
1611     if(priv->pixmap)
1612     g_object_unref (priv->pixmap);
1613    
1614 harbaum 55 if (priv->null_tile)
1615     g_object_unref (priv->null_tile);
1616    
1617 harbaum 32 if(priv->gc_map)
1618     g_object_unref(priv->gc_map);
1619    
1620     if (priv->idle_map_redraw != 0)
1621     g_source_remove (priv->idle_map_redraw);
1622    
1623 harbaum 57 g_free(priv->gps);
1624 harbaum 58 g_free(priv->balloon.coo);
1625 harbaum 57
1626 harbaum 32 G_OBJECT_CLASS (osm_gps_map_parent_class)->dispose (object);
1627     }
1628    
1629     static void
1630     osm_gps_map_finalize (GObject *object)
1631     {
1632     OsmGpsMap *map = OSM_GPS_MAP(object);
1633     OsmGpsMapPrivate *priv = map->priv;
1634    
1635     g_free(priv->cache_dir);
1636     g_free(priv->repo_uri);
1637 harbaum 55 g_free(priv->image_format);
1638 harbaum 32
1639     osm_gps_map_free_trip(map);
1640     osm_gps_map_free_tracks(map);
1641    
1642     G_OBJECT_CLASS (osm_gps_map_parent_class)->finalize (object);
1643     }
1644    
1645     static void
1646     osm_gps_map_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1647     {
1648     g_return_if_fail (OSM_IS_GPS_MAP (object));
1649     OsmGpsMap *map = OSM_GPS_MAP(object);
1650     OsmGpsMapPrivate *priv = map->priv;
1651    
1652     switch (prop_id)
1653     {
1654     case PROP_AUTO_CENTER:
1655     priv->map_auto_center = g_value_get_boolean (value);
1656     break;
1657     case PROP_RECORD_TRIP_HISTORY:
1658     priv->record_trip_history = g_value_get_boolean (value);
1659     break;
1660     case PROP_SHOW_TRIP_HISTORY:
1661     priv->show_trip_history = g_value_get_boolean (value);
1662     break;
1663     case PROP_AUTO_DOWNLOAD:
1664     priv->map_auto_download = g_value_get_boolean (value);
1665     break;
1666     case PROP_REPO_URI:
1667     priv->repo_uri = g_value_dup_string (value);
1668     break;
1669     case PROP_PROXY_URI:
1670     if ( g_value_get_string(value) ) {
1671     priv->proxy_uri = g_value_dup_string (value);
1672     g_debug("Setting proxy server: %s", priv->proxy_uri);
1673 harbaum 55
1674 harbaum 32 #ifndef LIBSOUP22
1675     GValue val = {0};
1676 harbaum 55
1677 harbaum 32 SoupURI* uri = soup_uri_new(priv->proxy_uri);
1678     g_value_init(&val, SOUP_TYPE_URI);
1679     g_value_take_boxed(&val, uri);
1680    
1681     g_object_set_property(G_OBJECT(priv->soup_session),SOUP_SESSION_PROXY_URI,&val);
1682     #else
1683     SoupUri* uri = soup_uri_new(priv->proxy_uri);
1684     g_object_set(G_OBJECT(priv->soup_session), SOUP_SESSION_PROXY_URI, uri, NULL);
1685     #endif
1686     } else
1687     priv->proxy_uri = NULL;
1688    
1689     break;
1690     case PROP_TILE_CACHE_DIR:
1691 harbaum 55 if ( g_value_get_string(value) )
1692     priv->cache_dir = g_value_dup_string (value);
1693 harbaum 32 break;
1694     case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
1695     priv->cache_dir_is_full_path = g_value_get_boolean (value);
1696     break;
1697     case PROP_ZOOM:
1698     priv->map_zoom = g_value_get_int (value);
1699     break;
1700     case PROP_MAX_ZOOM:
1701     priv->max_zoom = g_value_get_int (value);
1702     break;
1703     case PROP_MIN_ZOOM:
1704     priv->min_zoom = g_value_get_int (value);
1705     break;
1706     case PROP_MAP_X:
1707     priv->map_x = g_value_get_int (value);
1708     priv->center_coord_set = FALSE;
1709     break;
1710     case PROP_MAP_Y:
1711     priv->map_y = g_value_get_int (value);
1712     priv->center_coord_set = FALSE;
1713     break;
1714     case PROP_GPS_TRACK_WIDTH:
1715     priv->ui_gps_track_width = g_value_get_int (value);
1716     break;
1717     case PROP_GPS_POINT_R1:
1718     priv->ui_gps_point_inner_radius = g_value_get_int (value);
1719     break;
1720     case PROP_GPS_POINT_R2:
1721     priv->ui_gps_point_outer_radius = g_value_get_int (value);
1722     break;
1723 harbaum 55 case PROP_MAP_SOURCE:
1724     priv->map_source = g_value_get_int (value);
1725     break;
1726     case PROP_IMAGE_FORMAT:
1727     priv->image_format = g_value_dup_string (value);
1728     break;
1729 harbaum 32 default:
1730     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1731     break;
1732     }
1733     }
1734    
1735     static void
1736     osm_gps_map_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
1737     {
1738     g_return_if_fail (OSM_IS_GPS_MAP (object));
1739     float lat,lon;
1740     OsmGpsMap *map = OSM_GPS_MAP(object);
1741     OsmGpsMapPrivate *priv = map->priv;
1742    
1743     switch (prop_id)
1744     {
1745     case PROP_AUTO_CENTER:
1746     g_value_set_boolean(value, priv->map_auto_center);
1747     break;
1748     case PROP_RECORD_TRIP_HISTORY:
1749     g_value_set_boolean(value, priv->record_trip_history);
1750     break;
1751     case PROP_SHOW_TRIP_HISTORY:
1752     g_value_set_boolean(value, priv->show_trip_history);
1753     break;
1754     case PROP_AUTO_DOWNLOAD:
1755     g_value_set_boolean(value, priv->map_auto_download);
1756     break;
1757     case PROP_REPO_URI:
1758     g_value_set_string(value, priv->repo_uri);
1759     break;
1760     case PROP_PROXY_URI:
1761     g_value_set_string(value, priv->proxy_uri);
1762     break;
1763     case PROP_TILE_CACHE_DIR:
1764     g_value_set_string(value, priv->cache_dir);
1765     break;
1766     case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
1767     g_value_set_boolean(value, priv->cache_dir_is_full_path);
1768     break;
1769     case PROP_ZOOM:
1770     g_value_set_int(value, priv->map_zoom);
1771     break;
1772     case PROP_MAX_ZOOM:
1773     g_value_set_int(value, priv->max_zoom);
1774     break;
1775     case PROP_MIN_ZOOM:
1776     g_value_set_int(value, priv->min_zoom);
1777     break;
1778     case PROP_LATITUDE:
1779     lat = pixel2lat(priv->map_zoom,
1780     priv->map_y + (GTK_WIDGET(map)->allocation.height / 2));
1781     g_value_set_float(value, rad2deg(lat));
1782     break;
1783     case PROP_LONGITUDE:
1784     lon = pixel2lon(priv->map_zoom,
1785     priv->map_x + (GTK_WIDGET(map)->allocation.width / 2));
1786     g_value_set_float(value, rad2deg(lon));
1787     break;
1788     case PROP_MAP_X:
1789     g_value_set_int(value, priv->map_x);
1790     break;
1791     case PROP_MAP_Y:
1792     g_value_set_int(value, priv->map_y);
1793     break;
1794     case PROP_TILES_QUEUED:
1795     g_value_set_int(value, g_hash_table_size(priv->tile_queue));
1796     break;
1797     case PROP_GPS_TRACK_WIDTH:
1798     g_value_set_int(value, priv->ui_gps_track_width);
1799     break;
1800     case PROP_GPS_POINT_R1:
1801     g_value_set_int(value, priv->ui_gps_point_inner_radius);
1802     break;
1803     case PROP_GPS_POINT_R2:
1804     g_value_set_int(value, priv->ui_gps_point_outer_radius);
1805     break;
1806 harbaum 55 case PROP_MAP_SOURCE:
1807     g_value_set_int(value, priv->map_source);
1808     break;
1809     case PROP_IMAGE_FORMAT:
1810     g_value_set_string(value, priv->image_format);
1811     break;
1812 harbaum 32 default:
1813     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1814     break;
1815     }
1816     }
1817    
1818     static gboolean
1819 harbaum 55 osm_gps_map_scroll_event (GtkWidget *widget, GdkEventScroll *event)
1820 harbaum 32 {
1821     OsmGpsMap *map = OSM_GPS_MAP(widget);
1822     OsmGpsMapPrivate *priv = map->priv;
1823    
1824     if (event->direction == GDK_SCROLL_UP)
1825     {
1826     osm_gps_map_set_zoom(map, priv->map_zoom+1);
1827     }
1828     else
1829     {
1830     osm_gps_map_set_zoom(map, priv->map_zoom-1);
1831     }
1832    
1833     return FALSE;
1834     }
1835    
1836     static gboolean
1837     osm_gps_map_button_press (GtkWidget *widget, GdkEventButton *event)
1838     {
1839     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1840    
1841 harbaum 58 /* don't drag if the user clicked within the balloon */
1842     if (osm_gps_map_in_balloon(priv,
1843     event->x + EXTRA_BORDER,
1844     event->y + EXTRA_BORDER))
1845     {
1846     priv->drag_counter = -1;
1847     return FALSE;
1848     }
1849    
1850 harbaum 32 priv->drag_counter = 0;
1851     priv->drag_start_mouse_x = (int) event->x;
1852     priv->drag_start_mouse_y = (int) event->y;
1853     priv->drag_start_map_x = priv->map_x;
1854     priv->drag_start_map_y = priv->map_y;
1855    
1856     return FALSE;
1857     }
1858    
1859     static gboolean
1860     osm_gps_map_button_release (GtkWidget *widget, GdkEventButton *event)
1861     {
1862     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1863    
1864 harbaum 58 /* released inside the balloon? */
1865     if (osm_gps_map_in_balloon(priv,
1866     event->x + EXTRA_BORDER,
1867     event->y + EXTRA_BORDER))
1868     {
1869     osm_gps_map_handle_balloon_click(OSM_GPS_MAP(widget),
1870     event->x - priv->balloon.rect.x + EXTRA_BORDER,
1871     event->y - priv->balloon.rect.y + EXTRA_BORDER);
1872     return FALSE;
1873     }
1874    
1875     if (priv->drag_counter < 0)
1876     return FALSE;
1877    
1878 harbaum 32 if (priv->dragging)
1879     {
1880     priv->dragging = FALSE;
1881    
1882     priv->map_x = priv->drag_start_map_x;
1883     priv->map_y = priv->drag_start_map_y;
1884    
1885     priv->map_x += (priv->drag_start_mouse_x - (int) event->x);
1886     priv->map_y += (priv->drag_start_mouse_y - (int) event->y);
1887    
1888     priv->center_coord_set = FALSE;
1889    
1890     osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
1891     }
1892    
1893     priv->drag_mouse_dx = 0;
1894     priv->drag_mouse_dy = 0;
1895 harbaum 58 priv->drag_counter = -1;
1896 harbaum 32
1897     return FALSE;
1898     }
1899    
1900     static gboolean
1901     osm_gps_map_motion_notify (GtkWidget *widget, GdkEventMotion *event)
1902     {
1903     int x, y;
1904     GdkModifierType state;
1905     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1906    
1907     if (event->is_hint)
1908     gdk_window_get_pointer (event->window, &x, &y, &state);
1909     else
1910     {
1911     x = event->x;
1912     y = event->y;
1913     state = event->state;
1914     }
1915    
1916     // are we being dragged
1917     if (!(state & GDK_BUTTON1_MASK))
1918     return FALSE;
1919    
1920 harbaum 58 if (priv->drag_counter < 0)
1921     return FALSE;
1922    
1923 harbaum 32 priv->drag_counter++;
1924    
1925     // we havent dragged more than 6 pixels
1926     if (priv->drag_counter < 6)
1927     return FALSE;
1928    
1929     priv->dragging = TRUE;
1930    
1931     if (priv->map_auto_center)
1932     g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
1933    
1934     priv->drag_mouse_dx = x - priv->drag_start_mouse_x;
1935     priv->drag_mouse_dy = y - priv->drag_start_mouse_y;
1936    
1937     gdk_draw_drawable (
1938     widget->window,
1939     widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1940     priv->pixmap,
1941     0,0,
1942     priv->drag_mouse_dx - EXTRA_BORDER, priv->drag_mouse_dy - EXTRA_BORDER,
1943     -1,-1);
1944    
1945     //Paint white outside of the map if dragging. Its less
1946     //ugly than painting the corrupted map
1947     if(priv->drag_mouse_dx>EXTRA_BORDER) {
1948     gdk_draw_rectangle (
1949     widget->window,
1950     widget->style->white_gc,
1951     TRUE,
1952     0, 0,
1953     priv->drag_mouse_dx - EXTRA_BORDER,
1954     widget->allocation.height);
1955     }
1956     else if (-priv->drag_mouse_dx > EXTRA_BORDER)
1957     {
1958     gdk_draw_rectangle (
1959     widget->window,
1960     widget->style->white_gc,
1961     TRUE,
1962     priv->drag_mouse_dx + widget->allocation.width + EXTRA_BORDER, 0,
1963     -priv->drag_mouse_dx - EXTRA_BORDER,
1964     widget->allocation.height);
1965     }
1966    
1967     if (priv->drag_mouse_dy>EXTRA_BORDER) {
1968     gdk_draw_rectangle (
1969     widget->window,
1970     widget->style->white_gc,
1971     TRUE,
1972     0, 0,
1973     widget->allocation.width,
1974     priv->drag_mouse_dy - EXTRA_BORDER);
1975     }
1976     else if (-priv->drag_mouse_dy > EXTRA_BORDER)
1977     {
1978     gdk_draw_rectangle (
1979     widget->window,
1980     widget->style->white_gc,
1981     TRUE,
1982     0, priv->drag_mouse_dy + widget->allocation.height + EXTRA_BORDER,
1983     widget->allocation.width,
1984     -priv->drag_mouse_dy - EXTRA_BORDER);
1985     }
1986    
1987     return FALSE;
1988     }
1989    
1990     static gboolean
1991     osm_gps_map_configure (GtkWidget *widget, GdkEventConfigure *event)
1992     {
1993     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1994    
1995     /* create pixmap */
1996     if (priv->pixmap)
1997     g_object_unref (priv->pixmap);
1998    
1999     priv->pixmap = gdk_pixmap_new (
2000     widget->window,
2001     widget->allocation.width + EXTRA_BORDER * 2,
2002     widget->allocation.height + EXTRA_BORDER * 2,
2003     -1);
2004    
2005     /* and gc, used for clipping (I think......) */
2006     if(priv->gc_map)
2007     g_object_unref(priv->gc_map);
2008    
2009     priv->gc_map = gdk_gc_new(priv->pixmap);
2010    
2011     osm_gps_map_map_redraw(OSM_GPS_MAP(widget));
2012    
2013     return FALSE;
2014     }
2015    
2016     static gboolean
2017     osm_gps_map_expose (GtkWidget *widget, GdkEventExpose *event)
2018     {
2019     OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
2020    
2021     gdk_draw_drawable (
2022     widget->window,
2023     widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
2024     priv->pixmap,
2025 harbaum 58 event->area.x + EXTRA_BORDER,
2026     event->area.y + EXTRA_BORDER,
2027 harbaum 32 event->area.x, event->area.y,
2028     event->area.width, event->area.height);
2029    
2030     return FALSE;
2031     }
2032    
2033     static void
2034     osm_gps_map_class_init (OsmGpsMapClass *klass)
2035     {
2036     GObjectClass* object_class = G_OBJECT_CLASS (klass);
2037     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
2038    
2039     g_type_class_add_private (klass, sizeof (OsmGpsMapPrivate));
2040    
2041     object_class->dispose = osm_gps_map_dispose;
2042     object_class->finalize = osm_gps_map_finalize;
2043     object_class->constructor = osm_gps_map_constructor;
2044     object_class->set_property = osm_gps_map_set_property;
2045     object_class->get_property = osm_gps_map_get_property;
2046    
2047     widget_class->expose_event = osm_gps_map_expose;
2048     widget_class->configure_event = osm_gps_map_configure;
2049     widget_class->button_press_event = osm_gps_map_button_press;
2050     widget_class->button_release_event = osm_gps_map_button_release;
2051     widget_class->motion_notify_event = osm_gps_map_motion_notify;
2052 harbaum 55 widget_class->scroll_event = osm_gps_map_scroll_event;
2053 harbaum 32
2054     g_object_class_install_property (object_class,
2055     PROP_AUTO_CENTER,
2056     g_param_spec_boolean ("auto-center",
2057     "auto center",
2058     "map auto center",
2059     TRUE,
2060     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2061    
2062     g_object_class_install_property (object_class,
2063     PROP_RECORD_TRIP_HISTORY,
2064     g_param_spec_boolean ("record-trip-history",
2065     "record trip history",
2066     "should all gps points be recorded in a trip history",
2067     TRUE,
2068     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2069    
2070     g_object_class_install_property (object_class,
2071     PROP_SHOW_TRIP_HISTORY,
2072     g_param_spec_boolean ("show-trip-history",
2073     "show trip history",
2074     "should the recorded trip history be shown on the map",
2075     TRUE,
2076     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2077    
2078     g_object_class_install_property (object_class,
2079     PROP_AUTO_DOWNLOAD,
2080     g_param_spec_boolean ("auto-download",
2081     "auto download",
2082     "map auto download",
2083     TRUE,
2084     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2085    
2086     g_object_class_install_property (object_class,
2087     PROP_REPO_URI,
2088     g_param_spec_string ("repo-uri",
2089     "repo uri",
2090 harbaum 55 "map source tile repository uri",
2091     OSM_REPO_URI,
2092 harbaum 32 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2093    
2094 harbaum 55 g_object_class_install_property (object_class,
2095 harbaum 32 PROP_PROXY_URI,
2096     g_param_spec_string ("proxy-uri",
2097     "proxy uri",
2098     "http proxy uri on NULL",
2099     NULL,
2100     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2101    
2102     g_object_class_install_property (object_class,
2103     PROP_TILE_CACHE_DIR,
2104     g_param_spec_string ("tile-cache",
2105     "tile cache",
2106     "osm local tile cache dir",
2107 harbaum 55 NULL,
2108 harbaum 32 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2109    
2110     g_object_class_install_property (object_class,
2111     PROP_TILE_CACHE_DIR_IS_FULL_PATH,
2112     g_param_spec_boolean ("tile-cache-is-full-path",
2113     "tile cache is full path",
2114     "if true, the path passed to tile-cache is interpreted as the full cache path",
2115     FALSE,
2116     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2117    
2118     g_object_class_install_property (object_class,
2119     PROP_ZOOM,
2120     g_param_spec_int ("zoom",
2121     "zoom",
2122     "zoom level",
2123     MIN_ZOOM, /* minimum property value */
2124     MAX_ZOOM, /* maximum property value */
2125     3,
2126     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2127    
2128     g_object_class_install_property (object_class,
2129     PROP_MAX_ZOOM,
2130     g_param_spec_int ("max-zoom",
2131     "max zoom",
2132     "maximum zoom level",
2133     MIN_ZOOM, /* minimum property value */
2134     MAX_ZOOM, /* maximum property value */
2135 harbaum 55 OSM_MAX_ZOOM,
2136 harbaum 32 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2137    
2138     g_object_class_install_property (object_class,
2139     PROP_MIN_ZOOM,
2140     g_param_spec_int ("min-zoom",
2141     "min zoom",
2142     "minimum zoom level",
2143     MIN_ZOOM, /* minimum property value */
2144     MAX_ZOOM, /* maximum property value */
2145 harbaum 55 OSM_MIN_ZOOM,
2146 harbaum 32 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2147    
2148     g_object_class_install_property (object_class,
2149     PROP_LATITUDE,
2150     g_param_spec_float ("latitude",
2151     "latitude",
2152     "latitude in degrees",
2153     -90.0, /* minimum property value */
2154     90.0, /* maximum property value */
2155     0,
2156     G_PARAM_READABLE));
2157    
2158     g_object_class_install_property (object_class,
2159     PROP_LONGITUDE,
2160     g_param_spec_float ("longitude",
2161     "longitude",
2162     "longitude in degrees",
2163     -180.0, /* minimum property value */
2164     180.0, /* maximum property value */
2165     0,
2166     G_PARAM_READABLE));
2167    
2168     g_object_class_install_property (object_class,
2169     PROP_MAP_X,
2170     g_param_spec_int ("map-x",
2171     "map-x",
2172     "initial map x location",
2173     G_MININT, /* minimum property value */
2174     G_MAXINT, /* maximum property value */
2175     890,
2176     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2177    
2178     g_object_class_install_property (object_class,
2179     PROP_MAP_Y,
2180     g_param_spec_int ("map-y",
2181     "map-y",
2182     "initial map y location",
2183     G_MININT, /* minimum property value */
2184     G_MAXINT, /* maximum property value */
2185     515,
2186     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2187    
2188     g_object_class_install_property (object_class,
2189     PROP_TILES_QUEUED,
2190     g_param_spec_int ("tiles-queued",
2191     "tiles-queued",
2192     "number of tiles currently waiting to download",
2193     G_MININT, /* minimum property value */
2194     G_MAXINT, /* maximum property value */
2195     0,
2196     G_PARAM_READABLE));
2197    
2198     g_object_class_install_property (object_class,
2199     PROP_GPS_TRACK_WIDTH,
2200     g_param_spec_int ("gps-track-width",
2201     "gps-track-width",
2202     "width of the lines drawn for the gps track",
2203     1, /* minimum property value */
2204     G_MAXINT, /* maximum property value */
2205     4,
2206     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2207    
2208     g_object_class_install_property (object_class,
2209     PROP_GPS_POINT_R1,
2210     g_param_spec_int ("gps-track-point-radius",
2211     "gps-track-point-radius",
2212     "radius of the gps point inner circle",
2213     0, /* minimum property value */
2214     G_MAXINT, /* maximum property value */
2215     5,
2216     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2217    
2218     g_object_class_install_property (object_class,
2219     PROP_GPS_POINT_R2,
2220     g_param_spec_int ("gps-track-highlight-radius",
2221     "gps-track-highlight-radius",
2222     "radius of the gps point highlight circle",
2223     0, /* minimum property value */
2224     G_MAXINT, /* maximum property value */
2225     20,
2226     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2227    
2228 harbaum 55 g_object_class_install_property (object_class,
2229     PROP_MAP_SOURCE,
2230     g_param_spec_int ("map-source",
2231     "map source",
2232     "map source ID",
2233     -1, /* minimum property value */
2234     G_MAXINT, /* maximum property value */
2235     -1,
2236     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2237    
2238     g_object_class_install_property (object_class,
2239     PROP_IMAGE_FORMAT,
2240     g_param_spec_string ("image-format",
2241     "image format",
2242     "map source tile repository image format (jpg, png)",
2243     OSM_IMAGE_FORMAT,
2244     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2245 harbaum 32 }
2246    
2247 harbaum 55 const char*
2248     osm_gps_map_source_get_friendly_name(OsmGpsMapSource_t source)
2249     {
2250     switch(source)
2251     {
2252     case OSM_GPS_MAP_SOURCE_NULL:
2253     return "None";
2254     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
2255     return "OpenStreetMap";
2256     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
2257     return "OpenStreetMap Renderer";
2258     case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
2259     return "OpenAerialMap";
2260     case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
2261     return "Maps-For-Free";
2262     case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
2263     return "Google Maps";
2264     case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
2265     return "Google Satellite";
2266     case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
2267     return "Google Hybrid";
2268     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
2269     return "Virtual Earth";
2270     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
2271     return "Virtual Earth Satellite";
2272     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
2273     return "Virtual Earth Hybrid";
2274     case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
2275     return "Yahoo Maps";
2276     case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
2277     return "Yahoo Satellite";
2278     case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
2279     return "Yahoo Hybrid";
2280     default:
2281     return NULL;
2282     }
2283     return NULL;
2284     }
2285    
2286     //http://www.internettablettalk.com/forums/showthread.php?t=5209
2287     //https://garage.maemo.org/plugins/scmsvn/viewcvs.php/trunk/src/maps.c?root=maemo-mapper&view=markup
2288     //http://www.ponies.me.uk/maps/GoogleTileUtils.java
2289     //http://www.mgmaps.com/cache/MapTileCacher.perl
2290     const char*
2291     osm_gps_map_source_get_repo_uri(OsmGpsMapSource_t source)
2292     {
2293     switch(source)
2294     {
2295     case OSM_GPS_MAP_SOURCE_NULL:
2296     return "none://";
2297     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
2298     return OSM_REPO_URI;
2299     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
2300     return "http://tah.openstreetmap.org/Tiles/tile/#Z/#X/#Y.png";
2301     case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
2302     return "http://tile.openaerialmap.org/tiles/1.0.0/openaerialmap-900913/#Z/#X/#Y.jpg";
2303     case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
2304     return "http://maps-for-free.com/layer/relief/z#Z/row#Y/#Z_#X-#Y.jpg";
2305     case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
2306     return "http://mt#R.google.com/vt/v=w2.97&x=#X&y=#Y&z=#Z";
2307     case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
2308     return "http://khm#R.google.com/kh?n=404&v=3&t=#Q";
2309     case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
2310     return NULL; /* No longer working "http://mt#R.google.com/mt?n=404&v=w2t.99&x=#X&y=#Y&zoom=#S" */
2311     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
2312     return "http://a#R.ortho.tiles.virtualearth.net/tiles/r#W.jpeg?g=50";
2313     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
2314     return "http://a#R.ortho.tiles.virtualearth.net/tiles/a#W.jpeg?g=50";
2315     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
2316     return "http://a#R.ortho.tiles.virtualearth.net/tiles/h#W.jpeg?g=50";
2317     case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
2318     case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
2319     case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
2320     /* TODO: Implement signed Y, aka U
2321     * http://us.maps3.yimg.com/aerial.maps.yimg.com/ximg?v=1.7&t=a&s=256&x=%d&y=%-d&z=%d
2322     * x = tilex,
2323     * y = (1 << (MAX_ZOOM - zoom)) - tiley - 1,
2324     * z = zoom - (MAX_ZOOM - 17));
2325     */
2326     return NULL;
2327     default:
2328     return NULL;
2329     }
2330     return NULL;
2331     }
2332    
2333     const char *
2334     osm_gps_map_source_get_image_format(OsmGpsMapSource_t source)
2335     {
2336     switch(source) {
2337     case OSM_GPS_MAP_SOURCE_NULL:
2338     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
2339     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
2340     return "png";
2341     case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
2342     case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
2343     case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
2344     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
2345     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
2346     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
2347     case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
2348     case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
2349     case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
2350     case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
2351     case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
2352     return "jpg";
2353     default:
2354     return "bin";
2355     }
2356     return "bin";
2357     }
2358    
2359    
2360     int
2361     osm_gps_map_source_get_min_zoom(OsmGpsMapSource_t source)
2362     {
2363     return 1;
2364     }
2365    
2366     int
2367     osm_gps_map_source_get_max_zoom(OsmGpsMapSource_t source)
2368     {
2369     switch(source) {
2370     case OSM_GPS_MAP_SOURCE_NULL:
2371     return 18;
2372     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
2373     return OSM_MAX_ZOOM;
2374     case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
2375     case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
2376     case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
2377     case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
2378     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
2379     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
2380     case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
2381     case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
2382     case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
2383     case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
2384     return 17;
2385     case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
2386     return 11;
2387     case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
2388     return 18;
2389     default:
2390     return 17;
2391     }
2392     return 17;
2393     }
2394    
2395 harbaum 32 void
2396     osm_gps_map_download_maps (OsmGpsMap *map, coord_t *pt1, coord_t *pt2, int zoom_start, int zoom_end)
2397     {
2398     int i,j,zoom,num_tiles;
2399     OsmGpsMapPrivate *priv = map->priv;
2400    
2401     if (pt1 && pt2)
2402     {
2403     gchar *filename;
2404     num_tiles = 0;
2405     zoom_end = CLAMP(zoom_end, priv->min_zoom, priv->max_zoom);
2406     g_debug("Download maps: z:%d->%d",zoom_start, zoom_end);
2407    
2408     for(zoom=zoom_start; zoom<=zoom_end; zoom++)
2409     {
2410     int x1,y1,x2,y2;
2411    
2412     x1 = (int)floor((float)lon2pixel(zoom, pt1->rlon) / (float)TILESIZE);
2413     y1 = (int)floor((float)lat2pixel(zoom, pt1->rlat) / (float)TILESIZE);
2414    
2415     x2 = (int)floor((float)lon2pixel(zoom, pt2->rlon) / (float)TILESIZE);
2416     y2 = (int)floor((float)lat2pixel(zoom, pt2->rlat) / (float)TILESIZE);
2417    
2418     // loop x1-x2
2419     for(i=x1; i<=x2; i++)
2420     {
2421     // loop y1 - y2
2422     for(j=y1; j<=y2; j++)
2423     {
2424     // x = i, y = j
2425 harbaum 55 filename = g_strdup_printf("%s%c%d%c%d%c%d.%s",
2426     priv->cache_dir, G_DIR_SEPARATOR,
2427     zoom, G_DIR_SEPARATOR,
2428     i, G_DIR_SEPARATOR,
2429     j,
2430     priv->image_format);
2431     if (!g_file_test(filename, G_FILE_TEST_EXISTS))
2432 harbaum 32 {
2433     osm_gps_map_download_tile(map, zoom, i, j, FALSE);
2434     num_tiles++;
2435     }
2436     g_free(filename);
2437     }
2438     }
2439     g_debug("DL @Z:%d = %d tiles",zoom,num_tiles);
2440     }
2441     }
2442     }
2443    
2444     void
2445     osm_gps_map_get_bbox (OsmGpsMap *map, coord_t *pt1, coord_t *pt2)
2446     {
2447     OsmGpsMapPrivate *priv = map->priv;
2448    
2449     if (pt1 && pt2) {
2450     pt1->rlat = pixel2lat(priv->map_zoom, priv->map_y);
2451     pt1->rlon = pixel2lon(priv->map_zoom, priv->map_x);
2452     pt2->rlat = pixel2lat(priv->map_zoom, priv->map_y + GTK_WIDGET(map)->allocation.height);
2453     pt2->rlon = pixel2lon(priv->map_zoom, priv->map_x + GTK_WIDGET(map)->allocation.width);
2454    
2455     g_debug("BBOX: %f %f %f %f", pt1->rlat, pt1->rlon, pt2->rlat, pt2->rlon);
2456     }
2457     }
2458    
2459     void
2460     osm_gps_map_set_mapcenter (OsmGpsMap *map, float latitude, float longitude, int zoom)
2461     {
2462     osm_gps_map_set_center (map, latitude, longitude);
2463     osm_gps_map_set_zoom (map, zoom);
2464     }
2465    
2466     void
2467     osm_gps_map_set_center (OsmGpsMap *map, float latitude, float longitude)
2468     {
2469     int pixel_x, pixel_y;
2470     OsmGpsMapPrivate *priv;
2471    
2472     g_return_if_fail (OSM_IS_GPS_MAP (map));
2473     priv = map->priv;
2474    
2475     priv->center_rlat = deg2rad(latitude);
2476     priv->center_rlon = deg2rad(longitude);
2477     priv->center_coord_set = TRUE;
2478    
2479     // pixel_x,y, offsets
2480     pixel_x = lon2pixel(priv->map_zoom, priv->center_rlon);
2481     pixel_y = lat2pixel(priv->map_zoom, priv->center_rlat);
2482    
2483     priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
2484     priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
2485    
2486     osm_gps_map_map_redraw_idle(map);
2487     }
2488    
2489     int
2490     osm_gps_map_set_zoom (OsmGpsMap *map, int zoom)
2491     {
2492     int zoom_old;
2493 harbaum 55 double factor = 0.0;
2494 harbaum 32 int width_center, height_center;
2495     OsmGpsMapPrivate *priv;
2496    
2497     g_return_val_if_fail (OSM_IS_GPS_MAP (map), 0);
2498     priv = map->priv;
2499    
2500     if (zoom != priv->map_zoom)
2501     {
2502     width_center = GTK_WIDGET(map)->allocation.width / 2;
2503     height_center = GTK_WIDGET(map)->allocation.height / 2;
2504    
2505     zoom_old = priv->map_zoom;
2506     //constrain zoom min_zoom -> max_zoom
2507     priv->map_zoom = CLAMP(zoom, priv->min_zoom, priv->max_zoom);
2508    
2509     if (priv->center_coord_set)
2510     {
2511     priv->map_x = lon2pixel(priv->map_zoom, priv->center_rlon) - width_center;
2512     priv->map_y = lat2pixel(priv->map_zoom, priv->center_rlat) - height_center;
2513     }
2514     else
2515     {
2516     factor = exp(priv->map_zoom * M_LN2)/exp(zoom_old * M_LN2);
2517     priv->map_x = ((priv->map_x + width_center) * factor) - width_center;
2518     priv->map_y = ((priv->map_y + height_center) * factor) - height_center;
2519     }
2520    
2521     g_debug("Zoom changed from %d to %d factor:%f x:%d",
2522     zoom_old, priv->map_zoom, factor, priv->map_x);
2523    
2524     osm_gps_map_map_redraw_idle(map);
2525     }
2526     return priv->map_zoom;
2527     }
2528    
2529     void
2530     osm_gps_map_add_track (OsmGpsMap *map, GSList *track)
2531     {
2532     OsmGpsMapPrivate *priv;
2533    
2534     g_return_if_fail (OSM_IS_GPS_MAP (map));
2535     priv = map->priv;
2536    
2537     if (track) {
2538     priv->tracks = g_slist_append(priv->tracks, track);
2539     osm_gps_map_map_redraw_idle(map);
2540     }
2541     }
2542    
2543     void
2544     osm_gps_map_clear_tracks (OsmGpsMap *map)
2545     {
2546     g_return_if_fail (OSM_IS_GPS_MAP (map));
2547    
2548     osm_gps_map_free_tracks(map);
2549     osm_gps_map_map_redraw_idle(map);
2550     }
2551    
2552     void
2553     osm_gps_map_add_image (OsmGpsMap *map, float latitude, float longitude, GdkPixbuf *image)
2554     {
2555     g_return_if_fail (OSM_IS_GPS_MAP (map));
2556    
2557     if (image) {
2558     OsmGpsMapPrivate *priv = map->priv;
2559     image_t *im;
2560    
2561     //cache w/h for speed, and add image to list
2562     im = g_new0(image_t,1);
2563     im->w = gdk_pixbuf_get_width(image);
2564 harbaum 55 im->h = gdk_pixbuf_get_height(image);
2565 harbaum 32 im->pt.rlat = deg2rad(latitude);
2566     im->pt.rlon = deg2rad(longitude);
2567    
2568     g_object_ref(image);
2569     im->image = image;
2570    
2571     priv->images = g_slist_append(priv->images, im);
2572    
2573     osm_gps_map_map_redraw_idle(map);
2574     }
2575     }
2576    
2577 harbaum 55 gboolean
2578     osm_gps_map_remove_image (OsmGpsMap *map, GdkPixbuf *image)
2579     {
2580     OsmGpsMapPrivate *priv = map->priv;
2581     if (priv->images) {
2582     GSList *list;
2583     for(list = priv->images; list != NULL; list = list->next)
2584     {
2585     image_t *im = list->data;
2586     if (im->image == image)
2587     {
2588     priv->images = g_slist_remove_link(priv->images, list);
2589     g_object_unref(im->image);
2590     g_free(im);
2591     osm_gps_map_map_redraw_idle(map);
2592     return TRUE;
2593     }
2594     }
2595     }
2596     return FALSE;
2597     }
2598    
2599 harbaum 32 void
2600     osm_gps_map_clear_images (OsmGpsMap *map)
2601     {
2602     g_return_if_fail (OSM_IS_GPS_MAP (map));
2603    
2604     osm_gps_map_free_images(map);
2605     osm_gps_map_map_redraw_idle(map);
2606     }
2607    
2608     void
2609     osm_gps_map_osd_speed (OsmGpsMap *map, float speed)
2610     {
2611     OsmGpsMapPrivate *priv;
2612    
2613     PangoContext *context = NULL;
2614     PangoLayout *layout = NULL;
2615     PangoFontDescription *desc = NULL;
2616    
2617     GdkColor color;
2618     GdkGC *gc;
2619    
2620     gchar *buffer;
2621     //static int x = 10, y = 10;
2622     static int width = 0, height = 0;
2623    
2624     g_return_if_fail (OSM_IS_GPS_MAP (map));
2625     priv = map->priv;
2626    
2627     buffer = g_strdup_printf("%.0f", speed);
2628    
2629     /* pango initialisation */
2630     context = gtk_widget_get_pango_context (GTK_WIDGET(map));
2631     layout = pango_layout_new (context);
2632     desc = pango_font_description_new();
2633    
2634     pango_font_description_set_size (desc, 40 * PANGO_SCALE);
2635     pango_layout_set_font_description (layout, desc);
2636     pango_layout_set_text (layout, buffer, strlen(buffer));
2637    
2638     gc = gdk_gc_new (GTK_WIDGET(map)->window);
2639    
2640     color.red = (0 > 50) ? 0xffff : 0;
2641     color.green = 0;
2642     color.blue = 0;
2643    
2644     gdk_gc_set_rgb_fg_color (gc, &color);
2645    
2646     /* faster / less flicker alternative:*/
2647     gdk_draw_drawable (
2648     GTK_WIDGET(map)->window,
2649     GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(map)],
2650     priv->pixmap,
2651     0,0,
2652     0,0,
2653     width+10,width+10);
2654    
2655     gdk_draw_layout(GTK_WIDGET(map)->window,
2656     gc,
2657     0, 0,
2658     layout);
2659    
2660     /* set width and height */
2661     pango_layout_get_pixel_size(layout, &width, &height);
2662    
2663     g_free(buffer);
2664     pango_font_description_free (desc);
2665     g_object_unref (layout);
2666     g_object_unref (gc);
2667     }
2668    
2669     void
2670     osm_gps_map_draw_gps (OsmGpsMap *map, float latitude, float longitude, float heading)
2671     {
2672     int pixel_x, pixel_y;
2673     OsmGpsMapPrivate *priv;
2674    
2675     g_return_if_fail (OSM_IS_GPS_MAP (map));
2676     priv = map->priv;
2677    
2678     priv->gps->rlat = deg2rad(latitude);
2679     priv->gps->rlon = deg2rad(longitude);
2680     priv->gps_valid = TRUE;
2681    
2682     // pixel_x,y, offsets
2683     pixel_x = lon2pixel(priv->map_zoom, priv->gps->rlon);
2684     pixel_y = lat2pixel(priv->map_zoom, priv->gps->rlat);
2685    
2686     //If trip marker add to list of gps points.
2687     if (priv->record_trip_history) {
2688     coord_t *tp = g_new0(coord_t,1);
2689     tp->rlat = priv->gps->rlat;
2690     tp->rlon = priv->gps->rlon;
2691     priv->trip_history = g_slist_append(priv->trip_history, tp);
2692     }
2693    
2694     // dont draw anything if we are dragging
2695     if (priv->dragging) {
2696     g_debug("Dragging");
2697     return;
2698     }
2699    
2700     //Automatically center the map if the track approaches the edge
2701     if(priv->map_auto_center) {
2702     int x = pixel_x - priv->map_x;
2703     int y = pixel_y - priv->map_y;
2704     int width = GTK_WIDGET(map)->allocation.width;
2705     int height = GTK_WIDGET(map)->allocation.height;
2706     if( x < (width/2 - width/8) || x > (width/2 + width/8) ||
2707     y < (height/2 - height/8) || y > (height/2 + height/8)) {
2708    
2709     priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
2710     priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
2711     priv->center_coord_set = FALSE;
2712     }
2713     }
2714    
2715     // this redraws the map (including the gps track, and adjusts the
2716     // map center if it was changed
2717     osm_gps_map_map_redraw_idle(map);
2718     }
2719    
2720     void
2721     osm_gps_map_clear_gps (OsmGpsMap *map)
2722     {
2723     osm_gps_map_free_trip(map);
2724     osm_gps_map_map_redraw_idle(map);
2725     }
2726    
2727     coord_t
2728     osm_gps_map_get_co_ordinates (OsmGpsMap *map, int pixel_x, int pixel_y)
2729     {
2730     coord_t coord;
2731     OsmGpsMapPrivate *priv = map->priv;
2732    
2733     coord.rlat = pixel2lat(priv->map_zoom, priv->map_y + pixel_y);
2734     coord.rlon = pixel2lon(priv->map_zoom, priv->map_x + pixel_x);
2735     return coord;
2736     }
2737    
2738     GtkWidget *
2739     osm_gps_map_new (void)
2740     {
2741     return g_object_new (OSM_TYPE_GPS_MAP, NULL);
2742     }
2743    
2744     void
2745     osm_gps_map_screen_to_geographic (OsmGpsMap *map, gint pixel_x, gint pixel_y,
2746     gfloat *latitude, gfloat *longitude)
2747     {
2748     OsmGpsMapPrivate *priv;
2749    
2750     g_return_if_fail (OSM_IS_GPS_MAP (map));
2751     priv = map->priv;
2752    
2753     if (latitude)
2754     *latitude = rad2deg(pixel2lat(priv->map_zoom, priv->map_y + pixel_y));
2755     if (longitude)
2756     *longitude = rad2deg(pixel2lon(priv->map_zoom, priv->map_x + pixel_x));
2757     }
2758    
2759     void
2760     osm_gps_map_geographic_to_screen (OsmGpsMap *map,
2761     gfloat latitude, gfloat longitude,
2762     gint *pixel_x, gint *pixel_y)
2763     {
2764     OsmGpsMapPrivate *priv;
2765    
2766     g_return_if_fail (OSM_IS_GPS_MAP (map));
2767     priv = map->priv;
2768    
2769     if (pixel_x)
2770     *pixel_x = lon2pixel(priv->map_zoom, deg2rad(longitude)) - priv->map_x;
2771     if (pixel_y)
2772     *pixel_y = lat2pixel(priv->map_zoom, deg2rad(latitude)) - priv->map_y;
2773     }
2774    
2775     void
2776     osm_gps_map_scroll (OsmGpsMap *map, gint dx, gint dy)
2777     {
2778     OsmGpsMapPrivate *priv;
2779    
2780     g_return_if_fail (OSM_IS_GPS_MAP (map));
2781     priv = map->priv;
2782    
2783     priv->center_coord_set = FALSE;
2784     priv->map_x += dx;
2785     priv->map_y += dy;
2786    
2787     osm_gps_map_map_redraw_idle (map);
2788     }
2789    
2790 harbaum 55 float
2791     osm_gps_map_get_scale(OsmGpsMap *map)
2792     {
2793     OsmGpsMapPrivate *priv;
2794    
2795     g_return_val_if_fail (OSM_IS_GPS_MAP (map), OSM_NAN);
2796     priv = map->priv;
2797    
2798     return osm_gps_map_get_scale_at_point(priv->map_zoom, priv->center_rlat, priv->center_rlon);
2799     }
2800    
2801 harbaum 57 void
2802 harbaum 58 osm_gps_map_draw_balloon (OsmGpsMap *map, float latitude, float longitude,
2803     OsmGpsMapBalloonCallback cb, gpointer data)
2804 harbaum 57 {
2805     OsmGpsMapPrivate *priv;
2806    
2807     /* remove and previously installed balloon */
2808     osm_gps_map_clear_balloon (map);
2809    
2810     g_return_if_fail (OSM_IS_GPS_MAP (map));
2811     priv = map->priv;
2812    
2813 harbaum 58 priv->balloon.coo->rlat = deg2rad(latitude);
2814     priv->balloon.coo->rlon = deg2rad(longitude);
2815     priv->balloon.valid = TRUE;
2816 harbaum 57
2817 harbaum 58 priv->balloon.cb = cb;
2818     priv->balloon.data = data;
2819    
2820 harbaum 57 // this redraws the map
2821     osm_gps_map_map_redraw_idle(map);
2822     }
2823    
2824     void
2825     osm_gps_map_clear_balloon (OsmGpsMap *map)
2826     {
2827     OsmGpsMapPrivate *priv;
2828    
2829     g_return_if_fail (OSM_IS_GPS_MAP (map));
2830     priv = map->priv;
2831    
2832 harbaum 58 priv->balloon.valid = FALSE;
2833 harbaum 57
2834     osm_gps_map_map_redraw_idle(map);
2835     }