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

Parent Directory Parent Directory | Revision Log Revision Log


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