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

Parent Directory Parent Directory | Revision Log Revision Log


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