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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 111 - (show annotations)
Mon Sep 14 12:16:08 2009 UTC (14 years, 8 months ago) by harbaum
File MIME type: text/plain
File size: 104258 byte(s)
OSD drag offset fix
1 /* -*- 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 #include <glib/gstdio.h>
37 #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 #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 #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 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 char *tile_dir;
101 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 OsmGpsMapSource_t map_source;
107 char *repo_uri;
108 char *image_format;
109 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 float gps_heading;
119 gboolean gps_valid;
120
121 #ifdef ENABLE_BALLOON
122 //a balloon with additional info
123 struct {
124 coord_t *coo;
125 gboolean valid;
126 OsmGpsMapRect_t rect;
127 OsmGpsMapBalloonCallback cb;
128 gpointer data;
129 } balloon;
130 #endif
131
132 #ifdef ENABLE_OSD
133 //the osd controls (if present)
134 osm_gps_map_osd_t *osd;
135 #ifdef OSD_DOUBLE_BUFFER
136 GdkPixmap *dbuf_pixmap;
137 #endif
138 #endif
139
140 #ifdef OSM_GPS_MAP_KEY_FULLSCREEN
141 gboolean fullscreen;
142 #endif
143
144 //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 GdkPixbuf *null_tile;
154
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 PROP_GPS_POINT_R2,
206 PROP_MAP_SOURCE,
207 PROP_IMAGE_FORMAT
208 };
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 #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 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 priv->uri_format = 0;
356 priv->the_google = FALSE;
357
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 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 /* 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 int mr = MAX(3*r,r2);
594
595 map_x0 = priv->map_x - EXTRA_BORDER;
596 map_y0 = priv->map_y - EXTRA_BORDER;
597 x = lon2pixel(priv->map_zoom, priv->gps->rlon) - map_x0;
598 y = lat2pixel(priv->map_zoom, priv->gps->rlat) - map_y0;
599 #ifdef USE_CAIRO
600 cairo_t *cr;
601 cairo_pattern_t *pat;
602 #else
603 GdkColor color;
604 GdkGC *marker;
605 #endif
606
607 #ifdef USE_CAIRO
608 cr = gdk_cairo_create(priv->pixmap);
609
610 // draw transparent area
611 if (r2 > 0) {
612 cairo_set_line_width (cr, 1.5);
613 cairo_set_source_rgba (cr, 0.75, 0.75, 0.75, 0.4);
614 cairo_arc (cr, x, y, r2, 0, 2 * M_PI);
615 cairo_fill (cr);
616 // draw transparent area border
617 cairo_set_source_rgba (cr, 0.55, 0.55, 0.55, 0.4);
618 cairo_arc (cr, x, y, r2, 0, 2 * M_PI);
619 cairo_stroke(cr);
620 }
621
622 // draw ball gradient
623 if (r > 0) {
624 // 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 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
654 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 }
694
695 #ifdef ENABLE_BALLOON
696 /* most visual effects are hardcoded by now, but may be made */
697 /* available via properties later */
698 #ifndef BALLOON_AREA_WIDTH
699 #define BALLOON_AREA_WIDTH 290
700 #endif
701 #ifndef BALLOON_AREA_HEIGHT
702 #define BALLOON_AREA_HEIGHT 75
703 #endif
704 #ifndef BALLOON_CORNER_RADIUS
705 #define BALLOON_CORNER_RADIUS 10
706 #endif
707
708 #define BALLOON_BORDER (BALLOON_CORNER_RADIUS/2)
709 #define BALLOON_WIDTH (BALLOON_AREA_WIDTH + 2 * BALLOON_BORDER)
710 #define BALLOON_HEIGHT (BALLOON_AREA_HEIGHT + 2 * BALLOON_BORDER)
711 #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 #define BALLOON_SHADOW (BALLOON_CORNER_RADIUS/2)
716 #define BALLOON_SHADOW_TRANSPARENCY 0.2
717
718 #define CLOSE_BUTTON_RADIUS (BALLOON_CORNER_RADIUS)
719
720
721 /* 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 cairo_arc (cr, x0 + BALLOON_CORNER_RADIUS, y0 + BALLOON_CORNER_RADIUS,
729 BALLOON_CORNER_RADIUS, -M_PI, -M_PI/2);
730 if(!bottom) {
731 /* insert top pointer */
732 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 cairo_arc (cr, x1 - BALLOON_CORNER_RADIUS, y0 + BALLOON_CORNER_RADIUS,
739 BALLOON_CORNER_RADIUS, -M_PI/2, 0);
740 cairo_line_to (cr, x1 , y1 - BALLOON_CORNER_RADIUS);
741 cairo_arc (cr, x1 - BALLOON_CORNER_RADIUS, y1 - BALLOON_CORNER_RADIUS,
742 BALLOON_CORNER_RADIUS, 0, M_PI/2);
743 if(bottom) {
744 /* insert bottom pointer */
745 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 cairo_arc (cr, x0 + BALLOON_CORNER_RADIUS, y1 - BALLOON_CORNER_RADIUS,
752 BALLOON_CORNER_RADIUS, M_PI/2, M_PI);
753
754 cairo_close_path (cr);
755 }
756
757 static void
758 osm_gps_map_draw_balloon_int (OsmGpsMap *map)
759 {
760 OsmGpsMapPrivate *priv = map->priv;
761
762 if (priv->balloon.valid) {
763
764 /* ------- convert given coordinate into screen position --------- */
765 int x0 = lon2pixel(priv->map_zoom, priv->balloon.coo->rlon) -
766 priv->map_x + EXTRA_BORDER;
767 int y0 = lat2pixel(priv->map_zoom, priv->balloon.coo->rlat) -
768 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 /* 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 #ifdef USE_CAIRO
803 cairo_t *cr = gdk_cairo_create(priv->pixmap);
804
805 /* --------- 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
812 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
818 /* --------- 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
828
829 /* ---------- draw close button --------- */
830
831 int cx = x1 - BALLOON_BORDER - CLOSE_BUTTON_RADIUS;
832 int cy = y0 + BALLOON_BORDER + CLOSE_BUTTON_RADIUS;
833 int crad = CLOSE_BUTTON_RADIUS;
834
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 cairo_set_line_width (cr, 2);
840 cairo_stroke(cr);
841
842 cairo_set_source_rgba (cr, 1, 1, 1, 1.0);
843 cairo_set_line_width (cr, BALLOON_CORNER_RADIUS/3.3);
844 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
845 cairo_move_to (cr, cx - crad/2, cy - crad/2);
846 cairo_line_to (cr, cx + crad/2, cy + crad/2);
847 cairo_stroke (cr);
848 cairo_move_to (cr, cx + crad/2, cy - crad/2);
849 cairo_line_to (cr, cx - crad/2, cy + crad/2);
850 cairo_stroke (cr);
851
852 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
864 cairo_destroy(cr);
865
866 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 #endif
872 }
873 }
874
875 /* 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 #endif // ENABLE_BALLOON
906
907 static void
908 osm_gps_map_blit_tile(OsmGpsMap *map, GdkPixbuf *pixbuf, int offset_x, int offset_y)
909 {
910 OsmGpsMapPrivate *priv = map->priv;
911
912 g_debug("Queing redraw @ %d,%d (w:%d h:%d)", offset_x,offset_y, TILESIZE,TILESIZE);
913
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 FILE *file;
945 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 file = g_fopen(dl->filename, "wb");
958 if (file != NULL)
959 {
960 fwrite (MSG_RESPONSE_BODY(msg), 1, MSG_RESPONSE_LEN(msg), file);
961 file_saved = TRUE;
962 g_debug("Wrote "MSG_RESPONSE_LEN_FORMAT" bytes to %s", MSG_RESPONSE_LEN(msg), dl->filename);
963 fclose (file);
964
965 }
966 }
967 else
968 {
969 g_warning("Error creating tile download directory: %s",
970 dl->folder);
971 perror("perror:");
972 }
973 }
974
975 if (dl->redraw)
976 {
977 GdkPixbuf *pixbuf = NULL;
978
979 /* if the file was actually stored on disk, we can simply */
980 /* load and decode it from that file */
981 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
1015 /* 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 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 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 #ifdef LIBSOUP22
1119 soup_message_headers_append(msg->request_headers,
1120 "User-Agent", USER_AGENT);
1121 #endif
1122
1123 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 GdkPixbuf *pixbuf = NULL;
1141 OsmCachedTile *tile;
1142
1143 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
1150 tile = g_hash_table_lookup (priv->tile_cache, filename);
1151 if (tile)
1152 {
1153 g_free (filename);
1154 }
1155 else
1156 {
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 }
1172
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 gchar *filename;
1236 GdkPixbuf *pixbuf;
1237
1238 g_debug("Load tile %d,%d (%d,%d) z:%d", x, y, offset_x, offset_y, zoom);
1239
1240 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
1245 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 pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
1255
1256 if(pixbuf)
1257 {
1258 g_debug("Found tile %s", filename);
1259 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 g_free(filename);
1290 }
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
1438 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 static gboolean
1450 osm_gps_map_purge_cache_check(gpointer key, gpointer value, gpointer user)
1451 {
1452 return (((OsmCachedTile*)value)->redraw_cycle != ((OsmGpsMapPrivate*)user)->redraw_cycle);
1453 }
1454
1455 static void
1456 osm_gps_map_purge_cache (OsmGpsMap *map)
1457 {
1458 OsmGpsMapPrivate *priv = map->priv;
1459
1460 if (g_hash_table_size (priv->tile_cache) < priv->max_tile_cache_size)
1461 return;
1462
1463 /* 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 }
1467
1468 static gboolean
1469 osm_gps_map_map_redraw (OsmGpsMap *map)
1470 {
1471 OsmGpsMapPrivate *priv = map->priv;
1472
1473 priv->idle_map_redraw = 0;
1474
1475 /* 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 #ifdef DRAG_DEBUG
1482 printf("trying redraw\n");
1483 #endif
1484
1485 /* 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 /* undo all offsets that may have happened when dragging */
1493 priv->drag_mouse_dx = 0;
1494 priv->drag_mouse_dy = 0;
1495
1496 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 #ifdef ENABLE_BALLOON
1513 osm_gps_map_draw_balloon_int(map);
1514 #endif
1515
1516 #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 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 #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 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 priv->gps_heading = OSM_GPS_MAP_INVALID;
1631
1632 #ifdef ENABLE_BALLOON
1633 priv->balloon.coo = g_new0(coord_t, 1);
1634 priv->balloon.valid = FALSE;
1635 priv->balloon.cb = NULL;
1636 #endif
1637
1638 #ifdef ENABLE_OSD
1639 priv->osd = NULL;
1640 #endif
1641
1642 #ifdef OSM_GPS_MAP_BUTTON_FULLSCREEN
1643 priv->fullscreen = FALSE;
1644 #endif
1645
1646 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 priv->map_source = -1;
1659
1660 #ifndef LIBSOUP22
1661 //Change naumber of concurrent connections option?
1662 priv->soup_session =
1663 soup_session_async_new_with_options(SOUP_SESSION_USER_AGENT,
1664 USER_AGENT, NULL);
1665 #else
1666 /* 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 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 #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 }
1696
1697 static void
1698 osm_gps_map_setup(OsmGpsMapPrivate *priv) {
1699 const char *uri;
1700
1701 //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 const char *fname = osm_gps_map_source_get_friendly_name(priv->map_source);
1726 if(!fname) fname = "_unknown_";
1727
1728 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 }
1733 }
1734
1735 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
1742 osm_gps_map_setup(OSM_GPS_MAP_PRIVATE(object));
1743
1744 inspect_map_uri(OSM_GPS_MAP(object));
1745
1746 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 if (priv->null_tile)
1773 g_object_unref (priv->null_tile);
1774
1775 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 g_free(priv->gps);
1782
1783 #ifdef ENABLE_BALLOON
1784 g_free(priv->balloon.coo);
1785 #endif
1786
1787 #ifdef ENABLE_OSD
1788 if(priv->osd)
1789 priv->osd->free(priv->osd);
1790
1791 #ifdef OSD_DOUBLE_BUFFER
1792 if(priv->dbuf_pixmap)
1793 g_object_unref (priv->dbuf_pixmap);
1794 #endif
1795 #endif
1796
1797 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 if(priv->tile_dir)
1807 g_free(priv->tile_dir);
1808
1809 if(priv->cache_dir)
1810 g_free(priv->cache_dir);
1811
1812 g_free(priv->repo_uri);
1813 g_free(priv->image_format);
1814
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
1850 #ifndef LIBSOUP22
1851 GValue val = {0};
1852
1853 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 if ( g_value_get_string(value) )
1868 priv->tile_dir = g_value_dup_string (value);
1869 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 case PROP_MAP_SOURCE: {
1897 gint old = priv->map_source;
1898 priv->map_source = g_value_get_int (value);
1899 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 osm_gps_map_setup(priv);
1910
1911 inspect_map_uri(map);
1912
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 case PROP_IMAGE_FORMAT:
1922 priv->image_format = g_value_dup_string (value);
1923 break;
1924 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 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 default:
2005 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2006 break;
2007 }
2008 }
2009
2010 static gboolean
2011 osm_gps_map_scroll_event (GtkWidget *widget, GdkEventScroll *event)
2012 {
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 #ifdef ENABLE_BALLOON
2034 /* 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 #endif
2043
2044 #ifdef ENABLE_OSD
2045 /* pressed inside OSD control? */
2046 if(priv->osd) {
2047 osd_button_t but = priv->osd->check(priv->osd, event->x, event->y);
2048 if(but != OSD_NONE)
2049 {
2050 int step = GTK_WIDGET(widget)->allocation.width/OSM_GPS_MAP_SCROLL_STEP;
2051 priv->drag_counter = -1;
2052
2053 switch(but) {
2054 case OSD_UP:
2055 priv->map_y -= step;
2056 priv->center_coord_set = FALSE;
2057 g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
2058 osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
2059 break;
2060
2061 case OSD_DOWN:
2062 priv->map_y += step;
2063 priv->center_coord_set = FALSE;
2064 g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
2065 osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
2066 break;
2067
2068 case OSD_LEFT:
2069 priv->map_x -= step;
2070 priv->center_coord_set = FALSE;
2071 g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
2072 osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
2073 break;
2074
2075 case OSD_RIGHT:
2076 priv->map_x += step;
2077 priv->center_coord_set = FALSE;
2078 g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
2079 osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
2080 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 }
2099 }
2100 #endif
2101
2102 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 #ifdef ENABLE_BALLOON
2117 /* 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 #endif
2127
2128 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 #ifdef DRAG_DEBUG
2144 printf("dragging done\n");
2145 #endif
2146
2147 priv->drag_counter = -1;
2148
2149 return FALSE;
2150 }
2151
2152 static gboolean
2153 osm_gps_map_expose (GtkWidget *widget, GdkEventExpose *event);
2154
2155 static gboolean
2156 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 if (priv->drag_counter < 0)
2176 return FALSE;
2177
2178 priv->drag_counter++;
2179
2180 // we havent dragged more than 6 pixels
2181 if (priv->drag_counter < 6)
2182 return FALSE;
2183
2184 #ifdef OSM_GPS_MAP_REFRESH
2185 /* reduce update frequency on maemo to keep screen update fluid */
2186 static guint32 last_time = 0;
2187
2188 if(event->time - last_time < (1000/OSM_GPS_MAP_REFRESH)) return FALSE;
2189 last_time = event->time;
2190 #endif
2191
2192 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 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 widget->window,
2216 widget->allocation.width + EXTRA_BORDER * 2,
2217 widget->allocation.height + EXTRA_BORDER * 2,
2218 -1);
2219
2220 #ifdef ENABLE_OSD
2221
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 /* the osd needs some references to map internal objects */
2234 if(priv->osd)
2235 priv->osd->widget = widget;
2236 #endif
2237
2238 /* 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 #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 #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 {
2266 #ifdef DRAG_DEBUG
2267 printf(" dragging = %d, event = %p\n", priv->dragging, event);
2268 #endif
2269
2270 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 }
2277 else
2278 {
2279 #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 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 }
2331
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 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
2340 priv->dbuf_pixmap,
2341 0,0,0,0,-1,-1);
2342 #endif
2343
2344 #endif
2345
2346 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 widget_class->scroll_event = osm_gps_map_scroll_event;
2369
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 "map source tile repository uri",
2407 OSM_REPO_URI,
2408 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2409
2410 g_object_class_install_property (object_class,
2411 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 NULL,
2424 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 OSM_MAX_ZOOM,
2444 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 OSM_MIN_ZOOM,
2454 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 10,
2524 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 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 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2545
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 }
2554
2555 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 return "OpenStreetMap I";
2564 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
2565 return "OpenStreetMap II";
2566 case OSM_GPS_MAP_SOURCE_OPENCYCLEMAP:
2567 return "OpenCycleMap";
2568 case OSM_GPS_MAP_SOURCE_OSMC_TRAILS:
2569 return "OSMC Trails";
2570 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 case OSM_GPS_MAP_SOURCE_OPENCYCLEMAP:
2612 return "http://c.andy.sandbox.cloudmade.com/tiles/cycle/#Z/#X/#Y.png";
2613 case OSM_GPS_MAP_SOURCE_OSMC_TRAILS:
2614 return "http://topo.geofabrik.de/trails/#Z/#X/#Y.png";
2615 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 case OSM_GPS_MAP_SOURCE_OPENCYCLEMAP:
2653 case OSM_GPS_MAP_SOURCE_OSMC_TRAILS:
2654 return "png";
2655 case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
2656 case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
2657 case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
2658 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 case OSM_GPS_MAP_SOURCE_OPENCYCLEMAP:
2687 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 case OSM_GPS_MAP_SOURCE_OSMC_TRAILS:
2699 return 15;
2700 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 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 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 {
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 double factor = 0.0;
2809 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 #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 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 im->h = gdk_pixbuf_get_height(image);
2886 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 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 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 priv->gps_heading = deg2rad(heading);
2942
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 #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 osm_gps_map_map_redraw_idle (map);
3055 }
3056
3057 float
3058 osm_gps_map_get_scale(OsmGpsMap *map)
3059 {
3060 OsmGpsMapPrivate *priv;
3061
3062 g_return_val_if_fail (OSM_IS_GPS_MAP (map), OSM_GPS_MAP_INVALID);
3063 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 #ifdef ENABLE_BALLOON
3069 void
3070 osm_gps_map_draw_balloon (OsmGpsMap *map, float latitude, float longitude,
3071 OsmGpsMapBalloonCallback cb, gpointer data)
3072 {
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 priv->balloon.coo->rlat = deg2rad(latitude);
3082 priv->balloon.coo->rlon = deg2rad(longitude);
3083 priv->balloon.valid = TRUE;
3084
3085 priv->balloon.cb = cb;
3086 priv->balloon.data = data;
3087
3088 // 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 priv->balloon.valid = FALSE;
3101
3102 osm_gps_map_map_redraw_idle(map);
3103 }
3104 #endif
3105
3106 #ifdef ENABLE_OSD
3107
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 OsmGpsMapPrivate *priv;
3121
3122 g_return_if_fail (OSM_IS_GPS_MAP (map));
3123
3124 priv = map->priv;
3125 g_return_if_fail (!priv->osd);
3126
3127 priv->osd = osd;
3128 }
3129
3130 void
3131 osm_gps_map_repaint (OsmGpsMap *map) {
3132 osm_gps_map_expose (GTK_WIDGET(map), NULL);
3133 }
3134
3135 #endif