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

Parent Directory Parent Directory | Revision Log Revision Log


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