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

Parent Directory Parent Directory | Revision Log Revision Log


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