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

Parent Directory Parent Directory | Revision Log Revision Log


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