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

Parent Directory Parent Directory | Revision Log Revision Log


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