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

Parent Directory Parent Directory | Revision Log Revision Log


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