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

Parent Directory Parent Directory | Revision Log Revision Log


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