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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 32 - (show annotations)
Tue Jul 28 13:21:22 2009 UTC (14 years, 9 months ago) by harbaum
File MIME type: text/plain
File size: 76118 byte(s)
osm-gps-map integration
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/gprintf.h>
37 #include <libsoup/soup.h>
38
39 #include "converter.h"
40 #include "osm-gps-map-types.h"
41 #include "osm-gps-map.h"
42
43 #ifdef USE_CAIRO
44 #include <cairo.h>
45 #endif
46
47 #define ENABLE_DEBUG 0
48
49 #define EXTRA_BORDER (TILESIZE / 2)
50
51 struct _OsmGpsMapPrivate
52 {
53 GHashTable *tile_queue;
54 GHashTable *missing_tiles;
55 GHashTable *tile_cache;
56
57 int map_zoom;
58 int max_zoom;
59 int min_zoom;
60 gboolean map_auto_center;
61 gboolean map_auto_download;
62 int map_x;
63 int map_y;
64
65 /* Latitude and longitude of the center of the map, in radians */
66 gfloat center_rlat;
67 gfloat center_rlon;
68
69 guint max_tile_cache_size;
70 /* Incremented at each redraw */
71 guint redraw_cycle;
72 /* ID of the idle redraw operation */
73 guint idle_map_redraw;
74
75 //how we download tiles
76 SoupSession *soup_session;
77 char *proxy_uri;
78
79 //where downloaded tiles are cached
80 char *cache_dir;
81 gboolean cache_dir_is_full_path;
82
83 //contains flags indicating the various special characters
84 //the uri string contains, that will be replaced when calculating
85 //the uri to download.
86 char *repo_uri;
87 int uri_format;
88 //flag indicating if the map source is located on the google
89 gboolean the_google;
90
91 //gps tracking state
92 gboolean record_trip_history;
93 gboolean show_trip_history;
94 GSList *trip_history;
95 coord_t *gps;
96 gboolean gps_valid;
97
98 //additional images or tracks added to the map
99 GSList *tracks;
100 GSList *images;
101
102 //Used for storing the joined tiles
103 GdkPixmap *pixmap;
104 GdkGC *gc_map;
105
106 //The tile painted when one cannot be found
107 //GdkPixbuf *missing_tile;
108
109 //For tracking click and drag
110 int drag_counter;
111 int drag_mouse_dx;
112 int drag_mouse_dy;
113 int drag_start_mouse_x;
114 int drag_start_mouse_y;
115 int drag_start_map_x;
116 int drag_start_map_y;
117
118 //for customizing the redering of the gps track
119 int ui_gps_track_width;
120 int ui_gps_point_inner_radius;
121 int ui_gps_point_outer_radius;
122
123 guint is_disposed : 1;
124 guint dragging : 1;
125 guint center_coord_set : 1;
126 };
127
128 #define OSM_GPS_MAP_PRIVATE(o) (OSM_GPS_MAP (o)->priv)
129
130 typedef struct
131 {
132 GdkPixbuf *pixbuf;
133 /* We keep track of the number of the redraw cycle this tile was last used,
134 * so that osm_gps_map_purge_cache() can remove the older ones */
135 guint redraw_cycle;
136 } OsmCachedTile;
137
138 enum
139 {
140 PROP_0,
141
142 PROP_AUTO_CENTER,
143 PROP_RECORD_TRIP_HISTORY,
144 PROP_SHOW_TRIP_HISTORY,
145 PROP_AUTO_DOWNLOAD,
146 PROP_REPO_URI,
147 PROP_PROXY_URI,
148 PROP_TILE_CACHE_DIR,
149 PROP_TILE_CACHE_DIR_IS_FULL_PATH,
150 PROP_ZOOM,
151 PROP_MAX_ZOOM,
152 PROP_MIN_ZOOM,
153 PROP_LATITUDE,
154 PROP_LONGITUDE,
155 PROP_MAP_X,
156 PROP_MAP_Y,
157 PROP_TILES_QUEUED,
158 PROP_GPS_TRACK_WIDTH,
159 PROP_GPS_POINT_R1,
160 PROP_GPS_POINT_R2
161 };
162
163 G_DEFINE_TYPE (OsmGpsMap, osm_gps_map, GTK_TYPE_DRAWING_AREA);
164
165 /*
166 * Drawing function forward defintions
167 */
168 static gchar *replace_string(const gchar *src, const gchar *from, const gchar *to);
169 static void inspect_map_uri(OsmGpsMap *map);
170 static gchar *replace_map_uri(OsmGpsMap *map, const gchar *uri, int zoom, int x, int y);
171 static void osm_gps_map_print_images (OsmGpsMap *map);
172 static void osm_gps_map_draw_gps_point (OsmGpsMap *map);
173 static void osm_gps_map_blit_tile(OsmGpsMap *map, GdkPixbuf *pixbuf, int offset_x, int offset_y);
174 static void osm_gps_map_download_tile (OsmGpsMap *map, int zoom, int x, int y, gboolean redraw);
175 static void osm_gps_map_load_tile (OsmGpsMap *map, int zoom, int x, int y, int offset_x, int offset_y);
176 static void osm_gps_map_fill_tiles_pixel (OsmGpsMap *map);
177 static gboolean osm_gps_map_map_redraw (OsmGpsMap *map);
178 static void osm_gps_map_map_redraw_idle (OsmGpsMap *map);
179
180 static void
181 cached_tile_free (OsmCachedTile *tile)
182 {
183 g_object_unref (tile->pixbuf);
184 g_slice_free (OsmCachedTile, tile);
185 }
186
187 /*
188 * Description:
189 * Find and replace text within a string.
190 *
191 * Parameters:
192 * src (in) - pointer to source string
193 * from (in) - pointer to search text
194 * to (in) - pointer to replacement text
195 *
196 * Returns:
197 * Returns a pointer to dynamically-allocated memory containing string
198 * with occurences of the text pointed to by 'from' replaced by with the
199 * text pointed to by 'to'.
200 */
201 static gchar *
202 replace_string(const gchar *src, const gchar *from, const gchar *to)
203 {
204 size_t size = strlen(src) + 1;
205 size_t fromlen = strlen(from);
206 size_t tolen = strlen(to);
207
208 /* Allocate the first chunk with enough for the original string. */
209 gchar *value = g_malloc(size);
210
211
212 /* We need to return 'value', so let's make a copy to mess around with. */
213 gchar *dst = value;
214
215 if ( value != NULL )
216 {
217 for ( ;; )
218 {
219 /* Try to find the search text. */
220 const gchar *match = g_strstr_len(src, size, from);
221 if ( match != NULL )
222 {
223 gchar *temp;
224 /* Find out how many characters to copy up to the 'match'. */
225 size_t count = match - src;
226
227
228 /* Calculate the total size the string will be after the
229 * replacement is performed. */
230 size += tolen - fromlen;
231
232 temp = g_realloc(value, size);
233 if ( temp == NULL )
234 {
235 g_free(value);
236 return NULL;
237 }
238
239 /* we'll want to return 'value' eventually, so let's point it
240 * to the memory that we are now working with.
241 * And let's not forget to point to the right location in
242 * the destination as well. */
243 dst = temp + (dst - value);
244 value = temp;
245
246 /*
247 * Copy from the source to the point where we matched. Then
248 * move the source pointer ahead by the amount we copied. And
249 * move the destination pointer ahead by the same amount.
250 */
251 g_memmove(dst, src, count);
252 src += count;
253 dst += count;
254
255 /* Now copy in the replacement text 'to' at the position of
256 * the match. Adjust the source pointer by the text we replaced.
257 * Adjust the destination pointer by the amount of replacement
258 * text. */
259 g_memmove(dst, to, tolen);
260 src += fromlen;
261 dst += tolen;
262 }
263 else
264 {
265 /*
266 * Copy any remaining part of the string. This includes the null
267 * termination character.
268 */
269 strcpy(dst, src);
270 break;
271 }
272 }
273 }
274 return value;
275 }
276
277 static void
278 map_convert_coords_to_quadtree_string(OsmGpsMap *map, gint x, gint y, gint zoomlevel,
279 gchar *buffer, const gchar initial,
280 const gchar *const quadrant)
281 {
282 gchar *ptr = buffer;
283 gint n;
284
285 if (initial)
286 *ptr++ = initial;
287
288 for(n = zoomlevel-1; n >= 0; n--)
289 {
290 gint xbit = (x >> n) & 1;
291 gint ybit = (y >> n) & 1;
292 *ptr++ = quadrant[xbit + 2 * ybit];
293 }
294
295 *ptr++ = '\0';
296 }
297
298
299 static void
300 inspect_map_uri(OsmGpsMap *map)
301 {
302 OsmGpsMapPrivate *priv = map->priv;
303
304 if (g_strrstr(priv->repo_uri, URI_MARKER_X))
305 priv->uri_format |= URI_HAS_X;
306
307 if (g_strrstr(priv->repo_uri, URI_MARKER_Y))
308 priv->uri_format |= URI_HAS_Y;
309
310 if (g_strrstr(priv->repo_uri, URI_MARKER_Z))
311 priv->uri_format |= URI_HAS_Z;
312
313 if (g_strrstr(priv->repo_uri, URI_MARKER_S))
314 priv->uri_format |= URI_HAS_S;
315
316 if (g_strrstr(priv->repo_uri, URI_MARKER_Q))
317 priv->uri_format |= URI_HAS_Q;
318
319 if (g_strrstr(priv->repo_uri, URI_MARKER_Q0))
320 priv->uri_format |= URI_HAS_Q0;
321
322 if (g_strrstr(priv->repo_uri, URI_MARKER_YS))
323 priv->uri_format |= URI_HAS_YS;
324
325 if (g_strrstr(priv->repo_uri, URI_MARKER_R))
326 priv->uri_format |= URI_HAS_R;
327
328 if (g_strrstr(priv->repo_uri, "google.com"))
329 priv->the_google = TRUE;
330
331 g_debug("URI Format: 0x%X (google: %X)", priv->uri_format, priv->the_google);
332
333 }
334
335 static gchar *
336 replace_map_uri(OsmGpsMap *map, const gchar *uri, int zoom, int x, int y)
337 {
338 OsmGpsMapPrivate *priv = map->priv;
339 char *url;
340 unsigned int i;
341 char location[22];
342
343 i = 1;
344 url = g_strdup(uri);
345 while (i < URI_FLAG_END)
346 {
347 char *s = NULL;
348 char *old;
349
350 old = url;
351 switch(i & priv->uri_format)
352 {
353 case URI_HAS_X:
354 s = g_strdup_printf("%d", x);
355 url = replace_string(url, URI_MARKER_X, s);
356 //g_debug("FOUND " URI_MARKER_X);
357 break;
358 case URI_HAS_Y:
359 s = g_strdup_printf("%d", y);
360 url = replace_string(url, URI_MARKER_Y, s);
361 //g_debug("FOUND " URI_MARKER_Y);
362 break;
363 case URI_HAS_Z:
364 s = g_strdup_printf("%d", zoom);
365 url = replace_string(url, URI_MARKER_Z, s);
366 //g_debug("FOUND " URI_MARKER_Z);
367 break;
368 case URI_HAS_S:
369 s = g_strdup_printf("%d", priv->max_zoom-zoom);
370 url = replace_string(url, URI_MARKER_S, s);
371 //g_debug("FOUND " URI_MARKER_S);
372 break;
373 case URI_HAS_Q:
374 map_convert_coords_to_quadtree_string(map,x,y,zoom,location,'t',"qrts");
375 s = g_strdup_printf("%s", location);
376 url = replace_string(url, URI_MARKER_Q, s);
377 //g_debug("FOUND " URI_MARKER_Q);
378 break;
379 case URI_HAS_Q0:
380 map_convert_coords_to_quadtree_string(map,x,y,zoom,location,'\0', "0123");
381 s = g_strdup_printf("%s", location);
382 url = replace_string(url, URI_MARKER_Q0, s);
383 //g_debug("FOUND " URI_MARKER_Q0);
384 break;
385 case URI_HAS_YS:
386 // s = g_strdup_printf("%d", y);
387 // url = replace_string(url, URI_MARKER_YS, s);
388 g_warning("FOUND " URI_MARKER_YS " NOT IMPLEMENTED");
389 // retval = g_strdup_printf(repo->url,
390 // tilex,
391 // (1 << (MAX_ZOOM - zoom)) - tiley - 1,
392 // zoom - (MAX_ZOOM - 17));
393 break;
394 case URI_HAS_R:
395 s = g_strdup_printf("%d", g_random_int_range(0,4));
396 url = replace_string(url, URI_MARKER_R, s);
397 //g_debug("FOUND " URI_MARKER_R);
398 break;
399 default:
400 s = NULL;
401 break;
402 }
403
404 if (s) {
405 g_free(s);
406 g_free(old);
407 }
408
409 i = (i << 1);
410
411 }
412
413 return url;
414 }
415
416 static void
417 my_log_handler (const gchar * log_domain, GLogLevelFlags log_level, const gchar * message, gpointer user_data)
418 {
419 if (!(log_level & G_LOG_LEVEL_DEBUG) || ENABLE_DEBUG)
420 g_log_default_handler (log_domain, log_level, message, user_data);
421 }
422
423 /* clears the trip list and all resources */
424 static void
425 osm_gps_map_free_trip (OsmGpsMap *map)
426 {
427 OsmGpsMapPrivate *priv = map->priv;
428 if (priv->trip_history) {
429 g_slist_foreach(priv->trip_history, (GFunc) g_free, NULL);
430 g_slist_free(priv->trip_history);
431 priv->trip_history = NULL;
432 }
433 }
434
435 /* clears the tracks and all resources */
436 static void
437 osm_gps_map_free_tracks (OsmGpsMap *map)
438 {
439 OsmGpsMapPrivate *priv = map->priv;
440 if (priv->tracks)
441 {
442 GSList* tmp = priv->tracks;
443 while (tmp != NULL)
444 {
445 g_slist_foreach(tmp->data, (GFunc) g_free, NULL);
446 g_slist_free(tmp->data);
447 tmp = g_slist_next(tmp);
448 }
449 g_slist_free(priv->tracks);
450 priv->tracks = NULL;
451 }
452 }
453
454 /* free the poi image lists */
455 static void
456 osm_gps_map_free_images (OsmGpsMap *map)
457 {
458 OsmGpsMapPrivate *priv = map->priv;
459 if (priv->images) {
460 GSList *list;
461 for(list = priv->images; list != NULL; list = list->next)
462 {
463 image_t *im = list->data;
464 g_object_unref(im->image);
465 g_free(im);
466 }
467 g_slist_free(priv->images);
468 priv->images = NULL;
469 }
470 }
471
472 static void
473 osm_gps_map_print_images (OsmGpsMap *map)
474 {
475 GSList *list;
476 int x,y,pixel_x,pixel_y;
477 int min_x = 0,min_y = 0,max_x = 0,max_y = 0;
478 int map_x0, map_y0;
479 OsmGpsMapPrivate *priv = map->priv;
480
481 map_x0 = priv->map_x - EXTRA_BORDER;
482 map_y0 = priv->map_y - EXTRA_BORDER;
483 for(list = priv->images; list != NULL; list = list->next)
484 {
485 image_t *im = list->data;
486
487 // pixel_x,y, offsets
488 pixel_x = lon2pixel(priv->map_zoom, im->pt.rlon);
489 pixel_y = lat2pixel(priv->map_zoom, im->pt.rlat);
490
491 g_debug("Image %dx%d @: %f,%f (%d,%d)",
492 im->w, im->h,
493 im->pt.rlat, im->pt.rlon,
494 pixel_x, pixel_y);
495
496 x = pixel_x - map_x0;
497 y = pixel_y - map_y0;
498
499 gdk_draw_pixbuf (
500 priv->pixmap,
501 priv->gc_map,
502 im->image,
503 0,0,
504 x-(im->w/2),y-(im->h/2),
505 im->w,im->h,
506 GDK_RGB_DITHER_NONE, 0, 0);
507
508 max_x = MAX(x+im->w,max_x);
509 min_x = MIN(x-im->w,min_x);
510 max_y = MAX(y+im->h,max_y);
511 min_y = MIN(y-im->h,min_y);
512 }
513
514 gtk_widget_queue_draw_area (
515 GTK_WIDGET(map),
516 min_x + EXTRA_BORDER, min_y + EXTRA_BORDER,
517 max_x + EXTRA_BORDER, max_y + EXTRA_BORDER);
518
519 }
520
521 static void
522 osm_gps_map_draw_gps_point (OsmGpsMap *map)
523 {
524 OsmGpsMapPrivate *priv = map->priv;
525
526 //incase we get called before we have got a gps point
527 if (priv->gps_valid) {
528 int map_x0, map_y0;
529 int x, y;
530 int r = priv->ui_gps_point_inner_radius;
531 int r2 = priv->ui_gps_point_outer_radius;
532 int mr = MAX(r,r2);
533
534 map_x0 = priv->map_x - EXTRA_BORDER;
535 map_y0 = priv->map_y - EXTRA_BORDER;
536 x = lon2pixel(priv->map_zoom, priv->gps->rlon) - map_x0;
537 y = lat2pixel(priv->map_zoom, priv->gps->rlat) - map_y0;
538 #ifdef USE_CAIRO
539 cairo_t *cr;
540 cairo_pattern_t *pat;
541 #else
542 int lw = priv->ui_gps_track_width;
543 GdkColor color;
544 GdkGC *marker;
545 #endif
546
547 #ifdef USE_CAIRO
548 cr = gdk_cairo_create(priv->pixmap);
549
550 // draw transparent area
551 if (r2 > 0) {
552 cairo_set_line_width (cr, 1.5);
553 cairo_set_source_rgba (cr, 0.75, 0.75, 0.75, 0.4);
554 cairo_arc (cr, x, y, r2, 0, 2 * M_PI);
555 cairo_fill (cr);
556 // draw transparent area border
557 cairo_set_source_rgba (cr, 0.55, 0.55, 0.55, 0.4);
558 cairo_arc (cr, x, y, r2, 0, 2 * M_PI);
559 cairo_stroke(cr);
560 }
561
562 // draw ball gradient
563 if (r > 0) {
564 pat = cairo_pattern_create_radial (x-(r/5), y-(r/5), (r/5), x, y, r);
565 cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, 1.0);
566 cairo_pattern_add_color_stop_rgba (pat, 1, 0, 0, 1, 1.0);
567 cairo_set_source (cr, pat);
568 cairo_arc (cr, x, y, r, 0, 2 * M_PI);
569 cairo_fill (cr);
570 cairo_pattern_destroy (pat);
571 // draw ball border
572 cairo_set_line_width (cr, 1.0);
573 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
574 cairo_arc (cr, x, y, r, 0, 2 * M_PI);
575 cairo_stroke(cr);
576 }
577
578 cairo_destroy(cr);
579 gtk_widget_queue_draw_area (GTK_WIDGET(map),
580 x-mr,
581 y-mr,
582 mr*2,
583 mr*2);
584 #else
585 marker = gdk_gc_new(priv->pixmap);
586 color.red = 5000;
587 color.green = 5000;
588 color.blue = 55000;
589 gdk_gc_set_rgb_fg_color(marker, &color);
590 gdk_gc_set_line_attributes(marker, lw, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
591
592 if (r2 > 0) {
593 gdk_draw_arc (priv->pixmap,
594 marker,
595 FALSE, //filled
596 x-r2, y-r2, // x,y
597 r2*2,r2*2, // width, height
598 0, 360*64); // start-end angle 64th, from 3h, anti clockwise
599 }
600 if (r > 0) {
601 gdk_draw_arc (priv->pixmap,
602 marker,
603 TRUE, //filled
604 x-r, y-r, // x,y
605 r*2,r*2, // width, height
606 0, 360*64); // start-end angle 64th, from 3h, anti clockwise
607 }
608
609 g_object_unref(marker);
610 gtk_widget_queue_draw_area (GTK_WIDGET(map),
611 x-(mr+lw),
612 y-(mr+lw),
613 (mr*2)+lw+lw,
614 (mr*2)+lw+lw);
615 #endif
616 }
617 }
618
619 static void
620 osm_gps_map_blit_tile(OsmGpsMap *map, GdkPixbuf *pixbuf, int offset_x, int offset_y)
621 {
622 OsmGpsMapPrivate *priv = map->priv;
623
624 g_debug("Queing redraw @ %d,%d (w:%d h:%d)", offset_x,offset_y,
625 TILESIZE,TILESIZE);
626
627 /* draw pixbuf onto pixmap */
628 gdk_draw_pixbuf (priv->pixmap,
629 priv->gc_map,
630 pixbuf,
631 0,0,
632 offset_x,offset_y,
633 TILESIZE,TILESIZE,
634 GDK_RGB_DITHER_NONE, 0, 0);
635 }
636
637 /* libsoup-2.2 and libsoup-2.4 use different ways to store the body data */
638 #ifdef LIBSOUP22
639 #define soup_message_headers_append(a,b,c) soup_message_add_header(a,b,c)
640 #define MSG_RESPONSE_BODY(a) ((a)->response.body)
641 #define MSG_RESPONSE_LEN(a) ((a)->response.length)
642 #define MSG_RESPONSE_LEN_FORMAT "%u"
643 #else
644 #define MSG_RESPONSE_BODY(a) ((a)->response_body->data)
645 #define MSG_RESPONSE_LEN(a) ((a)->response_body->length)
646 #define MSG_RESPONSE_LEN_FORMAT "%lld"
647 #endif
648
649 #ifdef LIBSOUP22
650 static void
651 osm_gps_map_tile_download_complete (SoupMessage *msg, gpointer user_data)
652 #else
653 static void
654 osm_gps_map_tile_download_complete (SoupSession *session, SoupMessage *msg, gpointer user_data)
655 #endif
656 {
657 int fd;
658 tile_download_t *dl = (tile_download_t *)user_data;
659 OsmGpsMap *map = OSM_GPS_MAP(dl->map);
660 OsmGpsMapPrivate *priv = map->priv;
661 gboolean file_saved = FALSE;
662
663 if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
664 {
665 /* save tile into cachedir if one has been specified */
666 if (priv->cache_dir)
667 {
668 if (g_mkdir_with_parents(dl->folder,0700) == 0)
669 {
670 fd = open(dl->filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
671 if (fd != -1)
672 {
673 write (fd, MSG_RESPONSE_BODY(msg), MSG_RESPONSE_LEN(msg));
674 file_saved = TRUE;
675
676 g_debug("Wrote " MSG_RESPONSE_LEN_FORMAT " bytes to %s", MSG_RESPONSE_LEN(msg), dl->filename);
677 close (fd);
678 }
679 }
680 else
681 {
682 g_warning("Error creating tile download directory: %s", dl->folder);
683 }
684 }
685
686 if (dl->redraw)
687 {
688 GdkPixbuf *pixbuf = NULL;
689
690 if (priv->cache_dir)
691 {
692 if (file_saved)
693 {
694 pixbuf = gdk_pixbuf_new_from_file (dl->filename, NULL);
695 }
696 }
697 else
698 {
699 GdkPixbufLoader *loader;
700 char *extension = strrchr (dl->filename, '.');
701
702 /* parse file directly from memory */
703 if (extension)
704 {
705 loader = gdk_pixbuf_loader_new_with_type (extension+1, NULL);
706 if (!gdk_pixbuf_loader_write (loader, (unsigned char*)MSG_RESPONSE_BODY(msg), MSG_RESPONSE_LEN(msg), NULL))
707 {
708 g_warning("Error: Decoding of image failed");
709 }
710 gdk_pixbuf_loader_close(loader, NULL);
711
712 pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
713
714 /* give up loader but keep the pixbuf */
715 g_object_ref(pixbuf);
716 g_object_unref(loader);
717 }
718 else
719 {
720 g_warning("Error: Unable to determine image file format");
721 }
722 }
723
724 /* Store the tile into the cache */
725 if (G_LIKELY (pixbuf))
726 {
727 OsmCachedTile *tile = g_slice_new (OsmCachedTile);
728 tile->pixbuf = pixbuf;
729 tile->redraw_cycle = priv->redraw_cycle;
730 /* if the tile is already in the cache (it could be one
731 * rendered from another zoom level), it will be
732 * overwritten */
733 g_hash_table_insert (priv->tile_cache, dl->filename, tile);
734 /* NULL-ify dl->filename so that it won't be freed, as
735 * we are using it as a key in the hash table */
736 dl->filename = NULL;
737 }
738
739 osm_gps_map_map_redraw_idle (map);
740 }
741 g_hash_table_remove(priv->tile_queue, dl->uri);
742
743 g_free(dl->uri);
744 g_free(dl->folder);
745 g_free(dl->filename);
746 g_free(dl);
747 }
748 else
749 {
750 g_warning("Error downloading tile: %d - %s", msg->status_code, msg->reason_phrase);
751 if (msg->status_code == SOUP_STATUS_NOT_FOUND)
752 {
753 g_hash_table_insert(priv->missing_tiles, dl->uri, NULL);
754 g_hash_table_remove(priv->tile_queue, dl->uri);
755 }
756 else if (msg->status_code == SOUP_STATUS_CANCELLED)
757 {
758 ;//application exiting
759 }
760 else
761 {
762 #ifdef LIBSOUP22
763 soup_session_requeue_message(dl->session, msg);
764 #else
765 soup_session_requeue_message(session, msg);
766 #endif
767 return;
768 }
769 }
770
771
772 }
773
774 static void
775 osm_gps_map_download_tile (OsmGpsMap *map, int zoom, int x, int y, gboolean redraw)
776 {
777 SoupMessage *msg;
778 OsmGpsMapPrivate *priv = map->priv;
779 tile_download_t *dl = g_new0(tile_download_t,1);
780
781 //calculate the uri to download
782 dl->uri = replace_map_uri(map, priv->repo_uri, zoom, x, y);
783
784 #ifdef LIBSOUP22
785 dl->session = priv->soup_session;
786 #endif
787
788 //check the tile has not already been queued for download,
789 //or has been attempted, and its missing
790 if (g_hash_table_lookup_extended(priv->tile_queue, dl->uri, NULL, NULL) ||
791 g_hash_table_lookup_extended(priv->missing_tiles, dl->uri, NULL, NULL) )
792 {
793 g_debug("Tile already downloading (or missing)");
794 g_free(dl->uri);
795 g_free(dl);
796 } else {
797 dl->folder = g_strdup_printf("%s/%d/%d/",priv->cache_dir, zoom, x);
798 dl->filename = g_strdup_printf("%s/%d/%d/%d.png",priv->cache_dir, zoom, x, y);
799 dl->map = map;
800 dl->redraw = redraw;
801
802 g_debug("Download tile: %d,%d z:%d\n\t%s --> %s", x, y, zoom, dl->uri, dl->filename);
803
804 msg = soup_message_new (SOUP_METHOD_GET, dl->uri);
805 if (msg) {
806 if (priv->the_google) {
807 //Set maps.google.com as the referrer
808 g_debug("Setting Google Referrer");
809 soup_message_headers_append(msg->request_headers, "Referer", "http://maps.google.com/");
810 //For google satelite also set the appropriate cookie value
811 if (priv->uri_format & URI_HAS_Q) {
812 const char *cookie = g_getenv("GOOGLE_COOKIE");
813 if (cookie) {
814 g_debug("Adding Google Cookie");
815 soup_message_headers_append(msg->request_headers, "Cookie", cookie);
816
817 }
818 }
819 }
820
821 g_hash_table_insert (priv->tile_queue, dl->uri, msg);
822 soup_session_queue_message (priv->soup_session, msg, osm_gps_map_tile_download_complete, dl);
823 } else {
824 g_warning("Could not create soup message");
825 g_free(dl->uri);
826 g_free(dl->folder);
827 g_free(dl->filename);
828 g_free(dl);
829 }
830 }
831 }
832
833 static GdkPixbuf *
834 osm_gps_map_load_cached_tile (OsmGpsMap *map, int zoom, int x, int y)
835 {
836 OsmGpsMapPrivate *priv = map->priv;
837 gchar *filename;
838 GdkPixbuf *pixbuf;
839 OsmCachedTile *tile;
840
841 filename = g_strdup_printf("%s/%u/%u/%u.png",
842 priv->cache_dir,
843 zoom, x, y);
844
845 tile = g_hash_table_lookup (priv->tile_cache, filename);
846 if (tile)
847 {
848 g_free (filename);
849 }
850 else if (priv->cache_dir)
851 {
852 pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
853 if (pixbuf)
854 {
855 tile = g_slice_new (OsmCachedTile);
856 tile->pixbuf = pixbuf;
857 g_hash_table_insert (priv->tile_cache, filename, tile);
858 }
859 }
860
861 /* set/update the redraw_cycle timestamp on the tile */
862 if (tile)
863 {
864 tile->redraw_cycle = priv->redraw_cycle;
865 pixbuf = g_object_ref (tile->pixbuf);
866 } else
867 pixbuf = NULL;
868
869 return pixbuf;
870 }
871
872 static GdkPixbuf *
873 osm_gps_map_find_bigger_tile (OsmGpsMap *map, int zoom, int x, int y,
874 int *zoom_found)
875 {
876 GdkPixbuf *pixbuf;
877 int next_zoom, next_x, next_y;
878
879 if (zoom == 0) return NULL;
880 next_zoom = zoom - 1;
881 next_x = x / 2;
882 next_y = y / 2;
883 pixbuf = osm_gps_map_load_cached_tile (map, next_zoom, next_x, next_y);
884 if (pixbuf)
885 *zoom_found = next_zoom;
886 else
887 pixbuf = osm_gps_map_find_bigger_tile (map, next_zoom, next_x, next_y,
888 zoom_found);
889 return pixbuf;
890 }
891
892 static GdkPixbuf *
893 osm_gps_map_render_missing_tile_upscaled (OsmGpsMap *map, int zoom,
894 int x, int y)
895 {
896 GdkPixbuf *pixbuf, *big, *area;
897 int zoom_big, zoom_diff, area_size, area_x, area_y;
898 int modulo;
899
900 big = osm_gps_map_find_bigger_tile (map, zoom, x, y, &zoom_big);
901 if (!big) return NULL;
902
903 g_debug ("Found bigger tile (zoom = %d, wanted = %d)", zoom_big, zoom);
904
905 /* get a Pixbuf for the area to magnify */
906 zoom_diff = zoom - zoom_big;
907 area_size = TILESIZE >> zoom_diff;
908 modulo = 1 << zoom_diff;
909 area_x = (x % modulo) * area_size;
910 area_y = (y % modulo) * area_size;
911 area = gdk_pixbuf_new_subpixbuf (big, area_x, area_y,
912 area_size, area_size);
913 g_object_unref (big);
914 pixbuf = gdk_pixbuf_scale_simple (area, TILESIZE, TILESIZE,
915 GDK_INTERP_NEAREST);
916 g_object_unref (area);
917 return pixbuf;
918 }
919
920 static GdkPixbuf *
921 osm_gps_map_render_missing_tile (OsmGpsMap *map, int zoom, int x, int y)
922 {
923 /* maybe TODO: render from downscaled tiles, if the following fails */
924 return osm_gps_map_render_missing_tile_upscaled (map, zoom, x, y);
925 }
926
927 static void
928 osm_gps_map_load_tile (OsmGpsMap *map, int zoom, int x, int y, int offset_x, int offset_y)
929 {
930 OsmGpsMapPrivate *priv = map->priv;
931 GdkPixbuf *pixbuf;
932
933 g_debug("Load tile %d,%d (%d,%d) z:%d", x, y, offset_x, offset_y, zoom);
934
935 /* try to get file from internal cache */
936 pixbuf = osm_gps_map_load_cached_tile(map, zoom, x, y);
937
938 /* if a disk cache is being used, try to read the file from there */
939 if (priv->cache_dir && !pixbuf)
940 {
941 gchar *filename;
942 filename = g_strdup_printf("%s/%u/%u/%u.png",
943 priv->cache_dir,
944 zoom, x, y);
945
946 pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
947 if (pixbuf)
948 {
949 g_debug("Found tile %s", filename);
950 }
951
952 g_free(filename);
953 }
954
955 if (G_LIKELY(pixbuf))
956 {
957 osm_gps_map_blit_tile(map, pixbuf, offset_x,offset_y);
958 g_object_unref (pixbuf);
959 }
960 else
961 {
962 if (priv->map_auto_download)
963 osm_gps_map_download_tile(map, zoom, x, y, TRUE);
964
965 /* try to render the tile by scaling cached tiles from other zoom
966 * levels */
967 pixbuf = osm_gps_map_render_missing_tile (map, zoom, x, y);
968 if (pixbuf)
969 {
970 gdk_draw_pixbuf (priv->pixmap,
971 priv->gc_map,
972 pixbuf,
973 0,0,
974 offset_x,offset_y,
975 TILESIZE,TILESIZE,
976 GDK_RGB_DITHER_NONE, 0, 0);
977 g_object_unref (pixbuf);
978 }
979 else
980 {
981 //prevent some artifacts when drawing not yet loaded areas.
982 gdk_draw_rectangle (priv->pixmap,
983 GTK_WIDGET(map)->style->white_gc,
984 TRUE, offset_x, offset_y, TILESIZE, TILESIZE);
985 }
986 }
987 }
988
989 static void
990 osm_gps_map_fill_tiles_pixel (OsmGpsMap *map)
991 {
992 OsmGpsMapPrivate *priv = map->priv;
993 int i,j, width, height, tile_x0, tile_y0, tiles_nx, tiles_ny;
994 int offset_xn = 0;
995 int offset_yn = 0;
996 int offset_x;
997 int offset_y;
998
999 g_debug("Fill tiles: %d,%d z:%d", priv->map_x, priv->map_y, priv->map_zoom);
1000
1001 offset_x = - priv->map_x % TILESIZE;
1002 offset_y = - priv->map_y % TILESIZE;
1003 if (offset_x > 0) offset_x -= TILESIZE;
1004 if (offset_y > 0) offset_y -= TILESIZE;
1005
1006 offset_xn = offset_x + EXTRA_BORDER;
1007 offset_yn = offset_y + EXTRA_BORDER;
1008
1009 width = GTK_WIDGET(map)->allocation.width;
1010 height = GTK_WIDGET(map)->allocation.height;
1011
1012 tiles_nx = (width - offset_x) / TILESIZE + 1;
1013 tiles_ny = (height - offset_y) / TILESIZE + 1;
1014
1015 tile_x0 = floor((float)priv->map_x / (float)TILESIZE);
1016 tile_y0 = floor((float)priv->map_y / (float)TILESIZE);
1017
1018 //TODO: implement wrap around
1019 for (i=tile_x0; i<(tile_x0+tiles_nx);i++)
1020 {
1021 for (j=tile_y0; j<(tile_y0+tiles_ny); j++)
1022 {
1023 if( j<0 || i<0 || i>=exp(priv->map_zoom * M_LN2) || j>=exp(priv->map_zoom * M_LN2))
1024 {
1025 gdk_draw_rectangle (priv->pixmap,
1026 GTK_WIDGET(map)->style->white_gc,
1027 TRUE,
1028 offset_xn, offset_yn,
1029 TILESIZE,TILESIZE);
1030 }
1031 else
1032 {
1033 osm_gps_map_load_tile(map,
1034 priv->map_zoom,
1035 i,j,
1036 offset_xn,offset_yn);
1037 }
1038 offset_yn += TILESIZE;
1039 }
1040 offset_xn += TILESIZE;
1041 offset_yn = offset_y + EXTRA_BORDER;
1042 }
1043 }
1044
1045 static void
1046 osm_gps_map_print_track (OsmGpsMap *map, GSList *trackpoint_list)
1047 {
1048 OsmGpsMapPrivate *priv = map->priv;
1049
1050 GSList *list;
1051 int x,y;
1052 int min_x = 0,min_y = 0,max_x = 0,max_y = 0;
1053 int lw = priv->ui_gps_track_width;
1054 int map_x0, map_y0;
1055 #ifdef USE_CAIRO
1056 cairo_t *cr;
1057 #else
1058 int last_x = 0, last_y = 0;
1059 GdkColor color;
1060 GdkGC *gc;
1061 #endif
1062
1063 #ifdef USE_CAIRO
1064 cr = gdk_cairo_create(priv->pixmap);
1065 cairo_set_line_width (cr, lw);
1066 cairo_set_source_rgba (cr, 60000.0/65535.0, 0.0, 0.0, 0.6);
1067 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1068 cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
1069 #else
1070 gc = gdk_gc_new(priv->pixmap);
1071 color.green = 0;
1072 color.blue = 0;
1073 color.red = 60000;
1074 gdk_gc_set_rgb_fg_color(gc, &color);
1075 gdk_gc_set_line_attributes(gc, lw, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
1076 #endif
1077
1078 map_x0 = priv->map_x - EXTRA_BORDER;
1079 map_y0 = priv->map_y - EXTRA_BORDER;
1080
1081 for(list = trackpoint_list; list != NULL; list = list->next)
1082 {
1083 coord_t *tp = list->data;
1084
1085 x = lon2pixel(priv->map_zoom, tp->rlon) - map_x0;
1086 y = lat2pixel(priv->map_zoom, tp->rlat) - map_y0;
1087
1088 // first time through loop
1089 if (list == trackpoint_list) {
1090 #ifdef USE_CAIRO
1091 cairo_move_to(cr, x, y);
1092 #else
1093 last_x = x;
1094 last_y = y;
1095 #endif
1096 }
1097
1098 #ifdef USE_CAIRO
1099 cairo_line_to(cr, x, y);
1100 #else
1101 gdk_draw_line (priv->pixmap, gc, x, y, last_x, last_y);
1102 last_x = x;
1103 last_y = y;
1104 #endif
1105
1106 max_x = MAX(x,max_x);
1107 min_x = MIN(x,min_x);
1108 max_y = MAX(y,max_y);
1109 min_y = MIN(y,min_y);
1110 }
1111
1112 gtk_widget_queue_draw_area (
1113 GTK_WIDGET(map),
1114 min_x - lw,
1115 min_y - lw,
1116 max_x + (lw * 2),
1117 max_y + (lw * 2));
1118
1119 #ifdef USE_CAIRO
1120 cairo_stroke(cr);
1121 cairo_destroy(cr);
1122 #else
1123 g_object_unref(gc);
1124 #endif
1125 }
1126
1127 /* Prints the gps trip history, and any other tracks */
1128 static void
1129 osm_gps_map_print_tracks (OsmGpsMap *map)
1130 {
1131 OsmGpsMapPrivate *priv = map->priv;
1132
1133 if (priv->show_trip_history)
1134 osm_gps_map_print_track (map, priv->trip_history);
1135 if (priv->tracks)
1136 {
1137 g_debug("TRACK");
1138 GSList* tmp = priv->tracks;
1139 while (tmp != NULL)
1140 {
1141 osm_gps_map_print_track (map, tmp->data);
1142 tmp = g_slist_next(tmp);
1143 }
1144 }
1145 }
1146
1147 static gboolean
1148 osm_gps_map_purge_cache_check(gpointer key, gpointer value, gpointer user)
1149 {
1150 return(((OsmCachedTile*)value)->redraw_cycle != ((OsmGpsMapPrivate*)user)->redraw_cycle);
1151 }
1152
1153 static void
1154 osm_gps_map_purge_cache (OsmGpsMap *map)
1155 {
1156 OsmGpsMapPrivate *priv = map->priv;
1157
1158 if (g_hash_table_size (priv->tile_cache) < priv->max_tile_cache_size)
1159 return;
1160
1161 /* run through the cache, and remove the tiles which have not been used
1162 * during the last redraw operation */
1163 g_hash_table_foreach_remove(priv->tile_cache, osm_gps_map_purge_cache_check, priv);
1164 }
1165
1166 static gboolean
1167 osm_gps_map_map_redraw (OsmGpsMap *map)
1168 {
1169 OsmGpsMapPrivate *priv = map->priv;
1170
1171 priv->idle_map_redraw = 0;
1172
1173 /* the motion_notify handler uses priv->pixmap to redraw the area; if we
1174 * change it while we are dragging, we will end up showing it in the wrong
1175 * place. This could be fixed by carefully recompute the coordinates, but
1176 * for now it's easier just to disable redrawing the map while dragging */
1177 if (priv->dragging)
1178 return FALSE;
1179
1180 priv->redraw_cycle++;
1181
1182 /* draw white background to initialise pixmap */
1183 gdk_draw_rectangle (
1184 priv->pixmap,
1185 GTK_WIDGET(map)->style->white_gc,
1186 TRUE,
1187 0, 0,
1188 GTK_WIDGET(map)->allocation.width + EXTRA_BORDER * 2,
1189 GTK_WIDGET(map)->allocation.height + EXTRA_BORDER * 2);
1190
1191 osm_gps_map_fill_tiles_pixel(map);
1192
1193 osm_gps_map_print_tracks(map);
1194 osm_gps_map_draw_gps_point(map);
1195 osm_gps_map_print_images(map);
1196
1197 //osm_gps_map_osd_speed(map, 1.5);
1198 osm_gps_map_purge_cache(map);
1199 gtk_widget_queue_draw (GTK_WIDGET (map));
1200
1201 return FALSE;
1202 }
1203
1204 static void
1205 osm_gps_map_map_redraw_idle (OsmGpsMap *map)
1206 {
1207 OsmGpsMapPrivate *priv = map->priv;
1208
1209 if (priv->idle_map_redraw == 0)
1210 priv->idle_map_redraw = g_idle_add ((GSourceFunc)osm_gps_map_map_redraw, map);
1211 }
1212
1213 static void
1214 osm_gps_map_init (OsmGpsMap *object)
1215 {
1216 OsmGpsMapPrivate *priv;
1217
1218 priv = G_TYPE_INSTANCE_GET_PRIVATE (object, OSM_TYPE_GPS_MAP, OsmGpsMapPrivate);
1219 object->priv = priv;
1220
1221 priv->pixmap = NULL;
1222
1223 priv->trip_history = NULL;
1224 priv->gps = g_new0(coord_t, 1);
1225 priv->gps_valid = FALSE;
1226
1227 priv->tracks = NULL;
1228 priv->images = NULL;
1229
1230 priv->drag_counter = 0;
1231 priv->drag_mouse_dx = 0;
1232 priv->drag_mouse_dy = 0;
1233 priv->drag_start_mouse_x = 0;
1234 priv->drag_start_mouse_y = 0;
1235
1236 priv->uri_format = 0;
1237 priv->the_google = FALSE;
1238
1239 #ifndef LIBSOUP22
1240 //Change naumber of concurrent connections option?
1241 priv->soup_session = soup_session_async_new_with_options(
1242 SOUP_SESSION_USER_AGENT,
1243 "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11",
1244 NULL);
1245 #else
1246 /* libsoup-2.2 seems not to be able to set the user agent */
1247 priv->soup_session = soup_session_async_new();
1248 #endif
1249
1250 //Hash table which maps tile d/l URIs to SoupMessage requests
1251 priv->tile_queue = g_hash_table_new (g_str_hash, g_str_equal);
1252
1253 //Some mapping providers (Google) have varying degrees of tiles at multiple
1254 //zoom levels
1255 priv->missing_tiles = g_hash_table_new (g_str_hash, g_str_equal);
1256
1257 /* memory cache for most recently used tiles */
1258 priv->tile_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
1259 g_free, (GDestroyNotify)cached_tile_free);
1260 priv->max_tile_cache_size = 20;
1261
1262 gtk_widget_add_events (GTK_WIDGET (object),
1263 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1264 GDK_POINTER_MOTION_MASK |
1265 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
1266 GTK_WIDGET_SET_FLAGS (object, GTK_CAN_FOCUS);
1267
1268 g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, my_log_handler, NULL);
1269 }
1270
1271 #ifndef G_CHECKSUM_MD5
1272 /* simple hash algorithm hack if md5 is not present */
1273 static char *simple_hash(char *str) {
1274 union {
1275 char str[4];
1276 gulong val;
1277 } hash = { .val = 0x55555555 };
1278
1279 while(*str) {
1280 hash.str[(int)str & 3] ^= *str;
1281 str++;
1282 }
1283 return g_strdup_printf("%08lX", hash.val);
1284 }
1285 #endif
1286
1287 static GObject *
1288 osm_gps_map_constructor (GType gtype, guint n_properties, GObjectConstructParam *properties)
1289 {
1290 GObject *object;
1291 OsmGpsMapPrivate *priv;
1292
1293 //Always chain up to the parent constructor
1294 object = G_OBJECT_CLASS(osm_gps_map_parent_class)->constructor(gtype, n_properties, properties);
1295 priv = OSM_GPS_MAP_PRIVATE(object);
1296
1297 if (!priv->cache_dir_is_full_path) {
1298 #ifdef G_CHECKSUM_MD5
1299 char *md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, priv->repo_uri, -1);
1300 #else
1301 char *md5 = simple_hash(priv->repo_uri);
1302 #endif
1303
1304 if (priv->cache_dir) {
1305 char *old = priv->cache_dir;
1306 //the new cachedir is the given cache dir + the md5 of the repo_uri
1307 priv->cache_dir = g_strdup_printf("%s/%s", old, md5);
1308 g_debug("Adjusting cache dir %s -> %s", old, priv->cache_dir);
1309 g_free(old);
1310 }
1311
1312 g_free(md5);
1313 }
1314
1315 return object;
1316 }
1317
1318 static void
1319 osm_gps_map_dispose (GObject *object)
1320 {
1321 OsmGpsMap *map = OSM_GPS_MAP(object);
1322 OsmGpsMapPrivate *priv = map->priv;
1323
1324 if (priv->is_disposed)
1325 return;
1326
1327 priv->is_disposed = TRUE;
1328
1329 soup_session_abort(priv->soup_session);
1330 g_object_unref(priv->soup_session);
1331
1332 g_hash_table_destroy(priv->tile_queue);
1333 g_hash_table_destroy(priv->missing_tiles);
1334 g_hash_table_destroy(priv->tile_cache);
1335
1336 osm_gps_map_free_images(map);
1337
1338 if(priv->pixmap)
1339 g_object_unref (priv->pixmap);
1340
1341 if(priv->gc_map)
1342 g_object_unref(priv->gc_map);
1343
1344 if (priv->idle_map_redraw != 0)
1345 g_source_remove (priv->idle_map_redraw);
1346
1347 G_OBJECT_CLASS (osm_gps_map_parent_class)->dispose (object);
1348 }
1349
1350 static void
1351 osm_gps_map_finalize (GObject *object)
1352 {
1353 OsmGpsMap *map = OSM_GPS_MAP(object);
1354 OsmGpsMapPrivate *priv = map->priv;
1355
1356 g_free(priv->cache_dir);
1357 g_free(priv->repo_uri);
1358
1359 osm_gps_map_free_trip(map);
1360 osm_gps_map_free_tracks(map);
1361
1362 G_OBJECT_CLASS (osm_gps_map_parent_class)->finalize (object);
1363 }
1364
1365 static void
1366 osm_gps_map_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1367 {
1368 g_return_if_fail (OSM_IS_GPS_MAP (object));
1369 OsmGpsMap *map = OSM_GPS_MAP(object);
1370 OsmGpsMapPrivate *priv = map->priv;
1371
1372 switch (prop_id)
1373 {
1374 case PROP_AUTO_CENTER:
1375 priv->map_auto_center = g_value_get_boolean (value);
1376 break;
1377 case PROP_RECORD_TRIP_HISTORY:
1378 priv->record_trip_history = g_value_get_boolean (value);
1379 break;
1380 case PROP_SHOW_TRIP_HISTORY:
1381 priv->show_trip_history = g_value_get_boolean (value);
1382 break;
1383 case PROP_AUTO_DOWNLOAD:
1384 priv->map_auto_download = g_value_get_boolean (value);
1385 break;
1386 case PROP_REPO_URI:
1387 priv->repo_uri = g_value_dup_string (value);
1388 inspect_map_uri(map);
1389 break;
1390 case PROP_PROXY_URI:
1391 if ( g_value_get_string(value) ) {
1392
1393 priv->proxy_uri = g_value_dup_string (value);
1394 g_debug("Setting proxy server: %s", priv->proxy_uri);
1395 #ifndef LIBSOUP22
1396 GValue val = {0};
1397 SoupURI* uri = soup_uri_new(priv->proxy_uri);
1398 g_value_init(&val, SOUP_TYPE_URI);
1399 g_value_take_boxed(&val, uri);
1400
1401 g_object_set_property(G_OBJECT(priv->soup_session),SOUP_SESSION_PROXY_URI,&val);
1402 #else
1403 SoupUri* uri = soup_uri_new(priv->proxy_uri);
1404 g_object_set(G_OBJECT(priv->soup_session), SOUP_SESSION_PROXY_URI, uri, NULL);
1405 #endif
1406 soup_uri_free(uri);
1407 } else
1408 priv->proxy_uri = NULL;
1409
1410 break;
1411 case PROP_TILE_CACHE_DIR:
1412 priv->cache_dir = g_value_dup_string (value);
1413 break;
1414 case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
1415 priv->cache_dir_is_full_path = g_value_get_boolean (value);
1416 break;
1417 case PROP_ZOOM:
1418 priv->map_zoom = g_value_get_int (value);
1419 break;
1420 case PROP_MAX_ZOOM:
1421 priv->max_zoom = g_value_get_int (value);
1422 break;
1423 case PROP_MIN_ZOOM:
1424 priv->min_zoom = g_value_get_int (value);
1425 break;
1426 case PROP_MAP_X:
1427 priv->map_x = g_value_get_int (value);
1428 priv->center_coord_set = FALSE;
1429 break;
1430 case PROP_MAP_Y:
1431 priv->map_y = g_value_get_int (value);
1432 priv->center_coord_set = FALSE;
1433 break;
1434 case PROP_GPS_TRACK_WIDTH:
1435 priv->ui_gps_track_width = g_value_get_int (value);
1436 break;
1437 case PROP_GPS_POINT_R1:
1438 priv->ui_gps_point_inner_radius = g_value_get_int (value);
1439 break;
1440 case PROP_GPS_POINT_R2:
1441 priv->ui_gps_point_outer_radius = g_value_get_int (value);
1442 break;
1443 default:
1444 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1445 break;
1446 }
1447 }
1448
1449 static void
1450 osm_gps_map_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
1451 {
1452 g_return_if_fail (OSM_IS_GPS_MAP (object));
1453 float lat,lon;
1454 OsmGpsMap *map = OSM_GPS_MAP(object);
1455 OsmGpsMapPrivate *priv = map->priv;
1456
1457 switch (prop_id)
1458 {
1459 case PROP_AUTO_CENTER:
1460 g_value_set_boolean(value, priv->map_auto_center);
1461 break;
1462 case PROP_RECORD_TRIP_HISTORY:
1463 g_value_set_boolean(value, priv->record_trip_history);
1464 break;
1465 case PROP_SHOW_TRIP_HISTORY:
1466 g_value_set_boolean(value, priv->show_trip_history);
1467 break;
1468 case PROP_AUTO_DOWNLOAD:
1469 g_value_set_boolean(value, priv->map_auto_download);
1470 break;
1471 case PROP_REPO_URI:
1472 g_value_set_string(value, priv->repo_uri);
1473 break;
1474 case PROP_PROXY_URI:
1475 g_value_set_string(value, priv->proxy_uri);
1476 break;
1477 case PROP_TILE_CACHE_DIR:
1478 g_value_set_string(value, priv->cache_dir);
1479 break;
1480 case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
1481 g_value_set_boolean(value, priv->cache_dir_is_full_path);
1482 break;
1483 case PROP_ZOOM:
1484 g_value_set_int(value, priv->map_zoom);
1485 break;
1486 case PROP_MAX_ZOOM:
1487 g_value_set_int(value, priv->max_zoom);
1488 break;
1489 case PROP_MIN_ZOOM:
1490 g_value_set_int(value, priv->min_zoom);
1491 break;
1492 case PROP_LATITUDE:
1493 lat = pixel2lat(priv->map_zoom,
1494 priv->map_y + (GTK_WIDGET(map)->allocation.height / 2));
1495 g_value_set_float(value, rad2deg(lat));
1496 break;
1497 case PROP_LONGITUDE:
1498 lon = pixel2lon(priv->map_zoom,
1499 priv->map_x + (GTK_WIDGET(map)->allocation.width / 2));
1500 g_value_set_float(value, rad2deg(lon));
1501 break;
1502 case PROP_MAP_X:
1503 g_value_set_int(value, priv->map_x);
1504 break;
1505 case PROP_MAP_Y:
1506 g_value_set_int(value, priv->map_y);
1507 break;
1508 case PROP_TILES_QUEUED:
1509 g_value_set_int(value, g_hash_table_size(priv->tile_queue));
1510 break;
1511 case PROP_GPS_TRACK_WIDTH:
1512 g_value_set_int(value, priv->ui_gps_track_width);
1513 break;
1514 case PROP_GPS_POINT_R1:
1515 g_value_set_int(value, priv->ui_gps_point_inner_radius);
1516 break;
1517 case PROP_GPS_POINT_R2:
1518 g_value_set_int(value, priv->ui_gps_point_outer_radius);
1519 break;
1520 default:
1521 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1522 break;
1523 }
1524 }
1525
1526 static gboolean
1527 _osm_gps_map_scroll (GtkWidget *widget, GdkEventScroll *event)
1528 {
1529 OsmGpsMap *map = OSM_GPS_MAP(widget);
1530 OsmGpsMapPrivate *priv = map->priv;
1531
1532 if (event->direction == GDK_SCROLL_UP)
1533 {
1534 osm_gps_map_set_zoom(map, priv->map_zoom+1);
1535 }
1536 else
1537 {
1538 osm_gps_map_set_zoom(map, priv->map_zoom-1);
1539 }
1540
1541 return FALSE;
1542 }
1543
1544 static gboolean
1545 osm_gps_map_button_press (GtkWidget *widget, GdkEventButton *event)
1546 {
1547 OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1548
1549 priv->drag_counter = 0;
1550 priv->drag_start_mouse_x = (int) event->x;
1551 priv->drag_start_mouse_y = (int) event->y;
1552 priv->drag_start_map_x = priv->map_x;
1553 priv->drag_start_map_y = priv->map_y;
1554
1555 return FALSE;
1556 }
1557
1558 static gboolean
1559 osm_gps_map_button_release (GtkWidget *widget, GdkEventButton *event)
1560 {
1561 OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1562
1563 if (priv->dragging)
1564 {
1565 priv->dragging = FALSE;
1566
1567 priv->map_x = priv->drag_start_map_x;
1568 priv->map_y = priv->drag_start_map_y;
1569
1570 priv->map_x += (priv->drag_start_mouse_x - (int) event->x);
1571 priv->map_y += (priv->drag_start_mouse_y - (int) event->y);
1572
1573 priv->center_coord_set = FALSE;
1574
1575 osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
1576 }
1577
1578 priv->drag_mouse_dx = 0;
1579 priv->drag_mouse_dy = 0;
1580 priv->drag_counter = 0;
1581
1582 return FALSE;
1583 }
1584
1585 static gboolean
1586 osm_gps_map_motion_notify (GtkWidget *widget, GdkEventMotion *event)
1587 {
1588 int x, y;
1589 GdkModifierType state;
1590 OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1591
1592 if (event->is_hint)
1593 gdk_window_get_pointer (event->window, &x, &y, &state);
1594 else
1595 {
1596 x = event->x;
1597 y = event->y;
1598 state = event->state;
1599 }
1600
1601 // are we being dragged
1602 if (!(state & GDK_BUTTON1_MASK))
1603 return FALSE;
1604
1605 priv->drag_counter++;
1606
1607 // we havent dragged more than 6 pixels
1608 if (priv->drag_counter < 6)
1609 return FALSE;
1610
1611 priv->dragging = TRUE;
1612
1613 if (priv->map_auto_center)
1614 g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
1615
1616 priv->drag_mouse_dx = x - priv->drag_start_mouse_x;
1617 priv->drag_mouse_dy = y - priv->drag_start_mouse_y;
1618
1619 gdk_draw_drawable (
1620 widget->window,
1621 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1622 priv->pixmap,
1623 0,0,
1624 priv->drag_mouse_dx - EXTRA_BORDER, priv->drag_mouse_dy - EXTRA_BORDER,
1625 -1,-1);
1626
1627 //Paint white outside of the map if dragging. Its less
1628 //ugly than painting the corrupted map
1629 if(priv->drag_mouse_dx>EXTRA_BORDER) {
1630 gdk_draw_rectangle (
1631 widget->window,
1632 widget->style->white_gc,
1633 TRUE,
1634 0, 0,
1635 priv->drag_mouse_dx - EXTRA_BORDER,
1636 widget->allocation.height);
1637 }
1638 else if (-priv->drag_mouse_dx > EXTRA_BORDER)
1639 {
1640 gdk_draw_rectangle (
1641 widget->window,
1642 widget->style->white_gc,
1643 TRUE,
1644 priv->drag_mouse_dx + widget->allocation.width + EXTRA_BORDER, 0,
1645 -priv->drag_mouse_dx - EXTRA_BORDER,
1646 widget->allocation.height);
1647 }
1648
1649 if (priv->drag_mouse_dy>EXTRA_BORDER) {
1650 gdk_draw_rectangle (
1651 widget->window,
1652 widget->style->white_gc,
1653 TRUE,
1654 0, 0,
1655 widget->allocation.width,
1656 priv->drag_mouse_dy - EXTRA_BORDER);
1657 }
1658 else if (-priv->drag_mouse_dy > EXTRA_BORDER)
1659 {
1660 gdk_draw_rectangle (
1661 widget->window,
1662 widget->style->white_gc,
1663 TRUE,
1664 0, priv->drag_mouse_dy + widget->allocation.height + EXTRA_BORDER,
1665 widget->allocation.width,
1666 -priv->drag_mouse_dy - EXTRA_BORDER);
1667 }
1668
1669 return FALSE;
1670 }
1671
1672 static gboolean
1673 osm_gps_map_configure (GtkWidget *widget, GdkEventConfigure *event)
1674 {
1675 OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1676
1677 /* create pixmap */
1678 if (priv->pixmap)
1679 g_object_unref (priv->pixmap);
1680
1681 priv->pixmap = gdk_pixmap_new (
1682 widget->window,
1683 widget->allocation.width + EXTRA_BORDER * 2,
1684 widget->allocation.height + EXTRA_BORDER * 2,
1685 -1);
1686
1687 /* and gc, used for clipping (I think......) */
1688 if(priv->gc_map)
1689 g_object_unref(priv->gc_map);
1690
1691 priv->gc_map = gdk_gc_new(priv->pixmap);
1692
1693 osm_gps_map_map_redraw(OSM_GPS_MAP(widget));
1694
1695 return FALSE;
1696 }
1697
1698 static gboolean
1699 osm_gps_map_expose (GtkWidget *widget, GdkEventExpose *event)
1700 {
1701 OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1702
1703 gdk_draw_drawable (
1704 widget->window,
1705 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1706 priv->pixmap,
1707 event->area.x + EXTRA_BORDER, event->area.y + EXTRA_BORDER,
1708 event->area.x, event->area.y,
1709 event->area.width, event->area.height);
1710
1711 return FALSE;
1712 }
1713
1714 static void
1715 osm_gps_map_class_init (OsmGpsMapClass *klass)
1716 {
1717 GObjectClass* object_class = G_OBJECT_CLASS (klass);
1718 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1719
1720 g_type_class_add_private (klass, sizeof (OsmGpsMapPrivate));
1721
1722 object_class->dispose = osm_gps_map_dispose;
1723 object_class->finalize = osm_gps_map_finalize;
1724 object_class->constructor = osm_gps_map_constructor;
1725 object_class->set_property = osm_gps_map_set_property;
1726 object_class->get_property = osm_gps_map_get_property;
1727
1728 widget_class->expose_event = osm_gps_map_expose;
1729 widget_class->configure_event = osm_gps_map_configure;
1730 widget_class->button_press_event = osm_gps_map_button_press;
1731 widget_class->button_release_event = osm_gps_map_button_release;
1732 widget_class->motion_notify_event = osm_gps_map_motion_notify;
1733 widget_class->scroll_event = _osm_gps_map_scroll;
1734
1735 g_object_class_install_property (object_class,
1736 PROP_AUTO_CENTER,
1737 g_param_spec_boolean ("auto-center",
1738 "auto center",
1739 "map auto center",
1740 TRUE,
1741 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1742
1743 g_object_class_install_property (object_class,
1744 PROP_RECORD_TRIP_HISTORY,
1745 g_param_spec_boolean ("record-trip-history",
1746 "record trip history",
1747 "should all gps points be recorded in a trip history",
1748 TRUE,
1749 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1750
1751 g_object_class_install_property (object_class,
1752 PROP_SHOW_TRIP_HISTORY,
1753 g_param_spec_boolean ("show-trip-history",
1754 "show trip history",
1755 "should the recorded trip history be shown on the map",
1756 TRUE,
1757 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1758
1759 g_object_class_install_property (object_class,
1760 PROP_AUTO_DOWNLOAD,
1761 g_param_spec_boolean ("auto-download",
1762 "auto download",
1763 "map auto download",
1764 TRUE,
1765 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1766
1767 g_object_class_install_property (object_class,
1768 PROP_REPO_URI,
1769 g_param_spec_string ("repo-uri",
1770 "repo uri",
1771 "osm repo uri",
1772 "http://tile.openstreetmap.org/#Z/#X/#Y.png",
1773 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1774
1775 g_object_class_install_property (object_class,
1776 PROP_PROXY_URI,
1777 g_param_spec_string ("proxy-uri",
1778 "proxy uri",
1779 "http proxy uri on NULL",
1780 NULL,
1781 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1782
1783 g_object_class_install_property (object_class,
1784 PROP_TILE_CACHE_DIR,
1785 g_param_spec_string ("tile-cache",
1786 "tile cache",
1787 "osm local tile cache dir",
1788 "/tmp/Maps",
1789 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1790
1791 g_object_class_install_property (object_class,
1792 PROP_TILE_CACHE_DIR_IS_FULL_PATH,
1793 g_param_spec_boolean ("tile-cache-is-full-path",
1794 "tile cache is full path",
1795 "if true, the path passed to tile-cache is interpreted as the full cache path",
1796 FALSE,
1797 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1798
1799 g_object_class_install_property (object_class,
1800 PROP_ZOOM,
1801 g_param_spec_int ("zoom",
1802 "zoom",
1803 "zoom level",
1804 MIN_ZOOM, /* minimum property value */
1805 MAX_ZOOM, /* maximum property value */
1806 3,
1807 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1808
1809 g_object_class_install_property (object_class,
1810 PROP_MAX_ZOOM,
1811 g_param_spec_int ("max-zoom",
1812 "max zoom",
1813 "maximum zoom level",
1814 MIN_ZOOM, /* minimum property value */
1815 MAX_ZOOM, /* maximum property value */
1816 17,
1817 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1818
1819 g_object_class_install_property (object_class,
1820 PROP_MIN_ZOOM,
1821 g_param_spec_int ("min-zoom",
1822 "min zoom",
1823 "minimum zoom level",
1824 MIN_ZOOM, /* minimum property value */
1825 MAX_ZOOM, /* maximum property value */
1826 1,
1827 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1828
1829 g_object_class_install_property (object_class,
1830 PROP_LATITUDE,
1831 g_param_spec_float ("latitude",
1832 "latitude",
1833 "latitude in degrees",
1834 -90.0, /* minimum property value */
1835 90.0, /* maximum property value */
1836 0,
1837 G_PARAM_READABLE));
1838
1839 g_object_class_install_property (object_class,
1840 PROP_LONGITUDE,
1841 g_param_spec_float ("longitude",
1842 "longitude",
1843 "longitude in degrees",
1844 -180.0, /* minimum property value */
1845 180.0, /* maximum property value */
1846 0,
1847 G_PARAM_READABLE));
1848
1849 g_object_class_install_property (object_class,
1850 PROP_MAP_X,
1851 g_param_spec_int ("map-x",
1852 "map-x",
1853 "initial map x location",
1854 G_MININT, /* minimum property value */
1855 G_MAXINT, /* maximum property value */
1856 890,
1857 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1858
1859 g_object_class_install_property (object_class,
1860 PROP_MAP_Y,
1861 g_param_spec_int ("map-y",
1862 "map-y",
1863 "initial map y location",
1864 G_MININT, /* minimum property value */
1865 G_MAXINT, /* maximum property value */
1866 515,
1867 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1868
1869 g_object_class_install_property (object_class,
1870 PROP_TILES_QUEUED,
1871 g_param_spec_int ("tiles-queued",
1872 "tiles-queued",
1873 "number of tiles currently waiting to download",
1874 G_MININT, /* minimum property value */
1875 G_MAXINT, /* maximum property value */
1876 0,
1877 G_PARAM_READABLE));
1878
1879 g_object_class_install_property (object_class,
1880 PROP_GPS_TRACK_WIDTH,
1881 g_param_spec_int ("gps-track-width",
1882 "gps-track-width",
1883 "width of the lines drawn for the gps track",
1884 1, /* minimum property value */
1885 G_MAXINT, /* maximum property value */
1886 4,
1887 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1888
1889 g_object_class_install_property (object_class,
1890 PROP_GPS_POINT_R1,
1891 g_param_spec_int ("gps-track-point-radius",
1892 "gps-track-point-radius",
1893 "radius of the gps point inner circle",
1894 0, /* minimum property value */
1895 G_MAXINT, /* maximum property value */
1896 5,
1897 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1898
1899 g_object_class_install_property (object_class,
1900 PROP_GPS_POINT_R2,
1901 g_param_spec_int ("gps-track-highlight-radius",
1902 "gps-track-highlight-radius",
1903 "radius of the gps point highlight circle",
1904 0, /* minimum property value */
1905 G_MAXINT, /* maximum property value */
1906 20,
1907 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1908
1909 }
1910
1911 void
1912 osm_gps_map_download_maps (OsmGpsMap *map, coord_t *pt1, coord_t *pt2, int zoom_start, int zoom_end)
1913 {
1914 int i,j,zoom,num_tiles;
1915 OsmGpsMapPrivate *priv = map->priv;
1916
1917 if (pt1 && pt2)
1918 {
1919 gchar *filename;
1920 num_tiles = 0;
1921 zoom_end = CLAMP(zoom_end, priv->min_zoom, priv->max_zoom);
1922 g_debug("Download maps: z:%d->%d",zoom_start, zoom_end);
1923
1924 for(zoom=zoom_start; zoom<=zoom_end; zoom++)
1925 {
1926 int x1,y1,x2,y2;
1927
1928 x1 = (int)floor((float)lon2pixel(zoom, pt1->rlon) / (float)TILESIZE);
1929 y1 = (int)floor((float)lat2pixel(zoom, pt1->rlat) / (float)TILESIZE);
1930
1931 x2 = (int)floor((float)lon2pixel(zoom, pt2->rlon) / (float)TILESIZE);
1932 y2 = (int)floor((float)lat2pixel(zoom, pt2->rlat) / (float)TILESIZE);
1933
1934 // loop x1-x2
1935 for(i=x1; i<=x2; i++)
1936 {
1937 // loop y1 - y2
1938 for(j=y1; j<=y2; j++)
1939 {
1940 // x = i, y = j
1941 filename = g_strdup_printf("%s/%u/%u/%u.png", priv->cache_dir, zoom, i, j);
1942 if (!priv->cache_dir || !g_file_test(filename, G_FILE_TEST_EXISTS))
1943 {
1944 osm_gps_map_download_tile(map, zoom, i, j, FALSE);
1945 num_tiles++;
1946 }
1947 g_free(filename);
1948 }
1949 }
1950 g_debug("DL @Z:%d = %d tiles",zoom,num_tiles);
1951 }
1952 }
1953 }
1954
1955 void
1956 osm_gps_map_get_bbox (OsmGpsMap *map, coord_t *pt1, coord_t *pt2)
1957 {
1958 OsmGpsMapPrivate *priv = map->priv;
1959
1960 if (pt1 && pt2) {
1961 pt1->rlat = pixel2lat(priv->map_zoom, priv->map_y);
1962 pt1->rlon = pixel2lon(priv->map_zoom, priv->map_x);
1963 pt2->rlat = pixel2lat(priv->map_zoom, priv->map_y + GTK_WIDGET(map)->allocation.height);
1964 pt2->rlon = pixel2lon(priv->map_zoom, priv->map_x + GTK_WIDGET(map)->allocation.width);
1965
1966 g_debug("BBOX: %f %f %f %f", pt1->rlat, pt1->rlon, pt2->rlat, pt2->rlon);
1967 }
1968 }
1969
1970 void
1971 osm_gps_map_set_mapcenter (OsmGpsMap *map, float latitude, float longitude, int zoom)
1972 {
1973 osm_gps_map_set_center (map, latitude, longitude);
1974 osm_gps_map_set_zoom (map, zoom);
1975 }
1976
1977 void
1978 osm_gps_map_set_center (OsmGpsMap *map, float latitude, float longitude)
1979 {
1980 int pixel_x, pixel_y;
1981 OsmGpsMapPrivate *priv;
1982
1983 g_return_if_fail (OSM_IS_GPS_MAP (map));
1984 priv = map->priv;
1985
1986 priv->center_rlat = deg2rad(latitude);
1987 priv->center_rlon = deg2rad(longitude);
1988 priv->center_coord_set = TRUE;
1989
1990 // pixel_x,y, offsets
1991 pixel_x = lon2pixel(priv->map_zoom, priv->center_rlon);
1992 pixel_y = lat2pixel(priv->map_zoom, priv->center_rlat);
1993
1994 priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
1995 priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
1996
1997 osm_gps_map_map_redraw_idle(map);
1998 }
1999
2000 int
2001 osm_gps_map_set_zoom (OsmGpsMap *map, int zoom)
2002 {
2003 int zoom_old;
2004 double factor = 1.0;
2005 int width_center, height_center;
2006 OsmGpsMapPrivate *priv;
2007
2008 g_return_val_if_fail (OSM_IS_GPS_MAP (map), 0);
2009 priv = map->priv;
2010
2011 if (zoom != priv->map_zoom)
2012 {
2013 width_center = GTK_WIDGET(map)->allocation.width / 2;
2014 height_center = GTK_WIDGET(map)->allocation.height / 2;
2015
2016 zoom_old = priv->map_zoom;
2017 //constrain zoom min_zoom -> max_zoom
2018 priv->map_zoom = CLAMP(zoom, priv->min_zoom, priv->max_zoom);
2019
2020 if (priv->center_coord_set)
2021 {
2022 priv->map_x = lon2pixel(priv->map_zoom, priv->center_rlon) - width_center;
2023 priv->map_y = lat2pixel(priv->map_zoom, priv->center_rlat) - height_center;
2024 }
2025 else
2026 {
2027 factor = exp(priv->map_zoom * M_LN2)/exp(zoom_old * M_LN2);
2028 priv->map_x = ((priv->map_x + width_center) * factor) - width_center;
2029 priv->map_y = ((priv->map_y + height_center) * factor) - height_center;
2030 }
2031
2032 g_debug("Zoom changed from %d to %d factor:%f x:%d",
2033 zoom_old, priv->map_zoom, factor, priv->map_x);
2034
2035 osm_gps_map_map_redraw_idle(map);
2036 }
2037 return priv->map_zoom;
2038 }
2039
2040 void
2041 osm_gps_map_add_track (OsmGpsMap *map, GSList *track)
2042 {
2043 OsmGpsMapPrivate *priv;
2044
2045 g_return_if_fail (OSM_IS_GPS_MAP (map));
2046 priv = map->priv;
2047
2048 if (track) {
2049 priv->tracks = g_slist_append(priv->tracks, track);
2050 osm_gps_map_map_redraw_idle(map);
2051 }
2052 }
2053
2054 void
2055 osm_gps_map_clear_tracks (OsmGpsMap *map)
2056 {
2057 g_return_if_fail (OSM_IS_GPS_MAP (map));
2058
2059 osm_gps_map_free_tracks(map);
2060 osm_gps_map_map_redraw_idle(map);
2061 }
2062
2063 void
2064 osm_gps_map_add_image (OsmGpsMap *map, float latitude, float longitude, GdkPixbuf *image)
2065 {
2066 g_return_if_fail (OSM_IS_GPS_MAP (map));
2067
2068 if (image) {
2069 OsmGpsMapPrivate *priv = map->priv;
2070 image_t *im;
2071
2072 //cache w/h for speed, and add image to list
2073 im = g_new0(image_t,1);
2074 im->w = gdk_pixbuf_get_width(image);
2075 im->h = gdk_pixbuf_get_width(image);
2076 im->pt.rlat = deg2rad(latitude);
2077 im->pt.rlon = deg2rad(longitude);
2078
2079 g_object_ref(image);
2080 im->image = image;
2081
2082 priv->images = g_slist_append(priv->images, im);
2083
2084 osm_gps_map_map_redraw_idle(map);
2085 }
2086 }
2087
2088 void
2089 osm_gps_map_clear_images (OsmGpsMap *map)
2090 {
2091 g_return_if_fail (OSM_IS_GPS_MAP (map));
2092
2093 osm_gps_map_free_images(map);
2094 osm_gps_map_map_redraw_idle(map);
2095 }
2096
2097 void
2098 osm_gps_map_osd_speed (OsmGpsMap *map, float speed)
2099 {
2100 OsmGpsMapPrivate *priv;
2101
2102 PangoContext *context = NULL;
2103 PangoLayout *layout = NULL;
2104 PangoFontDescription *desc = NULL;
2105
2106 GdkColor color;
2107 GdkGC *gc;
2108
2109 gchar *buffer;
2110 //static int x = 10, y = 10;
2111 static int width = 0, height = 0;
2112
2113 g_return_if_fail (OSM_IS_GPS_MAP (map));
2114 priv = map->priv;
2115
2116 buffer = g_strdup_printf("%.0f", speed);
2117
2118 /* pango initialisation */
2119 context = gtk_widget_get_pango_context (GTK_WIDGET(map));
2120 layout = pango_layout_new (context);
2121 desc = pango_font_description_new();
2122
2123 pango_font_description_set_size (desc, 40 * PANGO_SCALE);
2124 pango_layout_set_font_description (layout, desc);
2125 pango_layout_set_text (layout, buffer, strlen(buffer));
2126
2127 gc = gdk_gc_new (GTK_WIDGET(map)->window);
2128
2129 color.red = (0 > 50) ? 0xffff : 0;
2130 color.green = 0;
2131 color.blue = 0;
2132
2133 gdk_gc_set_rgb_fg_color (gc, &color);
2134
2135 /* faster / less flicker alternative:*/
2136 gdk_draw_drawable (
2137 GTK_WIDGET(map)->window,
2138 GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(map)],
2139 priv->pixmap,
2140 0,0,
2141 0,0,
2142 width+10,width+10);
2143
2144 gdk_draw_layout(GTK_WIDGET(map)->window,
2145 gc,
2146 0, 0,
2147 layout);
2148
2149 /* set width and height */
2150 pango_layout_get_pixel_size(layout, &width, &height);
2151
2152 g_free(buffer);
2153 pango_font_description_free (desc);
2154 g_object_unref (layout);
2155 g_object_unref (gc);
2156 }
2157
2158 void
2159 osm_gps_map_draw_gps (OsmGpsMap *map, float latitude, float longitude, float heading)
2160 {
2161 int pixel_x, pixel_y;
2162 OsmGpsMapPrivate *priv;
2163
2164 g_return_if_fail (OSM_IS_GPS_MAP (map));
2165 priv = map->priv;
2166
2167 priv->gps->rlat = deg2rad(latitude);
2168 priv->gps->rlon = deg2rad(longitude);
2169 priv->gps_valid = TRUE;
2170
2171 // pixel_x,y, offsets
2172 pixel_x = lon2pixel(priv->map_zoom, priv->gps->rlon);
2173 pixel_y = lat2pixel(priv->map_zoom, priv->gps->rlat);
2174
2175 //If trip marker add to list of gps points.
2176 if (priv->record_trip_history) {
2177 coord_t *tp = g_new0(coord_t,1);
2178 tp->rlat = priv->gps->rlat;
2179 tp->rlon = priv->gps->rlon;
2180 priv->trip_history = g_slist_append(priv->trip_history, tp);
2181 }
2182
2183 // dont draw anything if we are dragging
2184 if (priv->dragging) {
2185 g_debug("Dragging");
2186 return;
2187 }
2188
2189 //Automatically center the map if the track approaches the edge
2190 if(priv->map_auto_center) {
2191 int x = pixel_x - priv->map_x;
2192 int y = pixel_y - priv->map_y;
2193 int width = GTK_WIDGET(map)->allocation.width;
2194 int height = GTK_WIDGET(map)->allocation.height;
2195 if( x < (width/2 - width/8) || x > (width/2 + width/8) ||
2196 y < (height/2 - height/8) || y > (height/2 + height/8)) {
2197
2198 priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
2199 priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
2200 priv->center_coord_set = FALSE;
2201 }
2202 }
2203
2204 // this redraws the map (including the gps track, and adjusts the
2205 // map center if it was changed
2206 osm_gps_map_map_redraw_idle(map);
2207 }
2208
2209 void
2210 osm_gps_map_clear_gps (OsmGpsMap *map)
2211 {
2212 osm_gps_map_free_trip(map);
2213 osm_gps_map_map_redraw_idle(map);
2214 }
2215
2216 coord_t
2217 osm_gps_map_get_co_ordinates (OsmGpsMap *map, int pixel_x, int pixel_y)
2218 {
2219 coord_t coord;
2220 OsmGpsMapPrivate *priv = map->priv;
2221
2222 coord.rlat = pixel2lat(priv->map_zoom, priv->map_y + pixel_y);
2223 coord.rlon = pixel2lon(priv->map_zoom, priv->map_x + pixel_x);
2224 return coord;
2225 }
2226
2227 GtkWidget *
2228 osm_gps_map_new (void)
2229 {
2230 return g_object_new (OSM_TYPE_GPS_MAP, NULL);
2231 }
2232
2233 void
2234 osm_gps_map_screen_to_geographic (OsmGpsMap *map, gint pixel_x, gint pixel_y,
2235 gfloat *latitude, gfloat *longitude)
2236 {
2237 OsmGpsMapPrivate *priv;
2238
2239 g_return_if_fail (OSM_IS_GPS_MAP (map));
2240 priv = map->priv;
2241
2242 if (latitude)
2243 *latitude = rad2deg(pixel2lat(priv->map_zoom, priv->map_y + pixel_y));
2244 if (longitude)
2245 *longitude = rad2deg(pixel2lon(priv->map_zoom, priv->map_x + pixel_x));
2246 }
2247
2248 void
2249 osm_gps_map_geographic_to_screen (OsmGpsMap *map,
2250 gfloat latitude, gfloat longitude,
2251 gint *pixel_x, gint *pixel_y)
2252 {
2253 OsmGpsMapPrivate *priv;
2254
2255 g_return_if_fail (OSM_IS_GPS_MAP (map));
2256 priv = map->priv;
2257
2258 if (pixel_x)
2259 *pixel_x = lon2pixel(priv->map_zoom, deg2rad(longitude)) - priv->map_x;
2260 if (pixel_y)
2261 *pixel_y = lat2pixel(priv->map_zoom, deg2rad(latitude)) - priv->map_y;
2262 }
2263
2264 void
2265 osm_gps_map_scroll (OsmGpsMap *map, gint dx, gint dy)
2266 {
2267 OsmGpsMapPrivate *priv;
2268
2269 g_return_if_fail (OSM_IS_GPS_MAP (map));
2270 priv = map->priv;
2271
2272 priv->center_coord_set = FALSE;
2273 priv->map_x += dx;
2274 priv->map_y += dy;
2275
2276 osm_gps_map_map_redraw_idle (map);
2277 }
2278