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

Parent Directory Parent Directory | Revision Log Revision Log


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