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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 56 - (show annotations)
Fri Aug 14 12:19:45 2009 UTC (14 years, 9 months ago) by harbaum
File MIME type: text/plain
File size: 88592 byte(s)
Map balloon started
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 /* http://cairographics.org/samples/ */
637 static void
638 osm_gps_map_draw_balloon (OsmGpsMap *map)
639 {
640 OsmGpsMapPrivate *priv = map->priv;
641
642 /* xyz */
643 #define X 100
644 #define Y 100
645 #define RADIUS 20
646 #define WIDTH 150
647 #define HEIGHT 75
648 #define TRANSPARENCY 0.7
649 #define PIN_HEIGHT 20
650 #define PIN_FOOT_WIDTH 20
651 #define PIN_X (X + EXTRA_BORDER)
652 #define PIN_X0 (X + EXTRA_BORDER + RADIUS + PIN_FOOT_WIDTH)
653 #define PIN_X1 (X + EXTRA_BORDER + RADIUS)
654 #define PIN_Y (Y + EXTRA_BORDER + HEIGHT + PIN_HEIGHT)
655
656 #ifdef USE_CAIRO
657 cairo_t *cr = gdk_cairo_create(priv->pixmap);
658
659 int x0 = X + EXTRA_BORDER, y0 = Y + EXTRA_BORDER;
660 int x1 = x0 + WIDTH, y1 = y0 + HEIGHT;
661
662 cairo_move_to (cr, x0, y0 + RADIUS);
663 cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + RADIUS, y0);
664 cairo_line_to (cr, x1 - RADIUS, y0);
665 cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + RADIUS);
666 cairo_line_to (cr, x1 , y1 - RADIUS);
667 cairo_curve_to (cr, x1, y1, x1, y1, x1 - RADIUS, y1);
668
669 /* insert pin */
670 cairo_line_to (cr, PIN_X0, y1);
671 cairo_line_to (cr, PIN_X, PIN_Y);
672 cairo_line_to (cr, PIN_X1, y1);
673
674
675 cairo_line_to (cr, x0 + RADIUS, y1);
676 cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - RADIUS);
677
678 cairo_close_path (cr);
679 cairo_set_source_rgba (cr, 1, 1, 1, TRANSPARENCY);
680 cairo_fill_preserve (cr);
681 cairo_set_source_rgba (cr, 0, 0, 0, TRANSPARENCY);
682 cairo_set_line_width (cr, 1);
683 cairo_stroke (cr);
684
685 gtk_widget_queue_draw_area (GTK_WIDGET(map),
686 x0,
687 y0,
688 WIDTH,
689 HEIGHT + PIN_HEIGHT);
690 #endif
691 }
692
693 static void
694 osm_gps_map_blit_tile(OsmGpsMap *map, GdkPixbuf *pixbuf, int offset_x, int offset_y)
695 {
696 OsmGpsMapPrivate *priv = map->priv;
697
698 g_debug("Queing redraw @ %d,%d (w:%d h:%d)", offset_x,offset_y, TILESIZE,TILESIZE);
699
700 /* draw pixbuf onto pixmap */
701 gdk_draw_pixbuf (priv->pixmap,
702 priv->gc_map,
703 pixbuf,
704 0,0,
705 offset_x,offset_y,
706 TILESIZE,TILESIZE,
707 GDK_RGB_DITHER_NONE, 0, 0);
708 }
709
710 /* libsoup-2.2 and libsoup-2.4 use different ways to store the body data */
711 #ifdef LIBSOUP22
712 #define soup_message_headers_append(a,b,c) soup_message_add_header(a,b,c)
713 #define MSG_RESPONSE_BODY(a) ((a)->response.body)
714 #define MSG_RESPONSE_LEN(a) ((a)->response.length)
715 #define MSG_RESPONSE_LEN_FORMAT "%u"
716 #else
717 #define MSG_RESPONSE_BODY(a) ((a)->response_body->data)
718 #define MSG_RESPONSE_LEN(a) ((a)->response_body->length)
719 #define MSG_RESPONSE_LEN_FORMAT "%lld"
720 #endif
721
722 #ifdef LIBSOUP22
723 static void
724 osm_gps_map_tile_download_complete (SoupMessage *msg, gpointer user_data)
725 #else
726 static void
727 osm_gps_map_tile_download_complete (SoupSession *session, SoupMessage *msg, gpointer user_data)
728 #endif
729 {
730 FILE *file;
731 tile_download_t *dl = (tile_download_t *)user_data;
732 OsmGpsMap *map = OSM_GPS_MAP(dl->map);
733 OsmGpsMapPrivate *priv = map->priv;
734 gboolean file_saved = FALSE;
735
736 if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
737 {
738 /* save tile into cachedir if one has been specified */
739 if (priv->cache_dir)
740 {
741 if (g_mkdir_with_parents(dl->folder,0700) == 0)
742 {
743 file = g_fopen(dl->filename, "wb");
744 if (file != NULL)
745 {
746 fwrite (MSG_RESPONSE_BODY(msg), 1, MSG_RESPONSE_LEN(msg), file);
747 file_saved = TRUE;
748 g_debug("Wrote "MSG_RESPONSE_LEN_FORMAT" bytes to %s", MSG_RESPONSE_LEN(msg), dl->filename);
749 fclose (file);
750
751 }
752 }
753 else
754 {
755 g_warning("Error creating tile download directory: %s", dl->folder);
756 }
757 }
758
759 if (dl->redraw)
760 {
761 GdkPixbuf *pixbuf = NULL;
762
763 /* if the file was actually stored on disk, we can simply */
764 /* load and decode it from that file */
765 if (priv->cache_dir)
766 {
767 if (file_saved)
768 {
769 pixbuf = gdk_pixbuf_new_from_file (dl->filename, NULL);
770 }
771 }
772 else
773 {
774 GdkPixbufLoader *loader;
775 char *extension = strrchr (dl->filename, '.');
776
777 /* parse file directly from memory */
778 if (extension)
779 {
780 loader = gdk_pixbuf_loader_new_with_type (extension+1, NULL);
781 if (!gdk_pixbuf_loader_write (loader, (unsigned char*)MSG_RESPONSE_BODY(msg), MSG_RESPONSE_LEN(msg), NULL))
782 {
783 g_warning("Error: Decoding of image failed");
784 }
785 gdk_pixbuf_loader_close(loader, NULL);
786
787 pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
788
789 /* give up loader but keep the pixbuf */
790 g_object_ref(pixbuf);
791 g_object_unref(loader);
792 }
793 else
794 {
795 g_warning("Error: Unable to determine image file format");
796 }
797 }
798
799 /* Store the tile into the cache */
800 if (G_LIKELY (pixbuf))
801 {
802 OsmCachedTile *tile = g_slice_new (OsmCachedTile);
803 tile->pixbuf = pixbuf;
804 tile->redraw_cycle = priv->redraw_cycle;
805 /* if the tile is already in the cache (it could be one
806 * rendered from another zoom level), it will be
807 * overwritten */
808 g_hash_table_insert (priv->tile_cache, dl->filename, tile);
809 /* NULL-ify dl->filename so that it won't be freed, as
810 * we are using it as a key in the hash table */
811 dl->filename = NULL;
812 }
813 osm_gps_map_map_redraw_idle (map);
814 }
815 g_hash_table_remove(priv->tile_queue, dl->uri);
816
817 g_free(dl->uri);
818 g_free(dl->folder);
819 g_free(dl->filename);
820 g_free(dl);
821 }
822 else
823 {
824 g_warning("Error downloading tile: %d - %s", msg->status_code, msg->reason_phrase);
825 if (msg->status_code == SOUP_STATUS_NOT_FOUND)
826 {
827 g_hash_table_insert(priv->missing_tiles, dl->uri, NULL);
828 g_hash_table_remove(priv->tile_queue, dl->uri);
829 }
830 else if (msg->status_code == SOUP_STATUS_CANCELLED)
831 {
832 ;//application exiting
833 }
834 else
835 {
836 #ifdef LIBSOUP22
837 soup_session_requeue_message(dl->session, msg);
838 #else
839 soup_session_requeue_message(session, msg);
840 #endif
841 return;
842 }
843 }
844
845
846 }
847
848 static void
849 osm_gps_map_download_tile (OsmGpsMap *map, int zoom, int x, int y, gboolean redraw)
850 {
851 SoupMessage *msg;
852 OsmGpsMapPrivate *priv = map->priv;
853 tile_download_t *dl = g_new0(tile_download_t,1);
854
855 //calculate the uri to download
856 dl->uri = replace_map_uri(map, priv->repo_uri, zoom, x, y);
857
858 #ifdef LIBSOUP22
859 dl->session = priv->soup_session;
860 #endif
861
862 //check the tile has not already been queued for download,
863 //or has been attempted, and its missing
864 if (g_hash_table_lookup_extended(priv->tile_queue, dl->uri, NULL, NULL) ||
865 g_hash_table_lookup_extended(priv->missing_tiles, dl->uri, NULL, NULL) )
866 {
867 g_debug("Tile already downloading (or missing)");
868 g_free(dl->uri);
869 g_free(dl);
870 } else {
871 dl->folder = g_strdup_printf("%s%c%d%c%d%c",
872 priv->cache_dir, G_DIR_SEPARATOR,
873 zoom, G_DIR_SEPARATOR,
874 x, G_DIR_SEPARATOR);
875 dl->filename = g_strdup_printf("%s%c%d%c%d%c%d.%s",
876 priv->cache_dir, G_DIR_SEPARATOR,
877 zoom, G_DIR_SEPARATOR,
878 x, G_DIR_SEPARATOR,
879 y,
880 priv->image_format);
881 dl->map = map;
882 dl->redraw = redraw;
883
884 g_debug("Download tile: %d,%d z:%d\n\t%s --> %s", x, y, zoom, dl->uri, dl->filename);
885
886 msg = soup_message_new (SOUP_METHOD_GET, dl->uri);
887 if (msg) {
888 if (priv->the_google) {
889 //Set maps.google.com as the referrer
890 g_debug("Setting Google Referrer");
891 soup_message_headers_append(msg->request_headers, "Referer", "http://maps.google.com/");
892 //For google satelite also set the appropriate cookie value
893 if (priv->uri_format & URI_HAS_Q) {
894 const char *cookie = g_getenv("GOOGLE_COOKIE");
895 if (cookie) {
896 g_debug("Adding Google Cookie");
897 soup_message_headers_append(msg->request_headers, "Cookie", cookie);
898 }
899 }
900 }
901
902 g_hash_table_insert (priv->tile_queue, dl->uri, msg);
903 soup_session_queue_message (priv->soup_session, msg, osm_gps_map_tile_download_complete, dl);
904 } else {
905 g_warning("Could not create soup message");
906 g_free(dl->uri);
907 g_free(dl->folder);
908 g_free(dl->filename);
909 g_free(dl);
910 }
911 }
912 }
913
914 static GdkPixbuf *
915 osm_gps_map_load_cached_tile (OsmGpsMap *map, int zoom, int x, int y)
916 {
917 OsmGpsMapPrivate *priv = map->priv;
918 gchar *filename;
919 GdkPixbuf *pixbuf = NULL;
920 OsmCachedTile *tile;
921
922 filename = g_strdup_printf("%s%c%d%c%d%c%d.%s",
923 priv->cache_dir, G_DIR_SEPARATOR,
924 zoom, G_DIR_SEPARATOR,
925 x, G_DIR_SEPARATOR,
926 y,
927 priv->image_format);
928
929 tile = g_hash_table_lookup (priv->tile_cache, filename);
930 if (tile)
931 {
932 g_free (filename);
933 }
934 else
935 {
936 pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
937 if (pixbuf)
938 {
939 tile = g_slice_new (OsmCachedTile);
940 tile->pixbuf = pixbuf;
941 g_hash_table_insert (priv->tile_cache, filename, tile);
942 }
943 }
944
945 /* set/update the redraw_cycle timestamp on the tile */
946 if (tile)
947 {
948 tile->redraw_cycle = priv->redraw_cycle;
949 pixbuf = g_object_ref (tile->pixbuf);
950 }
951
952 return pixbuf;
953 }
954
955 static GdkPixbuf *
956 osm_gps_map_find_bigger_tile (OsmGpsMap *map, int zoom, int x, int y,
957 int *zoom_found)
958 {
959 GdkPixbuf *pixbuf;
960 int next_zoom, next_x, next_y;
961
962 if (zoom == 0) return NULL;
963 next_zoom = zoom - 1;
964 next_x = x / 2;
965 next_y = y / 2;
966 pixbuf = osm_gps_map_load_cached_tile (map, next_zoom, next_x, next_y);
967 if (pixbuf)
968 *zoom_found = next_zoom;
969 else
970 pixbuf = osm_gps_map_find_bigger_tile (map, next_zoom, next_x, next_y,
971 zoom_found);
972 return pixbuf;
973 }
974
975 static GdkPixbuf *
976 osm_gps_map_render_missing_tile_upscaled (OsmGpsMap *map, int zoom,
977 int x, int y)
978 {
979 GdkPixbuf *pixbuf, *big, *area;
980 int zoom_big, zoom_diff, area_size, area_x, area_y;
981 int modulo;
982
983 big = osm_gps_map_find_bigger_tile (map, zoom, x, y, &zoom_big);
984 if (!big) return NULL;
985
986 g_debug ("Found bigger tile (zoom = %d, wanted = %d)", zoom_big, zoom);
987
988 /* get a Pixbuf for the area to magnify */
989 zoom_diff = zoom - zoom_big;
990 area_size = TILESIZE >> zoom_diff;
991 modulo = 1 << zoom_diff;
992 area_x = (x % modulo) * area_size;
993 area_y = (y % modulo) * area_size;
994 area = gdk_pixbuf_new_subpixbuf (big, area_x, area_y,
995 area_size, area_size);
996 g_object_unref (big);
997 pixbuf = gdk_pixbuf_scale_simple (area, TILESIZE, TILESIZE,
998 GDK_INTERP_NEAREST);
999 g_object_unref (area);
1000 return pixbuf;
1001 }
1002
1003 static GdkPixbuf *
1004 osm_gps_map_render_missing_tile (OsmGpsMap *map, int zoom, int x, int y)
1005 {
1006 /* maybe TODO: render from downscaled tiles, if the following fails */
1007 return osm_gps_map_render_missing_tile_upscaled (map, zoom, x, y);
1008 }
1009
1010 static void
1011 osm_gps_map_load_tile (OsmGpsMap *map, int zoom, int x, int y, int offset_x, int offset_y)
1012 {
1013 OsmGpsMapPrivate *priv = map->priv;
1014 gchar *filename;
1015 GdkPixbuf *pixbuf;
1016
1017 g_debug("Load tile %d,%d (%d,%d) z:%d", x, y, offset_x, offset_y, zoom);
1018
1019 if (priv->map_source == OSM_GPS_MAP_SOURCE_NULL) {
1020 osm_gps_map_blit_tile(map, priv->null_tile, offset_x,offset_y);
1021 return;
1022 }
1023
1024 filename = g_strdup_printf("%s%c%d%c%d%c%d.%s",
1025 priv->cache_dir, G_DIR_SEPARATOR,
1026 zoom, G_DIR_SEPARATOR,
1027 x, G_DIR_SEPARATOR,
1028 y,
1029 priv->image_format);
1030
1031 /* try to get file from internal cache first */
1032 if(!(pixbuf = osm_gps_map_load_cached_tile(map, zoom, x, y)))
1033 pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
1034
1035 if(pixbuf)
1036 {
1037 g_debug("Found tile %s", filename);
1038 osm_gps_map_blit_tile(map, pixbuf, offset_x,offset_y);
1039 g_object_unref (pixbuf);
1040 }
1041 else
1042 {
1043 if (priv->map_auto_download)
1044 osm_gps_map_download_tile(map, zoom, x, y, TRUE);
1045
1046 /* try to render the tile by scaling cached tiles from other zoom
1047 * levels */
1048 pixbuf = osm_gps_map_render_missing_tile (map, zoom, x, y);
1049 if (pixbuf)
1050 {
1051 gdk_draw_pixbuf (priv->pixmap,
1052 priv->gc_map,
1053 pixbuf,
1054 0,0,
1055 offset_x,offset_y,
1056 TILESIZE,TILESIZE,
1057 GDK_RGB_DITHER_NONE, 0, 0);
1058 g_object_unref (pixbuf);
1059 }
1060 else
1061 {
1062 //prevent some artifacts when drawing not yet loaded areas.
1063 gdk_draw_rectangle (priv->pixmap,
1064 GTK_WIDGET(map)->style->white_gc,
1065 TRUE, offset_x, offset_y, TILESIZE, TILESIZE);
1066 }
1067 }
1068 g_free(filename);
1069 }
1070
1071 static void
1072 osm_gps_map_fill_tiles_pixel (OsmGpsMap *map)
1073 {
1074 OsmGpsMapPrivate *priv = map->priv;
1075 int i,j, width, height, tile_x0, tile_y0, tiles_nx, tiles_ny;
1076 int offset_xn = 0;
1077 int offset_yn = 0;
1078 int offset_x;
1079 int offset_y;
1080
1081 g_debug("Fill tiles: %d,%d z:%d", priv->map_x, priv->map_y, priv->map_zoom);
1082
1083 offset_x = - priv->map_x % TILESIZE;
1084 offset_y = - priv->map_y % TILESIZE;
1085 if (offset_x > 0) offset_x -= TILESIZE;
1086 if (offset_y > 0) offset_y -= TILESIZE;
1087
1088 offset_xn = offset_x + EXTRA_BORDER;
1089 offset_yn = offset_y + EXTRA_BORDER;
1090
1091 width = GTK_WIDGET(map)->allocation.width;
1092 height = GTK_WIDGET(map)->allocation.height;
1093
1094 tiles_nx = (width - offset_x) / TILESIZE + 1;
1095 tiles_ny = (height - offset_y) / TILESIZE + 1;
1096
1097 tile_x0 = floor((float)priv->map_x / (float)TILESIZE);
1098 tile_y0 = floor((float)priv->map_y / (float)TILESIZE);
1099
1100 //TODO: implement wrap around
1101 for (i=tile_x0; i<(tile_x0+tiles_nx);i++)
1102 {
1103 for (j=tile_y0; j<(tile_y0+tiles_ny); j++)
1104 {
1105 if( j<0 || i<0 || i>=exp(priv->map_zoom * M_LN2) || j>=exp(priv->map_zoom * M_LN2))
1106 {
1107 gdk_draw_rectangle (priv->pixmap,
1108 GTK_WIDGET(map)->style->white_gc,
1109 TRUE,
1110 offset_xn, offset_yn,
1111 TILESIZE,TILESIZE);
1112 }
1113 else
1114 {
1115 osm_gps_map_load_tile(map,
1116 priv->map_zoom,
1117 i,j,
1118 offset_xn,offset_yn);
1119 }
1120 offset_yn += TILESIZE;
1121 }
1122 offset_xn += TILESIZE;
1123 offset_yn = offset_y + EXTRA_BORDER;
1124 }
1125 }
1126
1127 static void
1128 osm_gps_map_print_track (OsmGpsMap *map, GSList *trackpoint_list)
1129 {
1130 OsmGpsMapPrivate *priv = map->priv;
1131
1132 GSList *list;
1133 int x,y;
1134 int min_x = 0,min_y = 0,max_x = 0,max_y = 0;
1135 int lw = priv->ui_gps_track_width;
1136 int map_x0, map_y0;
1137 #ifdef USE_CAIRO
1138 cairo_t *cr;
1139 #else
1140 int last_x = 0, last_y = 0;
1141 GdkColor color;
1142 GdkGC *gc;
1143 #endif
1144
1145 #ifdef USE_CAIRO
1146 cr = gdk_cairo_create(priv->pixmap);
1147 cairo_set_line_width (cr, lw);
1148 cairo_set_source_rgba (cr, 60000.0/65535.0, 0.0, 0.0, 0.6);
1149 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1150 cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
1151 #else
1152 gc = gdk_gc_new(priv->pixmap);
1153 color.green = 0;
1154 color.blue = 0;
1155 color.red = 60000;
1156 gdk_gc_set_rgb_fg_color(gc, &color);
1157 gdk_gc_set_line_attributes(gc, lw, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
1158 #endif
1159
1160 map_x0 = priv->map_x - EXTRA_BORDER;
1161 map_y0 = priv->map_y - EXTRA_BORDER;
1162 for(list = trackpoint_list; list != NULL; list = list->next)
1163 {
1164 coord_t *tp = list->data;
1165
1166 x = lon2pixel(priv->map_zoom, tp->rlon) - map_x0;
1167 y = lat2pixel(priv->map_zoom, tp->rlat) - map_y0;
1168
1169 // first time through loop
1170 if (list == trackpoint_list) {
1171 #ifdef USE_CAIRO
1172 cairo_move_to(cr, x, y);
1173 #else
1174 last_x = x;
1175 last_y = y;
1176 #endif
1177 }
1178
1179 #ifdef USE_CAIRO
1180 cairo_line_to(cr, x, y);
1181 #else
1182 gdk_draw_line (priv->pixmap, gc, x, y, last_x, last_y);
1183 last_x = x;
1184 last_y = y;
1185 #endif
1186
1187 max_x = MAX(x,max_x);
1188 min_x = MIN(x,min_x);
1189 max_y = MAX(y,max_y);
1190 min_y = MIN(y,min_y);
1191 }
1192
1193 gtk_widget_queue_draw_area (
1194 GTK_WIDGET(map),
1195 min_x - lw,
1196 min_y - lw,
1197 max_x + (lw * 2),
1198 max_y + (lw * 2));
1199
1200 #ifdef USE_CAIRO
1201 cairo_stroke(cr);
1202 cairo_destroy(cr);
1203 #else
1204 g_object_unref(gc);
1205 #endif
1206 }
1207
1208 /* Prints the gps trip history, and any other tracks */
1209 static void
1210 osm_gps_map_print_tracks (OsmGpsMap *map)
1211 {
1212 OsmGpsMapPrivate *priv = map->priv;
1213
1214 if (priv->show_trip_history)
1215 osm_gps_map_print_track (map, priv->trip_history);
1216
1217 if (priv->tracks)
1218 {
1219 GSList* tmp = priv->tracks;
1220 while (tmp != NULL)
1221 {
1222 osm_gps_map_print_track (map, tmp->data);
1223 tmp = g_slist_next(tmp);
1224 }
1225 }
1226 }
1227
1228 static gboolean
1229 osm_gps_map_purge_cache_check(gpointer key, gpointer value, gpointer user)
1230 {
1231 return (((OsmCachedTile*)value)->redraw_cycle != ((OsmGpsMapPrivate*)user)->redraw_cycle);
1232 }
1233
1234 static void
1235 osm_gps_map_purge_cache (OsmGpsMap *map)
1236 {
1237 OsmGpsMapPrivate *priv = map->priv;
1238
1239 if (g_hash_table_size (priv->tile_cache) < priv->max_tile_cache_size)
1240 return;
1241
1242 /* run through the cache, and remove the tiles which have not been used
1243 * during the last redraw operation */
1244 g_hash_table_foreach_remove(priv->tile_cache, osm_gps_map_purge_cache_check, priv);
1245 }
1246
1247 static gboolean
1248 osm_gps_map_map_redraw (OsmGpsMap *map)
1249 {
1250 OsmGpsMapPrivate *priv = map->priv;
1251
1252 priv->idle_map_redraw = 0;
1253
1254 /* the motion_notify handler uses priv->pixmap to redraw the area; if we
1255 * change it while we are dragging, we will end up showing it in the wrong
1256 * place. This could be fixed by carefully recompute the coordinates, but
1257 * for now it's easier just to disable redrawing the map while dragging */
1258 if (priv->dragging)
1259 return FALSE;
1260
1261 priv->redraw_cycle++;
1262
1263 /* draw white background to initialise pixmap */
1264 gdk_draw_rectangle (
1265 priv->pixmap,
1266 GTK_WIDGET(map)->style->white_gc,
1267 TRUE,
1268 0, 0,
1269 GTK_WIDGET(map)->allocation.width + EXTRA_BORDER * 2,
1270 GTK_WIDGET(map)->allocation.height + EXTRA_BORDER * 2);
1271
1272 osm_gps_map_fill_tiles_pixel(map);
1273
1274 osm_gps_map_print_tracks(map);
1275 osm_gps_map_draw_gps_point(map);
1276 osm_gps_map_print_images(map);
1277
1278 osm_gps_map_draw_balloon(map);
1279
1280 //osm_gps_map_osd_speed(map, 1.5);
1281 osm_gps_map_purge_cache(map);
1282 gtk_widget_queue_draw (GTK_WIDGET (map));
1283
1284 return FALSE;
1285 }
1286
1287 static void
1288 osm_gps_map_map_redraw_idle (OsmGpsMap *map)
1289 {
1290 OsmGpsMapPrivate *priv = map->priv;
1291
1292 if (priv->idle_map_redraw == 0)
1293 priv->idle_map_redraw = g_idle_add ((GSourceFunc)osm_gps_map_map_redraw, map);
1294 }
1295
1296 static void
1297 osm_gps_map_init (OsmGpsMap *object)
1298 {
1299 OsmGpsMapPrivate *priv;
1300
1301 priv = G_TYPE_INSTANCE_GET_PRIVATE (object, OSM_TYPE_GPS_MAP, OsmGpsMapPrivate);
1302 object->priv = priv;
1303
1304 priv->pixmap = NULL;
1305
1306 priv->trip_history = NULL;
1307 priv->gps = g_new0(coord_t, 1);
1308 priv->gps_valid = FALSE;
1309
1310 priv->tracks = NULL;
1311 priv->images = NULL;
1312
1313 priv->drag_counter = 0;
1314 priv->drag_mouse_dx = 0;
1315 priv->drag_mouse_dy = 0;
1316 priv->drag_start_mouse_x = 0;
1317 priv->drag_start_mouse_y = 0;
1318
1319 priv->uri_format = 0;
1320 priv->the_google = FALSE;
1321
1322 priv->map_source = -1;
1323
1324 #ifndef LIBSOUP22
1325 //Change naumber of concurrent connections option?
1326 priv->soup_session = soup_session_async_new_with_options(
1327 SOUP_SESSION_USER_AGENT,
1328 "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11",
1329 NULL);
1330 #else
1331 /* libsoup-2.2 seems not to be able to set the user agent */
1332 priv->soup_session = soup_session_async_new();
1333 #endif
1334
1335 //Hash table which maps tile d/l URIs to SoupMessage requests
1336 priv->tile_queue = g_hash_table_new (g_str_hash, g_str_equal);
1337
1338 //Some mapping providers (Google) have varying degrees of tiles at multiple
1339 //zoom levels
1340 priv->missing_tiles = g_hash_table_new (g_str_hash, g_str_equal);
1341
1342 /* memory cache for most recently used tiles */
1343 priv->tile_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
1344 g_free, (GDestroyNotify)cached_tile_free);
1345 priv->max_tile_cache_size = 20;
1346
1347 gtk_widget_add_events (GTK_WIDGET (object),
1348 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1349 GDK_POINTER_MOTION_MASK |
1350 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
1351 GTK_WIDGET_SET_FLAGS (object, GTK_CAN_FOCUS);
1352
1353 g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, my_log_handler, NULL);
1354 }
1355
1356 #ifndef G_CHECKSUM_MD5
1357 /* simple hash algorithm hack if md5 is not present */
1358 static char *simple_hash(char *str) {
1359 union {
1360 char str[4];
1361 gulong val;
1362 } hash = { .val = 0x55555555 };
1363
1364 while(*str) {
1365 hash.str[(int)str & 3] ^= *str;
1366 str++;
1367 }
1368 return g_strdup_printf("%08lX", hash.val);
1369 }
1370 #endif
1371
1372 static GObject *
1373 osm_gps_map_constructor (GType gtype, guint n_properties, GObjectConstructParam *properties)
1374 {
1375 GObject *object;
1376 OsmGpsMapPrivate *priv;
1377 OsmGpsMap *map;
1378 const char *uri;
1379
1380 //Always chain up to the parent constructor
1381 object = G_OBJECT_CLASS(osm_gps_map_parent_class)->constructor(gtype, n_properties, properties);
1382 map = OSM_GPS_MAP(object);
1383 priv = OSM_GPS_MAP_PRIVATE(object);
1384
1385 //user can specify a map source ID, or a repo URI as the map source
1386 uri = osm_gps_map_source_get_repo_uri(OSM_GPS_MAP_SOURCE_NULL);
1387 if ( (priv->map_source == 0) || (strcmp(priv->repo_uri, uri) == 0) ) {
1388 g_debug("Using null source");
1389 priv->map_source = OSM_GPS_MAP_SOURCE_NULL;
1390
1391 priv->null_tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 256, 256);
1392 gdk_pixbuf_fill(priv->null_tile, 0xcccccc00);
1393 }
1394 else if (priv->map_source >= 0) {
1395 //check if the source given is valid
1396 uri = osm_gps_map_source_get_repo_uri(priv->map_source);
1397 if (uri) {
1398 g_debug("Setting map source from ID");
1399 g_free(priv->repo_uri);
1400
1401 priv->repo_uri = g_strdup(uri);
1402 priv->image_format = g_strdup(
1403 osm_gps_map_source_get_image_format(priv->map_source));
1404 priv->max_zoom = osm_gps_map_source_get_max_zoom(priv->map_source);
1405 priv->min_zoom = osm_gps_map_source_get_min_zoom(priv->map_source);
1406 }
1407 }
1408
1409 if (!priv->cache_dir_is_full_path) {
1410 #ifdef G_CHECKSUM_MD5
1411 char *md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, priv->repo_uri, -1);
1412 #else
1413 char *md5 = simple_hash(priv->repo_uri);
1414 #endif
1415
1416 if (priv->cache_dir) {
1417 char *old = priv->cache_dir;
1418 //the new cachedir is the given cache dir + the md5 of the repo_uri
1419 priv->cache_dir = g_strdup_printf("%s%c%s", old, G_DIR_SEPARATOR, md5);
1420 g_debug("Adjusting cache dir %s -> %s", old, priv->cache_dir);
1421 g_free(old);
1422 } else {
1423 //the new cachedir is the current dir + the md5 of the repo_uri
1424 priv->cache_dir = g_strdup(md5);
1425 }
1426
1427 g_free(md5);
1428 }
1429
1430 inspect_map_uri(map);
1431
1432 return object;
1433 }
1434
1435 static void
1436 osm_gps_map_dispose (GObject *object)
1437 {
1438 OsmGpsMap *map = OSM_GPS_MAP(object);
1439 OsmGpsMapPrivate *priv = map->priv;
1440
1441 if (priv->is_disposed)
1442 return;
1443
1444 priv->is_disposed = TRUE;
1445
1446 soup_session_abort(priv->soup_session);
1447 g_object_unref(priv->soup_session);
1448
1449 g_hash_table_destroy(priv->tile_queue);
1450 g_hash_table_destroy(priv->missing_tiles);
1451 g_hash_table_destroy(priv->tile_cache);
1452
1453 osm_gps_map_free_images(map);
1454
1455 if(priv->pixmap)
1456 g_object_unref (priv->pixmap);
1457
1458 if (priv->null_tile)
1459 g_object_unref (priv->null_tile);
1460
1461 if(priv->gc_map)
1462 g_object_unref(priv->gc_map);
1463
1464 if (priv->idle_map_redraw != 0)
1465 g_source_remove (priv->idle_map_redraw);
1466
1467 G_OBJECT_CLASS (osm_gps_map_parent_class)->dispose (object);
1468 }
1469
1470 static void
1471 osm_gps_map_finalize (GObject *object)
1472 {
1473 OsmGpsMap *map = OSM_GPS_MAP(object);
1474 OsmGpsMapPrivate *priv = map->priv;
1475
1476 g_free(priv->cache_dir);
1477 g_free(priv->repo_uri);
1478 g_free(priv->image_format);
1479
1480 osm_gps_map_free_trip(map);
1481 osm_gps_map_free_tracks(map);
1482
1483 G_OBJECT_CLASS (osm_gps_map_parent_class)->finalize (object);
1484 }
1485
1486 static void
1487 osm_gps_map_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1488 {
1489 g_return_if_fail (OSM_IS_GPS_MAP (object));
1490 OsmGpsMap *map = OSM_GPS_MAP(object);
1491 OsmGpsMapPrivate *priv = map->priv;
1492
1493 switch (prop_id)
1494 {
1495 case PROP_AUTO_CENTER:
1496 priv->map_auto_center = g_value_get_boolean (value);
1497 break;
1498 case PROP_RECORD_TRIP_HISTORY:
1499 priv->record_trip_history = g_value_get_boolean (value);
1500 break;
1501 case PROP_SHOW_TRIP_HISTORY:
1502 priv->show_trip_history = g_value_get_boolean (value);
1503 break;
1504 case PROP_AUTO_DOWNLOAD:
1505 priv->map_auto_download = g_value_get_boolean (value);
1506 break;
1507 case PROP_REPO_URI:
1508 priv->repo_uri = g_value_dup_string (value);
1509 break;
1510 case PROP_PROXY_URI:
1511 if ( g_value_get_string(value) ) {
1512 priv->proxy_uri = g_value_dup_string (value);
1513 g_debug("Setting proxy server: %s", priv->proxy_uri);
1514
1515 #ifndef LIBSOUP22
1516 GValue val = {0};
1517
1518 SoupURI* uri = soup_uri_new(priv->proxy_uri);
1519 g_value_init(&val, SOUP_TYPE_URI);
1520 g_value_take_boxed(&val, uri);
1521
1522 g_object_set_property(G_OBJECT(priv->soup_session),SOUP_SESSION_PROXY_URI,&val);
1523 #else
1524 SoupUri* uri = soup_uri_new(priv->proxy_uri);
1525 g_object_set(G_OBJECT(priv->soup_session), SOUP_SESSION_PROXY_URI, uri, NULL);
1526 #endif
1527 } else
1528 priv->proxy_uri = NULL;
1529
1530 break;
1531 case PROP_TILE_CACHE_DIR:
1532 if ( g_value_get_string(value) )
1533 priv->cache_dir = g_value_dup_string (value);
1534 break;
1535 case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
1536 priv->cache_dir_is_full_path = g_value_get_boolean (value);
1537 break;
1538 case PROP_ZOOM:
1539 priv->map_zoom = g_value_get_int (value);
1540 break;
1541 case PROP_MAX_ZOOM:
1542 priv->max_zoom = g_value_get_int (value);
1543 break;
1544 case PROP_MIN_ZOOM:
1545 priv->min_zoom = g_value_get_int (value);
1546 break;
1547 case PROP_MAP_X:
1548 priv->map_x = g_value_get_int (value);
1549 priv->center_coord_set = FALSE;
1550 break;
1551 case PROP_MAP_Y:
1552 priv->map_y = g_value_get_int (value);
1553 priv->center_coord_set = FALSE;
1554 break;
1555 case PROP_GPS_TRACK_WIDTH:
1556 priv->ui_gps_track_width = g_value_get_int (value);
1557 break;
1558 case PROP_GPS_POINT_R1:
1559 priv->ui_gps_point_inner_radius = g_value_get_int (value);
1560 break;
1561 case PROP_GPS_POINT_R2:
1562 priv->ui_gps_point_outer_radius = g_value_get_int (value);
1563 break;
1564 case PROP_MAP_SOURCE:
1565 priv->map_source = g_value_get_int (value);
1566 break;
1567 case PROP_IMAGE_FORMAT:
1568 priv->image_format = g_value_dup_string (value);
1569 break;
1570 default:
1571 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1572 break;
1573 }
1574 }
1575
1576 static void
1577 osm_gps_map_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
1578 {
1579 g_return_if_fail (OSM_IS_GPS_MAP (object));
1580 float lat,lon;
1581 OsmGpsMap *map = OSM_GPS_MAP(object);
1582 OsmGpsMapPrivate *priv = map->priv;
1583
1584 switch (prop_id)
1585 {
1586 case PROP_AUTO_CENTER:
1587 g_value_set_boolean(value, priv->map_auto_center);
1588 break;
1589 case PROP_RECORD_TRIP_HISTORY:
1590 g_value_set_boolean(value, priv->record_trip_history);
1591 break;
1592 case PROP_SHOW_TRIP_HISTORY:
1593 g_value_set_boolean(value, priv->show_trip_history);
1594 break;
1595 case PROP_AUTO_DOWNLOAD:
1596 g_value_set_boolean(value, priv->map_auto_download);
1597 break;
1598 case PROP_REPO_URI:
1599 g_value_set_string(value, priv->repo_uri);
1600 break;
1601 case PROP_PROXY_URI:
1602 g_value_set_string(value, priv->proxy_uri);
1603 break;
1604 case PROP_TILE_CACHE_DIR:
1605 g_value_set_string(value, priv->cache_dir);
1606 break;
1607 case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
1608 g_value_set_boolean(value, priv->cache_dir_is_full_path);
1609 break;
1610 case PROP_ZOOM:
1611 g_value_set_int(value, priv->map_zoom);
1612 break;
1613 case PROP_MAX_ZOOM:
1614 g_value_set_int(value, priv->max_zoom);
1615 break;
1616 case PROP_MIN_ZOOM:
1617 g_value_set_int(value, priv->min_zoom);
1618 break;
1619 case PROP_LATITUDE:
1620 lat = pixel2lat(priv->map_zoom,
1621 priv->map_y + (GTK_WIDGET(map)->allocation.height / 2));
1622 g_value_set_float(value, rad2deg(lat));
1623 break;
1624 case PROP_LONGITUDE:
1625 lon = pixel2lon(priv->map_zoom,
1626 priv->map_x + (GTK_WIDGET(map)->allocation.width / 2));
1627 g_value_set_float(value, rad2deg(lon));
1628 break;
1629 case PROP_MAP_X:
1630 g_value_set_int(value, priv->map_x);
1631 break;
1632 case PROP_MAP_Y:
1633 g_value_set_int(value, priv->map_y);
1634 break;
1635 case PROP_TILES_QUEUED:
1636 g_value_set_int(value, g_hash_table_size(priv->tile_queue));
1637 break;
1638 case PROP_GPS_TRACK_WIDTH:
1639 g_value_set_int(value, priv->ui_gps_track_width);
1640 break;
1641 case PROP_GPS_POINT_R1:
1642 g_value_set_int(value, priv->ui_gps_point_inner_radius);
1643 break;
1644 case PROP_GPS_POINT_R2:
1645 g_value_set_int(value, priv->ui_gps_point_outer_radius);
1646 break;
1647 case PROP_MAP_SOURCE:
1648 g_value_set_int(value, priv->map_source);
1649 break;
1650 case PROP_IMAGE_FORMAT:
1651 g_value_set_string(value, priv->image_format);
1652 break;
1653 default:
1654 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1655 break;
1656 }
1657 }
1658
1659 static gboolean
1660 osm_gps_map_scroll_event (GtkWidget *widget, GdkEventScroll *event)
1661 {
1662 OsmGpsMap *map = OSM_GPS_MAP(widget);
1663 OsmGpsMapPrivate *priv = map->priv;
1664
1665 if (event->direction == GDK_SCROLL_UP)
1666 {
1667 osm_gps_map_set_zoom(map, priv->map_zoom+1);
1668 }
1669 else
1670 {
1671 osm_gps_map_set_zoom(map, priv->map_zoom-1);
1672 }
1673
1674 return FALSE;
1675 }
1676
1677 static gboolean
1678 osm_gps_map_button_press (GtkWidget *widget, GdkEventButton *event)
1679 {
1680 OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1681
1682 priv->drag_counter = 0;
1683 priv->drag_start_mouse_x = (int) event->x;
1684 priv->drag_start_mouse_y = (int) event->y;
1685 priv->drag_start_map_x = priv->map_x;
1686 priv->drag_start_map_y = priv->map_y;
1687
1688 return FALSE;
1689 }
1690
1691 static gboolean
1692 osm_gps_map_button_release (GtkWidget *widget, GdkEventButton *event)
1693 {
1694 OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1695
1696 if (priv->dragging)
1697 {
1698 priv->dragging = FALSE;
1699
1700 priv->map_x = priv->drag_start_map_x;
1701 priv->map_y = priv->drag_start_map_y;
1702
1703 priv->map_x += (priv->drag_start_mouse_x - (int) event->x);
1704 priv->map_y += (priv->drag_start_mouse_y - (int) event->y);
1705
1706 priv->center_coord_set = FALSE;
1707
1708 osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
1709 }
1710
1711 priv->drag_mouse_dx = 0;
1712 priv->drag_mouse_dy = 0;
1713 priv->drag_counter = 0;
1714
1715 return FALSE;
1716 }
1717
1718 static gboolean
1719 osm_gps_map_motion_notify (GtkWidget *widget, GdkEventMotion *event)
1720 {
1721 int x, y;
1722 GdkModifierType state;
1723 OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1724
1725 if (event->is_hint)
1726 gdk_window_get_pointer (event->window, &x, &y, &state);
1727 else
1728 {
1729 x = event->x;
1730 y = event->y;
1731 state = event->state;
1732 }
1733
1734 // are we being dragged
1735 if (!(state & GDK_BUTTON1_MASK))
1736 return FALSE;
1737
1738 priv->drag_counter++;
1739
1740 // we havent dragged more than 6 pixels
1741 if (priv->drag_counter < 6)
1742 return FALSE;
1743
1744 priv->dragging = TRUE;
1745
1746 if (priv->map_auto_center)
1747 g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
1748
1749 priv->drag_mouse_dx = x - priv->drag_start_mouse_x;
1750 priv->drag_mouse_dy = y - priv->drag_start_mouse_y;
1751
1752 gdk_draw_drawable (
1753 widget->window,
1754 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1755 priv->pixmap,
1756 0,0,
1757 priv->drag_mouse_dx - EXTRA_BORDER, priv->drag_mouse_dy - EXTRA_BORDER,
1758 -1,-1);
1759
1760 //Paint white outside of the map if dragging. Its less
1761 //ugly than painting the corrupted map
1762 if(priv->drag_mouse_dx>EXTRA_BORDER) {
1763 gdk_draw_rectangle (
1764 widget->window,
1765 widget->style->white_gc,
1766 TRUE,
1767 0, 0,
1768 priv->drag_mouse_dx - EXTRA_BORDER,
1769 widget->allocation.height);
1770 }
1771 else if (-priv->drag_mouse_dx > EXTRA_BORDER)
1772 {
1773 gdk_draw_rectangle (
1774 widget->window,
1775 widget->style->white_gc,
1776 TRUE,
1777 priv->drag_mouse_dx + widget->allocation.width + EXTRA_BORDER, 0,
1778 -priv->drag_mouse_dx - EXTRA_BORDER,
1779 widget->allocation.height);
1780 }
1781
1782 if (priv->drag_mouse_dy>EXTRA_BORDER) {
1783 gdk_draw_rectangle (
1784 widget->window,
1785 widget->style->white_gc,
1786 TRUE,
1787 0, 0,
1788 widget->allocation.width,
1789 priv->drag_mouse_dy - EXTRA_BORDER);
1790 }
1791 else if (-priv->drag_mouse_dy > EXTRA_BORDER)
1792 {
1793 gdk_draw_rectangle (
1794 widget->window,
1795 widget->style->white_gc,
1796 TRUE,
1797 0, priv->drag_mouse_dy + widget->allocation.height + EXTRA_BORDER,
1798 widget->allocation.width,
1799 -priv->drag_mouse_dy - EXTRA_BORDER);
1800 }
1801
1802 return FALSE;
1803 }
1804
1805 static gboolean
1806 osm_gps_map_configure (GtkWidget *widget, GdkEventConfigure *event)
1807 {
1808 OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1809
1810 /* create pixmap */
1811 if (priv->pixmap)
1812 g_object_unref (priv->pixmap);
1813
1814 priv->pixmap = gdk_pixmap_new (
1815 widget->window,
1816 widget->allocation.width + EXTRA_BORDER * 2,
1817 widget->allocation.height + EXTRA_BORDER * 2,
1818 -1);
1819
1820 /* and gc, used for clipping (I think......) */
1821 if(priv->gc_map)
1822 g_object_unref(priv->gc_map);
1823
1824 priv->gc_map = gdk_gc_new(priv->pixmap);
1825
1826 osm_gps_map_map_redraw(OSM_GPS_MAP(widget));
1827
1828 return FALSE;
1829 }
1830
1831 static gboolean
1832 osm_gps_map_expose (GtkWidget *widget, GdkEventExpose *event)
1833 {
1834 OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1835
1836 gdk_draw_drawable (
1837 widget->window,
1838 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1839 priv->pixmap,
1840 event->area.x + EXTRA_BORDER, event->area.y + EXTRA_BORDER,
1841 event->area.x, event->area.y,
1842 event->area.width, event->area.height);
1843
1844 return FALSE;
1845 }
1846
1847 static void
1848 osm_gps_map_class_init (OsmGpsMapClass *klass)
1849 {
1850 GObjectClass* object_class = G_OBJECT_CLASS (klass);
1851 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1852
1853 g_type_class_add_private (klass, sizeof (OsmGpsMapPrivate));
1854
1855 object_class->dispose = osm_gps_map_dispose;
1856 object_class->finalize = osm_gps_map_finalize;
1857 object_class->constructor = osm_gps_map_constructor;
1858 object_class->set_property = osm_gps_map_set_property;
1859 object_class->get_property = osm_gps_map_get_property;
1860
1861 widget_class->expose_event = osm_gps_map_expose;
1862 widget_class->configure_event = osm_gps_map_configure;
1863 widget_class->button_press_event = osm_gps_map_button_press;
1864 widget_class->button_release_event = osm_gps_map_button_release;
1865 widget_class->motion_notify_event = osm_gps_map_motion_notify;
1866 widget_class->scroll_event = osm_gps_map_scroll_event;
1867
1868 g_object_class_install_property (object_class,
1869 PROP_AUTO_CENTER,
1870 g_param_spec_boolean ("auto-center",
1871 "auto center",
1872 "map auto center",
1873 TRUE,
1874 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1875
1876 g_object_class_install_property (object_class,
1877 PROP_RECORD_TRIP_HISTORY,
1878 g_param_spec_boolean ("record-trip-history",
1879 "record trip history",
1880 "should all gps points be recorded in a trip history",
1881 TRUE,
1882 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1883
1884 g_object_class_install_property (object_class,
1885 PROP_SHOW_TRIP_HISTORY,
1886 g_param_spec_boolean ("show-trip-history",
1887 "show trip history",
1888 "should the recorded trip history be shown on the map",
1889 TRUE,
1890 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1891
1892 g_object_class_install_property (object_class,
1893 PROP_AUTO_DOWNLOAD,
1894 g_param_spec_boolean ("auto-download",
1895 "auto download",
1896 "map auto download",
1897 TRUE,
1898 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1899
1900 g_object_class_install_property (object_class,
1901 PROP_REPO_URI,
1902 g_param_spec_string ("repo-uri",
1903 "repo uri",
1904 "map source tile repository uri",
1905 OSM_REPO_URI,
1906 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1907
1908 g_object_class_install_property (object_class,
1909 PROP_PROXY_URI,
1910 g_param_spec_string ("proxy-uri",
1911 "proxy uri",
1912 "http proxy uri on NULL",
1913 NULL,
1914 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1915
1916 g_object_class_install_property (object_class,
1917 PROP_TILE_CACHE_DIR,
1918 g_param_spec_string ("tile-cache",
1919 "tile cache",
1920 "osm local tile cache dir",
1921 NULL,
1922 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1923
1924 g_object_class_install_property (object_class,
1925 PROP_TILE_CACHE_DIR_IS_FULL_PATH,
1926 g_param_spec_boolean ("tile-cache-is-full-path",
1927 "tile cache is full path",
1928 "if true, the path passed to tile-cache is interpreted as the full cache path",
1929 FALSE,
1930 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1931
1932 g_object_class_install_property (object_class,
1933 PROP_ZOOM,
1934 g_param_spec_int ("zoom",
1935 "zoom",
1936 "zoom level",
1937 MIN_ZOOM, /* minimum property value */
1938 MAX_ZOOM, /* maximum property value */
1939 3,
1940 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1941
1942 g_object_class_install_property (object_class,
1943 PROP_MAX_ZOOM,
1944 g_param_spec_int ("max-zoom",
1945 "max zoom",
1946 "maximum zoom level",
1947 MIN_ZOOM, /* minimum property value */
1948 MAX_ZOOM, /* maximum property value */
1949 OSM_MAX_ZOOM,
1950 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1951
1952 g_object_class_install_property (object_class,
1953 PROP_MIN_ZOOM,
1954 g_param_spec_int ("min-zoom",
1955 "min zoom",
1956 "minimum zoom level",
1957 MIN_ZOOM, /* minimum property value */
1958 MAX_ZOOM, /* maximum property value */
1959 OSM_MIN_ZOOM,
1960 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1961
1962 g_object_class_install_property (object_class,
1963 PROP_LATITUDE,
1964 g_param_spec_float ("latitude",
1965 "latitude",
1966 "latitude in degrees",
1967 -90.0, /* minimum property value */
1968 90.0, /* maximum property value */
1969 0,
1970 G_PARAM_READABLE));
1971
1972 g_object_class_install_property (object_class,
1973 PROP_LONGITUDE,
1974 g_param_spec_float ("longitude",
1975 "longitude",
1976 "longitude in degrees",
1977 -180.0, /* minimum property value */
1978 180.0, /* maximum property value */
1979 0,
1980 G_PARAM_READABLE));
1981
1982 g_object_class_install_property (object_class,
1983 PROP_MAP_X,
1984 g_param_spec_int ("map-x",
1985 "map-x",
1986 "initial map x location",
1987 G_MININT, /* minimum property value */
1988 G_MAXINT, /* maximum property value */
1989 890,
1990 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1991
1992 g_object_class_install_property (object_class,
1993 PROP_MAP_Y,
1994 g_param_spec_int ("map-y",
1995 "map-y",
1996 "initial map y location",
1997 G_MININT, /* minimum property value */
1998 G_MAXINT, /* maximum property value */
1999 515,
2000 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2001
2002 g_object_class_install_property (object_class,
2003 PROP_TILES_QUEUED,
2004 g_param_spec_int ("tiles-queued",
2005 "tiles-queued",
2006 "number of tiles currently waiting to download",
2007 G_MININT, /* minimum property value */
2008 G_MAXINT, /* maximum property value */
2009 0,
2010 G_PARAM_READABLE));
2011
2012 g_object_class_install_property (object_class,
2013 PROP_GPS_TRACK_WIDTH,
2014 g_param_spec_int ("gps-track-width",
2015 "gps-track-width",
2016 "width of the lines drawn for the gps track",
2017 1, /* minimum property value */
2018 G_MAXINT, /* maximum property value */
2019 4,
2020 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2021
2022 g_object_class_install_property (object_class,
2023 PROP_GPS_POINT_R1,
2024 g_param_spec_int ("gps-track-point-radius",
2025 "gps-track-point-radius",
2026 "radius of the gps point inner circle",
2027 0, /* minimum property value */
2028 G_MAXINT, /* maximum property value */
2029 5,
2030 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2031
2032 g_object_class_install_property (object_class,
2033 PROP_GPS_POINT_R2,
2034 g_param_spec_int ("gps-track-highlight-radius",
2035 "gps-track-highlight-radius",
2036 "radius of the gps point highlight circle",
2037 0, /* minimum property value */
2038 G_MAXINT, /* maximum property value */
2039 20,
2040 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
2041
2042 g_object_class_install_property (object_class,
2043 PROP_MAP_SOURCE,
2044 g_param_spec_int ("map-source",
2045 "map source",
2046 "map source ID",
2047 -1, /* minimum property value */
2048 G_MAXINT, /* maximum property value */
2049 -1,
2050 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2051
2052 g_object_class_install_property (object_class,
2053 PROP_IMAGE_FORMAT,
2054 g_param_spec_string ("image-format",
2055 "image format",
2056 "map source tile repository image format (jpg, png)",
2057 OSM_IMAGE_FORMAT,
2058 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2059 }
2060
2061 const char*
2062 osm_gps_map_source_get_friendly_name(OsmGpsMapSource_t source)
2063 {
2064 switch(source)
2065 {
2066 case OSM_GPS_MAP_SOURCE_NULL:
2067 return "None";
2068 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
2069 return "OpenStreetMap";
2070 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
2071 return "OpenStreetMap Renderer";
2072 case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
2073 return "OpenAerialMap";
2074 case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
2075 return "Maps-For-Free";
2076 case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
2077 return "Google Maps";
2078 case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
2079 return "Google Satellite";
2080 case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
2081 return "Google Hybrid";
2082 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
2083 return "Virtual Earth";
2084 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
2085 return "Virtual Earth Satellite";
2086 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
2087 return "Virtual Earth Hybrid";
2088 case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
2089 return "Yahoo Maps";
2090 case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
2091 return "Yahoo Satellite";
2092 case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
2093 return "Yahoo Hybrid";
2094 default:
2095 return NULL;
2096 }
2097 return NULL;
2098 }
2099
2100 //http://www.internettablettalk.com/forums/showthread.php?t=5209
2101 //https://garage.maemo.org/plugins/scmsvn/viewcvs.php/trunk/src/maps.c?root=maemo-mapper&view=markup
2102 //http://www.ponies.me.uk/maps/GoogleTileUtils.java
2103 //http://www.mgmaps.com/cache/MapTileCacher.perl
2104 const char*
2105 osm_gps_map_source_get_repo_uri(OsmGpsMapSource_t source)
2106 {
2107 switch(source)
2108 {
2109 case OSM_GPS_MAP_SOURCE_NULL:
2110 return "none://";
2111 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
2112 return OSM_REPO_URI;
2113 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
2114 return "http://tah.openstreetmap.org/Tiles/tile/#Z/#X/#Y.png";
2115 case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
2116 return "http://tile.openaerialmap.org/tiles/1.0.0/openaerialmap-900913/#Z/#X/#Y.jpg";
2117 case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
2118 return "http://maps-for-free.com/layer/relief/z#Z/row#Y/#Z_#X-#Y.jpg";
2119 case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
2120 return "http://mt#R.google.com/vt/v=w2.97&x=#X&y=#Y&z=#Z";
2121 case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
2122 return "http://khm#R.google.com/kh?n=404&v=3&t=#Q";
2123 case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
2124 return NULL; /* No longer working "http://mt#R.google.com/mt?n=404&v=w2t.99&x=#X&y=#Y&zoom=#S" */
2125 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
2126 return "http://a#R.ortho.tiles.virtualearth.net/tiles/r#W.jpeg?g=50";
2127 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
2128 return "http://a#R.ortho.tiles.virtualearth.net/tiles/a#W.jpeg?g=50";
2129 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
2130 return "http://a#R.ortho.tiles.virtualearth.net/tiles/h#W.jpeg?g=50";
2131 case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
2132 case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
2133 case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
2134 /* TODO: Implement signed Y, aka U
2135 * http://us.maps3.yimg.com/aerial.maps.yimg.com/ximg?v=1.7&t=a&s=256&x=%d&y=%-d&z=%d
2136 * x = tilex,
2137 * y = (1 << (MAX_ZOOM - zoom)) - tiley - 1,
2138 * z = zoom - (MAX_ZOOM - 17));
2139 */
2140 return NULL;
2141 default:
2142 return NULL;
2143 }
2144 return NULL;
2145 }
2146
2147 const char *
2148 osm_gps_map_source_get_image_format(OsmGpsMapSource_t source)
2149 {
2150 switch(source) {
2151 case OSM_GPS_MAP_SOURCE_NULL:
2152 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
2153 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
2154 return "png";
2155 case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
2156 case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
2157 case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
2158 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
2159 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
2160 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
2161 case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
2162 case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
2163 case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
2164 case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
2165 case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
2166 return "jpg";
2167 default:
2168 return "bin";
2169 }
2170 return "bin";
2171 }
2172
2173
2174 int
2175 osm_gps_map_source_get_min_zoom(OsmGpsMapSource_t source)
2176 {
2177 return 1;
2178 }
2179
2180 int
2181 osm_gps_map_source_get_max_zoom(OsmGpsMapSource_t source)
2182 {
2183 switch(source) {
2184 case OSM_GPS_MAP_SOURCE_NULL:
2185 return 18;
2186 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
2187 return OSM_MAX_ZOOM;
2188 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
2189 case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
2190 case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
2191 case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
2192 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
2193 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
2194 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
2195 case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
2196 case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
2197 case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
2198 return 17;
2199 case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
2200 return 11;
2201 case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
2202 return 18;
2203 default:
2204 return 17;
2205 }
2206 return 17;
2207 }
2208
2209 void
2210 osm_gps_map_download_maps (OsmGpsMap *map, coord_t *pt1, coord_t *pt2, int zoom_start, int zoom_end)
2211 {
2212 int i,j,zoom,num_tiles;
2213 OsmGpsMapPrivate *priv = map->priv;
2214
2215 if (pt1 && pt2)
2216 {
2217 gchar *filename;
2218 num_tiles = 0;
2219 zoom_end = CLAMP(zoom_end, priv->min_zoom, priv->max_zoom);
2220 g_debug("Download maps: z:%d->%d",zoom_start, zoom_end);
2221
2222 for(zoom=zoom_start; zoom<=zoom_end; zoom++)
2223 {
2224 int x1,y1,x2,y2;
2225
2226 x1 = (int)floor((float)lon2pixel(zoom, pt1->rlon) / (float)TILESIZE);
2227 y1 = (int)floor((float)lat2pixel(zoom, pt1->rlat) / (float)TILESIZE);
2228
2229 x2 = (int)floor((float)lon2pixel(zoom, pt2->rlon) / (float)TILESIZE);
2230 y2 = (int)floor((float)lat2pixel(zoom, pt2->rlat) / (float)TILESIZE);
2231
2232 // loop x1-x2
2233 for(i=x1; i<=x2; i++)
2234 {
2235 // loop y1 - y2
2236 for(j=y1; j<=y2; j++)
2237 {
2238 // x = i, y = j
2239 filename = g_strdup_printf("%s%c%d%c%d%c%d.%s",
2240 priv->cache_dir, G_DIR_SEPARATOR,
2241 zoom, G_DIR_SEPARATOR,
2242 i, G_DIR_SEPARATOR,
2243 j,
2244 priv->image_format);
2245 if (!g_file_test(filename, G_FILE_TEST_EXISTS))
2246 {
2247 osm_gps_map_download_tile(map, zoom, i, j, FALSE);
2248 num_tiles++;
2249 }
2250 g_free(filename);
2251 }
2252 }
2253 g_debug("DL @Z:%d = %d tiles",zoom,num_tiles);
2254 }
2255 }
2256 }
2257
2258 void
2259 osm_gps_map_get_bbox (OsmGpsMap *map, coord_t *pt1, coord_t *pt2)
2260 {
2261 OsmGpsMapPrivate *priv = map->priv;
2262
2263 if (pt1 && pt2) {
2264 pt1->rlat = pixel2lat(priv->map_zoom, priv->map_y);
2265 pt1->rlon = pixel2lon(priv->map_zoom, priv->map_x);
2266 pt2->rlat = pixel2lat(priv->map_zoom, priv->map_y + GTK_WIDGET(map)->allocation.height);
2267 pt2->rlon = pixel2lon(priv->map_zoom, priv->map_x + GTK_WIDGET(map)->allocation.width);
2268
2269 g_debug("BBOX: %f %f %f %f", pt1->rlat, pt1->rlon, pt2->rlat, pt2->rlon);
2270 }
2271 }
2272
2273 void
2274 osm_gps_map_set_mapcenter (OsmGpsMap *map, float latitude, float longitude, int zoom)
2275 {
2276 osm_gps_map_set_center (map, latitude, longitude);
2277 osm_gps_map_set_zoom (map, zoom);
2278 }
2279
2280 void
2281 osm_gps_map_set_center (OsmGpsMap *map, float latitude, float longitude)
2282 {
2283 int pixel_x, pixel_y;
2284 OsmGpsMapPrivate *priv;
2285
2286 g_return_if_fail (OSM_IS_GPS_MAP (map));
2287 priv = map->priv;
2288
2289 priv->center_rlat = deg2rad(latitude);
2290 priv->center_rlon = deg2rad(longitude);
2291 priv->center_coord_set = TRUE;
2292
2293 // pixel_x,y, offsets
2294 pixel_x = lon2pixel(priv->map_zoom, priv->center_rlon);
2295 pixel_y = lat2pixel(priv->map_zoom, priv->center_rlat);
2296
2297 priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
2298 priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
2299
2300 osm_gps_map_map_redraw_idle(map);
2301 }
2302
2303 int
2304 osm_gps_map_set_zoom (OsmGpsMap *map, int zoom)
2305 {
2306 int zoom_old;
2307 double factor = 0.0;
2308 int width_center, height_center;
2309 OsmGpsMapPrivate *priv;
2310
2311 g_return_val_if_fail (OSM_IS_GPS_MAP (map), 0);
2312 priv = map->priv;
2313
2314 if (zoom != priv->map_zoom)
2315 {
2316 width_center = GTK_WIDGET(map)->allocation.width / 2;
2317 height_center = GTK_WIDGET(map)->allocation.height / 2;
2318
2319 zoom_old = priv->map_zoom;
2320 //constrain zoom min_zoom -> max_zoom
2321 priv->map_zoom = CLAMP(zoom, priv->min_zoom, priv->max_zoom);
2322
2323 if (priv->center_coord_set)
2324 {
2325 priv->map_x = lon2pixel(priv->map_zoom, priv->center_rlon) - width_center;
2326 priv->map_y = lat2pixel(priv->map_zoom, priv->center_rlat) - height_center;
2327 }
2328 else
2329 {
2330 factor = exp(priv->map_zoom * M_LN2)/exp(zoom_old * M_LN2);
2331 priv->map_x = ((priv->map_x + width_center) * factor) - width_center;
2332 priv->map_y = ((priv->map_y + height_center) * factor) - height_center;
2333 }
2334
2335 g_debug("Zoom changed from %d to %d factor:%f x:%d",
2336 zoom_old, priv->map_zoom, factor, priv->map_x);
2337
2338 osm_gps_map_map_redraw_idle(map);
2339 }
2340 return priv->map_zoom;
2341 }
2342
2343 void
2344 osm_gps_map_add_track (OsmGpsMap *map, GSList *track)
2345 {
2346 OsmGpsMapPrivate *priv;
2347
2348 g_return_if_fail (OSM_IS_GPS_MAP (map));
2349 priv = map->priv;
2350
2351 if (track) {
2352 priv->tracks = g_slist_append(priv->tracks, track);
2353 osm_gps_map_map_redraw_idle(map);
2354 }
2355 }
2356
2357 void
2358 osm_gps_map_clear_tracks (OsmGpsMap *map)
2359 {
2360 g_return_if_fail (OSM_IS_GPS_MAP (map));
2361
2362 osm_gps_map_free_tracks(map);
2363 osm_gps_map_map_redraw_idle(map);
2364 }
2365
2366 void
2367 osm_gps_map_add_image (OsmGpsMap *map, float latitude, float longitude, GdkPixbuf *image)
2368 {
2369 g_return_if_fail (OSM_IS_GPS_MAP (map));
2370
2371 if (image) {
2372 OsmGpsMapPrivate *priv = map->priv;
2373 image_t *im;
2374
2375 //cache w/h for speed, and add image to list
2376 im = g_new0(image_t,1);
2377 im->w = gdk_pixbuf_get_width(image);
2378 im->h = gdk_pixbuf_get_height(image);
2379 im->pt.rlat = deg2rad(latitude);
2380 im->pt.rlon = deg2rad(longitude);
2381
2382 g_object_ref(image);
2383 im->image = image;
2384
2385 priv->images = g_slist_append(priv->images, im);
2386
2387 osm_gps_map_map_redraw_idle(map);
2388 }
2389 }
2390
2391 gboolean
2392 osm_gps_map_remove_image (OsmGpsMap *map, GdkPixbuf *image)
2393 {
2394 OsmGpsMapPrivate *priv = map->priv;
2395 if (priv->images) {
2396 GSList *list;
2397 for(list = priv->images; list != NULL; list = list->next)
2398 {
2399 image_t *im = list->data;
2400 if (im->image == image)
2401 {
2402 priv->images = g_slist_remove_link(priv->images, list);
2403 g_object_unref(im->image);
2404 g_free(im);
2405 osm_gps_map_map_redraw_idle(map);
2406 return TRUE;
2407 }
2408 }
2409 }
2410 return FALSE;
2411 }
2412
2413 void
2414 osm_gps_map_clear_images (OsmGpsMap *map)
2415 {
2416 g_return_if_fail (OSM_IS_GPS_MAP (map));
2417
2418 osm_gps_map_free_images(map);
2419 osm_gps_map_map_redraw_idle(map);
2420 }
2421
2422 void
2423 osm_gps_map_osd_speed (OsmGpsMap *map, float speed)
2424 {
2425 OsmGpsMapPrivate *priv;
2426
2427 PangoContext *context = NULL;
2428 PangoLayout *layout = NULL;
2429 PangoFontDescription *desc = NULL;
2430
2431 GdkColor color;
2432 GdkGC *gc;
2433
2434 gchar *buffer;
2435 //static int x = 10, y = 10;
2436 static int width = 0, height = 0;
2437
2438 g_return_if_fail (OSM_IS_GPS_MAP (map));
2439 priv = map->priv;
2440
2441 buffer = g_strdup_printf("%.0f", speed);
2442
2443 /* pango initialisation */
2444 context = gtk_widget_get_pango_context (GTK_WIDGET(map));
2445 layout = pango_layout_new (context);
2446 desc = pango_font_description_new();
2447
2448 pango_font_description_set_size (desc, 40 * PANGO_SCALE);
2449 pango_layout_set_font_description (layout, desc);
2450 pango_layout_set_text (layout, buffer, strlen(buffer));
2451
2452 gc = gdk_gc_new (GTK_WIDGET(map)->window);
2453
2454 color.red = (0 > 50) ? 0xffff : 0;
2455 color.green = 0;
2456 color.blue = 0;
2457
2458 gdk_gc_set_rgb_fg_color (gc, &color);
2459
2460 /* faster / less flicker alternative:*/
2461 gdk_draw_drawable (
2462 GTK_WIDGET(map)->window,
2463 GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(map)],
2464 priv->pixmap,
2465 0,0,
2466 0,0,
2467 width+10,width+10);
2468
2469 gdk_draw_layout(GTK_WIDGET(map)->window,
2470 gc,
2471 0, 0,
2472 layout);
2473
2474 /* set width and height */
2475 pango_layout_get_pixel_size(layout, &width, &height);
2476
2477 g_free(buffer);
2478 pango_font_description_free (desc);
2479 g_object_unref (layout);
2480 g_object_unref (gc);
2481 }
2482
2483 void
2484 osm_gps_map_draw_gps (OsmGpsMap *map, float latitude, float longitude, float heading)
2485 {
2486 int pixel_x, pixel_y;
2487 OsmGpsMapPrivate *priv;
2488
2489 g_return_if_fail (OSM_IS_GPS_MAP (map));
2490 priv = map->priv;
2491
2492 priv->gps->rlat = deg2rad(latitude);
2493 priv->gps->rlon = deg2rad(longitude);
2494 priv->gps_valid = TRUE;
2495
2496 // pixel_x,y, offsets
2497 pixel_x = lon2pixel(priv->map_zoom, priv->gps->rlon);
2498 pixel_y = lat2pixel(priv->map_zoom, priv->gps->rlat);
2499
2500 //If trip marker add to list of gps points.
2501 if (priv->record_trip_history) {
2502 coord_t *tp = g_new0(coord_t,1);
2503 tp->rlat = priv->gps->rlat;
2504 tp->rlon = priv->gps->rlon;
2505 priv->trip_history = g_slist_append(priv->trip_history, tp);
2506 }
2507
2508 // dont draw anything if we are dragging
2509 if (priv->dragging) {
2510 g_debug("Dragging");
2511 return;
2512 }
2513
2514 //Automatically center the map if the track approaches the edge
2515 if(priv->map_auto_center) {
2516 int x = pixel_x - priv->map_x;
2517 int y = pixel_y - priv->map_y;
2518 int width = GTK_WIDGET(map)->allocation.width;
2519 int height = GTK_WIDGET(map)->allocation.height;
2520 if( x < (width/2 - width/8) || x > (width/2 + width/8) ||
2521 y < (height/2 - height/8) || y > (height/2 + height/8)) {
2522
2523 priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
2524 priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
2525 priv->center_coord_set = FALSE;
2526 }
2527 }
2528
2529 // this redraws the map (including the gps track, and adjusts the
2530 // map center if it was changed
2531 osm_gps_map_map_redraw_idle(map);
2532 }
2533
2534 void
2535 osm_gps_map_clear_gps (OsmGpsMap *map)
2536 {
2537 osm_gps_map_free_trip(map);
2538 osm_gps_map_map_redraw_idle(map);
2539 }
2540
2541 coord_t
2542 osm_gps_map_get_co_ordinates (OsmGpsMap *map, int pixel_x, int pixel_y)
2543 {
2544 coord_t coord;
2545 OsmGpsMapPrivate *priv = map->priv;
2546
2547 coord.rlat = pixel2lat(priv->map_zoom, priv->map_y + pixel_y);
2548 coord.rlon = pixel2lon(priv->map_zoom, priv->map_x + pixel_x);
2549 return coord;
2550 }
2551
2552 GtkWidget *
2553 osm_gps_map_new (void)
2554 {
2555 return g_object_new (OSM_TYPE_GPS_MAP, NULL);
2556 }
2557
2558 void
2559 osm_gps_map_screen_to_geographic (OsmGpsMap *map, gint pixel_x, gint pixel_y,
2560 gfloat *latitude, gfloat *longitude)
2561 {
2562 OsmGpsMapPrivate *priv;
2563
2564 g_return_if_fail (OSM_IS_GPS_MAP (map));
2565 priv = map->priv;
2566
2567 if (latitude)
2568 *latitude = rad2deg(pixel2lat(priv->map_zoom, priv->map_y + pixel_y));
2569 if (longitude)
2570 *longitude = rad2deg(pixel2lon(priv->map_zoom, priv->map_x + pixel_x));
2571 }
2572
2573 void
2574 osm_gps_map_geographic_to_screen (OsmGpsMap *map,
2575 gfloat latitude, gfloat longitude,
2576 gint *pixel_x, gint *pixel_y)
2577 {
2578 OsmGpsMapPrivate *priv;
2579
2580 g_return_if_fail (OSM_IS_GPS_MAP (map));
2581 priv = map->priv;
2582
2583 if (pixel_x)
2584 *pixel_x = lon2pixel(priv->map_zoom, deg2rad(longitude)) - priv->map_x;
2585 if (pixel_y)
2586 *pixel_y = lat2pixel(priv->map_zoom, deg2rad(latitude)) - priv->map_y;
2587 }
2588
2589 void
2590 osm_gps_map_scroll (OsmGpsMap *map, gint dx, gint dy)
2591 {
2592 OsmGpsMapPrivate *priv;
2593
2594 g_return_if_fail (OSM_IS_GPS_MAP (map));
2595 priv = map->priv;
2596
2597 priv->center_coord_set = FALSE;
2598 priv->map_x += dx;
2599 priv->map_y += dy;
2600
2601 osm_gps_map_map_redraw_idle (map);
2602 }
2603
2604 float
2605 osm_gps_map_get_scale(OsmGpsMap *map)
2606 {
2607 OsmGpsMapPrivate *priv;
2608
2609 g_return_val_if_fail (OSM_IS_GPS_MAP (map), OSM_NAN);
2610 priv = map->priv;
2611
2612 return osm_gps_map_get_scale_at_point(priv->map_zoom, priv->center_rlat, priv->center_rlon);
2613 }
2614