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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 231 - (show 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 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
2 /* vim:set et sw=4 ts=4 cino=t0,(0: */
3 /*
4 * osm-gps-map.c
5 * Copyright (C) Marcus Bauer 2008 <marcus.bauer@gmail.com>
6 * Copyright (C) John Stowers 2009 <john.stowers@gmail.com>
7 *
8 * Contributions by
9 * Everaldo Canuto 2009 <everaldo.canuto@gmail.com>
10 *
11 * osm-gps-map.c is free software: you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * osm-gps-map.c is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 * See the GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with this program. If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "config.h"
26
27 #include <fcntl.h>
28 #include <math.h>
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include <gdk/gdk.h>
35 #include <glib.h>
36 #include <glib/gstdio.h>
37 #include <glib/gprintf.h>
38 #include <libsoup/soup.h>
39
40 #include "converter.h"
41 #include "osm-gps-map-types.h"
42 #include "osm-gps-map.h"
43
44 #ifdef USE_CAIRO
45 #include <cairo.h>
46 #endif
47
48 #define ENABLE_DEBUG 0
49
50 #define EXTRA_BORDER (TILESIZE / 2)
51
52 #define OSM_GPS_MAP_SCROLL_STEP 10
53
54 /* any defined key enables key support */
55 #if (defined(OSM_GPS_MAP_KEY_FULLSCREEN) || \
56 defined(OSM_GPS_MAP_KEY_ZOOMIN) || \
57 defined(OSM_GPS_MAP_KEY_ZOOMOUT) || \
58 defined(OSM_GPS_MAP_KEY_UP) || \
59 defined(OSM_GPS_MAP_KEY_DOWN) || \
60 defined(OSM_GPS_MAP_KEY_LEFT) || \
61 defined(OSM_GPS_MAP_KEY_RIGHT))
62 #define OSM_GPS_MAP_KEYS
63 #endif
64
65 #ifdef OSM_GPS_MAP_KEYS
66 #include <gdk/gdkkeysyms.h>
67 #endif
68
69 #define USER_AGENT "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11"
70
71 struct _OsmGpsMapPrivate
72 {
73 GHashTable *tile_queue;
74 GHashTable *missing_tiles;
75 GHashTable *tile_cache;
76
77 int map_zoom;
78 int max_zoom;
79 int min_zoom;
80 gboolean map_auto_center;
81 gboolean map_auto_download;
82 int map_x;
83 int map_y;
84
85 /* Latitude and longitude of the center of the map, in radians */
86 gfloat center_rlat;
87 gfloat center_rlon;
88
89 guint max_tile_cache_size;
90 /* Incremented at each redraw */
91 guint redraw_cycle;
92 /* ID of the idle redraw operation */
93 guint idle_map_redraw;
94
95 //how we download tiles
96 SoupSession *soup_session;
97 char *proxy_uri;
98
99 //where downloaded tiles are cached
100 char *tile_dir;
101 char *cache_dir;
102
103 //contains flags indicating the various special characters
104 //the uri string contains, that will be replaced when calculating
105 //the uri to download.
106 OsmGpsMapSource_t map_source;
107 char *repo_uri;
108 char *image_format;
109 int uri_format;
110 //flag indicating if the map source is located on the google
111 gboolean the_google;
112
113 //gps tracking state
114 gboolean record_trip_history;
115 gboolean show_trip_history;
116 GSList *trip_history;
117 coord_t *gps;
118 float gps_heading;
119 gboolean gps_valid;
120
121 #ifdef ENABLE_OSD
122 //the osd controls (if present)
123 osm_gps_map_osd_t *osd;
124 #ifdef OSD_DOUBLE_BUFFER
125 GdkPixmap *dbuf_pixmap;
126 #endif
127 #endif
128
129 #ifdef OSM_GPS_MAP_KEY_FULLSCREEN
130 gboolean fullscreen;
131 #endif
132
133 //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 GdkPixbuf *null_tile;
143
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 guint drag_expose;
153
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 PROP_GPS_POINT_R2,
196 PROP_MAP_SOURCE,
197 PROP_IMAGE_FORMAT
198 };
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 #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 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 priv->uri_format = 0;
346 priv->the_google = FALSE;
347
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 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 /* 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 int mr = MAX(3*r,r2);
584
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 // 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 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
644 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 }
684
685 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 g_debug("Queing redraw @ %d,%d (w:%d h:%d)", offset_x,offset_y, TILESIZE,TILESIZE);
691
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 FILE *file;
723 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 file = g_fopen(dl->filename, "wb");
736 if (file != NULL)
737 {
738 fwrite (MSG_RESPONSE_BODY(msg), 1, MSG_RESPONSE_LEN(msg), file);
739 file_saved = TRUE;
740 g_debug("Wrote "MSG_RESPONSE_LEN_FORMAT" bytes to %s", MSG_RESPONSE_LEN(msg), dl->filename);
741 fclose (file);
742
743 }
744 }
745 else
746 {
747 g_warning("Error creating tile download directory: %s",
748 dl->folder);
749 perror("perror:");
750 }
751 }
752
753 if (dl->redraw)
754 {
755 GdkPixbuf *pixbuf = NULL;
756
757 /* if the file was actually stored on disk, we can simply */
758 /* load and decode it from that file */
759 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
793 /* 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 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 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 #ifdef LIBSOUP22
897 soup_message_headers_append(msg->request_headers,
898 "User-Agent", USER_AGENT);
899 #endif
900
901 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 GdkPixbuf *pixbuf = NULL;
919 OsmCachedTile *tile;
920
921 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
928 tile = g_hash_table_lookup (priv->tile_cache, filename);
929 if (tile)
930 {
931 g_free (filename);
932 }
933 else
934 {
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 }
950
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 gchar *filename;
1014 GdkPixbuf *pixbuf;
1015
1016 g_debug("Load tile %d,%d (%d,%d) z:%d", x, y, offset_x, offset_y, zoom);
1017
1018 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
1023 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 pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
1033
1034 if(pixbuf)
1035 {
1036 g_debug("Found tile %s", filename);
1037 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 g_free(filename);
1068 }
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
1216 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 static gboolean
1228 osm_gps_map_purge_cache_check(gpointer key, gpointer value, gpointer user)
1229 {
1230 return (((OsmCachedTile*)value)->redraw_cycle != ((OsmGpsMapPrivate*)user)->redraw_cycle);
1231 }
1232
1233 static void
1234 osm_gps_map_purge_cache (OsmGpsMap *map)
1235 {
1236 OsmGpsMapPrivate *priv = map->priv;
1237
1238 if (g_hash_table_size (priv->tile_cache) < priv->max_tile_cache_size)
1239 return;
1240
1241 /* 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 }
1245
1246 static gboolean
1247 osm_gps_map_map_redraw (OsmGpsMap *map)
1248 {
1249 OsmGpsMapPrivate *priv = map->priv;
1250
1251 /* on diablo the map comes up at 1x1 pixel size and */
1252 /* 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 return FALSE;
1257 }
1258
1259 priv->idle_map_redraw = 0;
1260
1261 #ifdef ENABLE_OSD
1262 /* 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 #endif
1268
1269 #ifdef DRAG_DEBUG
1270 printf("trying redraw\n");
1271 #endif
1272
1273 /* 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 /* undo all offsets that may have happened when dragging */
1281 priv->drag_mouse_dx = 0;
1282 priv->drag_mouse_dy = 0;
1283
1284 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
1301 #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 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 #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 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 priv->gps_heading = OSM_GPS_MAP_INVALID;
1416
1417 #ifdef ENABLE_OSD
1418 priv->osd = NULL;
1419 #endif
1420
1421 #ifdef OSM_GPS_MAP_BUTTON_FULLSCREEN
1422 priv->fullscreen = FALSE;
1423 #endif
1424
1425 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 priv->map_source = -1;
1438
1439 #ifndef LIBSOUP22
1440 //Change naumber of concurrent connections option?
1441 priv->soup_session =
1442 soup_session_async_new_with_options(SOUP_SESSION_USER_AGENT,
1443 USER_AGENT, NULL);
1444 #else
1445 /* 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 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 #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 }
1475
1476 static void
1477 osm_gps_map_setup(OsmGpsMapPrivate *priv) {
1478 const char *uri;
1479
1480 //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 const char *fname = osm_gps_map_source_get_friendly_name(priv->map_source);
1505 if(!fname) fname = "_unknown_";
1506
1507 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 }
1512 }
1513
1514 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
1521 osm_gps_map_setup(OSM_GPS_MAP_PRIVATE(object));
1522
1523 inspect_map_uri(OSM_GPS_MAP(object));
1524
1525 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 if (priv->null_tile)
1552 g_object_unref (priv->null_tile);
1553
1554 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 if (priv->drag_expose != 0)
1561 g_source_remove (priv->drag_expose);
1562
1563 g_free(priv->gps);
1564
1565 #ifdef ENABLE_OSD
1566 if(priv->osd)
1567 priv->osd->free(priv->osd);
1568
1569 #ifdef OSD_DOUBLE_BUFFER
1570 if(priv->dbuf_pixmap)
1571 g_object_unref (priv->dbuf_pixmap);
1572 #endif
1573 #endif
1574
1575 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 if(priv->tile_dir)
1585 g_free(priv->tile_dir);
1586
1587 if(priv->cache_dir)
1588 g_free(priv->cache_dir);
1589
1590 g_free(priv->repo_uri);
1591 g_free(priv->image_format);
1592
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
1628 #ifndef LIBSOUP22
1629 GValue val = {0};
1630
1631 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 if ( g_value_get_string(value) )
1646 priv->tile_dir = g_value_dup_string (value);
1647 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 case PROP_MAP_SOURCE: {
1675 gint old = priv->map_source;
1676 priv->map_source = g_value_get_int (value);
1677 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 osm_gps_map_setup(priv);
1688
1689 inspect_map_uri(map);
1690
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 case PROP_IMAGE_FORMAT:
1700 priv->image_format = g_value_dup_string (value);
1701 break;
1702 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 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 default:
1783 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1784 break;
1785 }
1786 }
1787
1788 static gboolean
1789 osm_gps_map_scroll_event (GtkWidget *widget, GdkEventScroll *event)
1790 {
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 #ifdef ENABLE_OSD
1812 /* pressed inside OSD control? */
1813 if(priv->osd) {
1814 osd_button_t but =
1815 priv->osd->check(priv->osd, TRUE, event->x, event->y);
1816
1817 if(but != OSD_NONE)
1818 {
1819 int step =
1820 GTK_WIDGET(widget)->allocation.width/OSM_GPS_MAP_SCROLL_STEP;
1821 priv->drag_counter = -1;
1822
1823 switch(but) {
1824 case OSD_UP:
1825 priv->map_y -= step;
1826 priv->center_coord_set = FALSE;
1827 g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
1828 osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
1829 break;
1830
1831 case OSD_DOWN:
1832 priv->map_y += step;
1833 priv->center_coord_set = FALSE;
1834 g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
1835 osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
1836 break;
1837
1838 case OSD_LEFT:
1839 priv->map_x -= step;
1840 priv->center_coord_set = FALSE;
1841 g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
1842 osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
1843 break;
1844
1845 case OSD_RIGHT:
1846 priv->map_x += step;
1847 priv->center_coord_set = FALSE;
1848 g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
1849 osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
1850 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 }
1869 }
1870 #endif
1871
1872 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 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 #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
1906 #ifdef DRAG_DEBUG
1907 printf("dragging done\n");
1908 #endif
1909
1910 priv->drag_counter = -1;
1911
1912 return FALSE;
1913 }
1914
1915 static gboolean
1916 osm_gps_map_expose (GtkWidget *widget, GdkEventExpose *event);
1917
1918 static gboolean
1919 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 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 if (priv->drag_counter < 0)
1949 return FALSE;
1950
1951 /* 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 return FALSE;
1957
1958 priv->drag_counter++;
1959
1960 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 /* 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
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 widget->window,
1987 widget->allocation.width + EXTRA_BORDER * 2,
1988 widget->allocation.height + EXTRA_BORDER * 2,
1989 -1);
1990
1991 #ifdef ENABLE_OSD
1992
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 /* the osd needs some references to map internal objects */
2005 if(priv->osd)
2006 priv->osd->widget = widget;
2007 #endif
2008
2009 /* 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 #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 #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 {
2037 #ifdef DRAG_DEBUG
2038 printf(" dragging = %d, event = %p\n", priv->dragging, event);
2039 #endif
2040
2041 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 }
2048 else
2049 {
2050 #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 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 }
2102
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 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
2111 priv->dbuf_pixmap,
2112 0,0,0,0,-1,-1);
2113 #endif
2114
2115 #endif
2116
2117 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 widget_class->scroll_event = osm_gps_map_scroll_event;
2140
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 "map source tile repository uri",
2178 OSM_REPO_URI,
2179 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2180
2181 g_object_class_install_property (object_class,
2182 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 NULL,
2195 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 OSM_MAX_ZOOM,
2215 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 OSM_MIN_ZOOM,
2225 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 10,
2295 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 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 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2316
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 }
2325
2326 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 return "OpenStreetMap I";
2335 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
2336 return "OpenStreetMap II";
2337 case OSM_GPS_MAP_SOURCE_OPENCYCLEMAP:
2338 return "OpenCycleMap";
2339 case OSM_GPS_MAP_SOURCE_OSMC_TRAILS:
2340 return "OSMC Trails";
2341 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 case OSM_GPS_MAP_SOURCE_OPENCYCLEMAP:
2383 return "http://c.andy.sandbox.cloudmade.com/tiles/cycle/#Z/#X/#Y.png";
2384 case OSM_GPS_MAP_SOURCE_OSMC_TRAILS:
2385 return "http://topo.geofabrik.de/trails/#Z/#X/#Y.png";
2386 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 case OSM_GPS_MAP_SOURCE_OPENCYCLEMAP:
2424 case OSM_GPS_MAP_SOURCE_OSMC_TRAILS:
2425 return "png";
2426 case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
2427 case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
2428 case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
2429 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 case OSM_GPS_MAP_SOURCE_OPENCYCLEMAP:
2458 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 case OSM_GPS_MAP_SOURCE_OSMC_TRAILS:
2470 return 15;
2471 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 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 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 {
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 double factor = 0.0;
2580 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 #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 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 im->h = gdk_pixbuf_get_height(image);
2657 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 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 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 priv->gps_heading = deg2rad(heading);
2713
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 *pixel_x = lon2pixel(priv->map_zoom, deg2rad(longitude)) -
2803 priv->map_x + priv->drag_mouse_dx;
2804 if (pixel_y)
2805 *pixel_y = lat2pixel(priv->map_zoom, deg2rad(latitude)) -
2806 priv->map_y + priv->drag_mouse_dy;
2807 }
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 #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 osm_gps_map_map_redraw_idle (map);
2828 }
2829
2830 float
2831 osm_gps_map_get_scale(OsmGpsMap *map)
2832 {
2833 OsmGpsMapPrivate *priv;
2834
2835 g_return_val_if_fail (OSM_IS_GPS_MAP (map), OSM_GPS_MAP_INVALID);
2836 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 #ifdef ENABLE_OSD
2842
2843 void
2844 osm_gps_map_redraw (OsmGpsMap *map)
2845 {
2846 osm_gps_map_map_redraw_idle(map);
2847 }
2848
2849 osm_gps_map_osd_t *
2850 osm_gps_map_osd_get(OsmGpsMap *map)
2851 {
2852 g_return_val_if_fail (OSM_IS_GPS_MAP (map), NULL);
2853 return map->priv->osd;
2854 }
2855
2856 void
2857 osm_gps_map_register_osd(OsmGpsMap *map, osm_gps_map_osd_t *osd)
2858 {
2859 OsmGpsMapPrivate *priv;
2860
2861 g_return_if_fail (OSM_IS_GPS_MAP (map));
2862
2863 priv = map->priv;
2864 g_return_if_fail (!priv->osd);
2865
2866 priv->osd = osd;
2867 }
2868
2869 void
2870 osm_gps_map_repaint (OsmGpsMap *map)
2871 {
2872 osm_gps_map_expose (GTK_WIDGET(map), NULL);
2873 }
2874
2875 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 #endif