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

Parent Directory Parent Directory | Revision Log Revision Log


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