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

Parent Directory Parent Directory | Revision Log Revision Log


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