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

Parent Directory Parent Directory | Revision Log Revision Log


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