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

Parent Directory Parent Directory | Revision Log Revision Log


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