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

Parent Directory Parent Directory | Revision Log Revision Log


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