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

Parent Directory Parent Directory | Revision Log Revision Log


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