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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 70 - (show annotations)
Thu Aug 20 19:17:23 2009 UTC (14 years, 9 months ago) by harbaum
File MIME type: text/plain
File size: 66262 byte(s)
Seperate OSD implementation
1 /*
2 * Copyright (C) Till Harbaum 2009 <till@harbaum.org>
3 *
4 * osm-gps-map is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * osm-gps-map is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 * See the GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #ifdef USE_CAIRO
21 #include <cairo.h>
22 #endif
23
24 //the osd controls
25 typedef struct {
26 GdkPixmap *backup;
27 cairo_surface_t *overlay;
28 gint backup_x, backup_y;
29 OsmGpsMapOsdGpsCallback cb;
30 gpointer data;
31 } osd_priv_t;
32
33 typedef struct {
34
35
36
37
38 gpointer priv;
39 } osm_gps_map_osd_t;
40
41 /* position and extent of bounding box */
42 #define OSD_X (10)
43 #define OSD_Y (10)
44
45 #define OSD_COLOR 0.5, 0.5, 1
46 #define OSD_COLOR_DISABLED 0.8, 0.8, 0.8
47
48 /* parameters of the direction shape */
49 #ifndef OSM_GPS_MAP_OSD_DIAMETER
50 #define D_RAD (30) // diameter of dpad
51 #else
52 #define D_RAD (OSM_GPS_MAP_OSD_DIAMETER)
53 #endif
54 #define D_TIP (4*D_RAD/5) // distance of arrow tip from dpad center
55 #define D_LEN (D_RAD/4) // length of arrow
56 #define D_WID (D_LEN) // width of arrow
57
58 /* parameters of the "zoom" pad */
59 #define Z_STEP (D_RAD/4) // distance between dpad and zoom
60 #define Z_RAD (D_RAD/2) // radius of "caps" of zoom bar
61
62 /* shadow also depends on control size */
63 #define OSD_SHADOW (D_RAD/6)
64
65 /* total width and height of controls incl. shadow */
66 #define OSD_W (2*D_RAD + OSD_SHADOW)
67 #define OSD_H (2*D_RAD + Z_STEP + 2*Z_RAD + OSD_SHADOW)
68
69 #define OSD_LBL_SHADOW (OSD_SHADOW/2)
70
71 #define Z_TOP (2 * D_RAD + Z_STEP)
72 #define Z_MID (Z_TOP + Z_RAD)
73 #define Z_BOT (Z_MID + Z_RAD)
74 #define Z_LEFT (Z_RAD)
75 #define Z_RIGHT (2 * D_RAD - Z_RAD)
76
77 /* create the cairo shape used for the zoom buttons */
78 static void
79 osm_gps_map_osd_zoom_shape(cairo_t *cr, gint x, gint y) {
80 cairo_move_to (cr, x+Z_LEFT, y+Z_TOP);
81 cairo_line_to (cr, x+Z_RIGHT, y+Z_TOP);
82 cairo_arc (cr, x+Z_RIGHT, y+Z_MID, Z_RAD, -M_PI/2, M_PI/2);
83 cairo_line_to (cr, x+Z_LEFT, y+Z_BOT);
84 cairo_arc (cr, x+Z_LEFT, y+Z_MID, Z_RAD, M_PI/2, -M_PI/2);
85 }
86
87 /* create the cairo shape used for the dpad */
88 static void
89 osm_gps_map_osd_dpad_shape(cairo_t *cr, gint x, gint y) {
90 cairo_arc (cr, x+D_RAD, y+D_RAD, D_RAD, 0, 2 * M_PI);
91 }
92
93 typedef enum {
94 OSD_NONE = 0,
95 OSD_BG,
96 OSD_UP,
97 OSD_DOWN,
98 OSD_LEFT,
99 OSD_RIGHT,
100 OSD_IN,
101 OSD_OUT,
102 OSD_GPS
103 } osd_button_t;
104
105 static gboolean
106 osm_gps_map_in_circle(gint x, gint y, gint cx, gint cy, gint rad)
107 {
108 return( pow(cx - x, 2) + pow(cy - y, 2) < rad * rad);
109 }
110
111 /* check whether x/y is within the dpad */
112 static osd_button_t
113 osm_gps_map_osd_check_dpad(gint x, gint y)
114 {
115 /* within entire dpad circle */
116 if( osm_gps_map_in_circle(x, y, OSD_X + D_RAD, OSD_Y + D_RAD, D_RAD))
117 {
118 /* convert into position relative to dpads centre */
119 x -= (OSD_X + D_RAD);
120 y -= (OSD_Y + D_RAD);
121
122 /* check for dpad center goes here! */
123 if( osm_gps_map_in_circle(x, y, 0, 0, D_RAD/3))
124 return OSD_GPS;
125
126 if( y < 0 && abs(x) < abs(y))
127 return OSD_UP;
128
129 if( y > 0 && abs(x) < abs(y))
130 return OSD_DOWN;
131
132 if( x < 0 && abs(y) < abs(x))
133 return OSD_LEFT;
134
135 if( x > 0 && abs(y) < abs(x))
136 return OSD_RIGHT;
137
138 return OSD_BG;
139 }
140 return OSD_NONE;
141 }
142
143 /* check whether x/y is within the zoom pads */
144 static osd_button_t
145 osm_gps_map_osd_check_zoom(gint x, gint y) {
146 if( x > OSD_X && x < (OSD_X + OSD_W) && y > Z_TOP && y < (OSD_Y+Z_BOT)) {
147
148 /* within circle around (-) label */
149 if( osm_gps_map_in_circle(x, y, OSD_X + Z_LEFT, OSD_Y + Z_MID, Z_RAD))
150 return OSD_OUT;
151
152 /* between center of (-) button and center of entire zoom control area */
153 if(x > OSD_LEFT && x < OSD_X + D_RAD)
154 return OSD_OUT;
155
156 /* within circle around (+) label */
157 if( osm_gps_map_in_circle(x, y, OSD_X + Z_RIGHT, OSD_Y + Z_MID, Z_RAD))
158 return OSD_IN;
159
160 /* between center of (+) button and center of entire zoom control area */
161 if(x < OSD_RIGHT && x > OSD_X + D_RAD)
162 return OSD_IN;
163 }
164
165 return OSD_NONE;
166 }
167
168 static osd_button_t
169 osm_gps_map_osd_check(gint x, gint y) {
170 osd_button_t but = OSD_NONE;
171
172 /* first do a rough test for the OSD area. */
173 /* this is just to avoid an unnecessary detailed test */
174 if(x > OSD_X && x < OSD_X + OSD_W &&
175 y > OSD_Y && y < OSD_Y + OSD_H) {
176 but = osm_gps_map_osd_check_dpad(x, y);
177
178 if(but == OSD_NONE)
179 but = osm_gps_map_osd_check_zoom(x, y);
180 }
181
182 return but;
183 }
184
185 static void
186 osm_gps_map_osd_shape_shadow(cairo_t *cr) {
187 cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
188 cairo_fill (cr);
189 cairo_stroke (cr);
190 }
191
192 static void
193 osm_gps_map_osd_shape(cairo_t *cr) {
194 cairo_set_source_rgb (cr, 1, 1, 1);
195 cairo_fill_preserve (cr);
196 cairo_set_source_rgb (cr, OSD_COLOR);
197 cairo_set_line_width (cr, 1);
198 cairo_stroke (cr);
199 }
200
201 static void
202 osm_gps_map_osd_dpad_labels(cairo_t *cr, gint x, gint y) {
203 /* move reference to dpad center */
204 x += D_RAD;
205 y += D_RAD;
206
207 const static gint offset[][3][2] = {
208 /* left arrow/triangle */
209 { { -D_TIP+D_LEN, -D_WID }, { -D_LEN, D_WID }, { +D_LEN, D_WID } },
210 /* right arrow/triangle */
211 { { +D_TIP-D_LEN, -D_WID }, { +D_LEN, D_WID }, { -D_LEN, D_WID } },
212 /* top arrow/triangle */
213 { { -D_WID, -D_TIP+D_LEN }, { D_WID, -D_LEN }, { D_WID, +D_LEN } },
214 /* bottom arrow/triangle */
215 { { -D_WID, +D_TIP-D_LEN }, { D_WID, +D_LEN }, { D_WID, -D_LEN } }
216 };
217
218 int i;
219 for(i=0;i<4;i++) {
220 cairo_move_to (cr, x + offset[i][0][0], y + offset[i][0][1]);
221 cairo_rel_line_to (cr, offset[i][1][0], offset[i][1][1]);
222 cairo_rel_line_to (cr, offset[i][2][0], offset[i][2][1]);
223 }
224 }
225
226 /* draw the sattelite dish icon in the center of the dpad */
227 #define GPS_V0 (D_RAD/8)
228 #define GPS_V1 (D_RAD/10)
229 #define GPS_V2 (D_RAD/5)
230
231 /* draw a satellite receiver dish */
232 static void
233 osm_gps_map_osd_dpad_gps(cairo_t *cr, gint x, gint y) {
234 /* move reference to dpad center */
235 x += D_RAD;
236 y += D_RAD + GPS_V0;
237
238 cairo_move_to (cr, x-GPS_V0, y+GPS_V0);
239 cairo_rel_line_to (cr, +GPS_V0, -GPS_V0);
240 cairo_rel_line_to (cr, +GPS_V0, +GPS_V0);
241 cairo_close_path (cr);
242
243 cairo_move_to (cr, x+GPS_V1-GPS_V2, y-2*GPS_V2);
244 cairo_curve_to (cr, x-GPS_V2, y, x+GPS_V1, y+GPS_V1, x+GPS_V1+GPS_V2, y);
245 cairo_close_path (cr);
246
247 x += GPS_V1;
248 cairo_move_to (cr, x, y-GPS_V2);
249 cairo_rel_line_to (cr, +GPS_V1, -GPS_V1);
250 }
251
252 #define Z_LEN (2*Z_RAD/3)
253
254 static void
255 osm_gps_map_osd_zoom_labels(cairo_t *cr, gint x, gint y) {
256 cairo_move_to (cr, x + Z_LEFT - Z_LEN, y + Z_MID);
257 cairo_line_to (cr, x + Z_LEFT + Z_LEN, y + Z_MID);
258
259 cairo_move_to (cr, x + Z_RIGHT, y + Z_MID - Z_LEN);
260 cairo_line_to (cr, x + Z_RIGHT, y + Z_MID + Z_LEN);
261 cairo_move_to (cr, x + Z_RIGHT - Z_LEN, y + Z_MID);
262 cairo_line_to (cr, x + Z_RIGHT + Z_LEN, y + Z_MID);
263 }
264
265 static void
266 osm_gps_map_osd_labels(cairo_t *cr, gint width, gboolean enabled) {
267 if(enabled) cairo_set_source_rgb (cr, OSD_COLOR);
268 else cairo_set_source_rgb (cr, OSD_COLOR_DISABLED);
269 cairo_set_line_width (cr, width);
270 cairo_stroke (cr);
271 }
272
273 static void
274 osm_gps_map_osd_labels_shadow(cairo_t *cr, gint width, gboolean enabled) {
275 cairo_set_source_rgba (cr, 0, 0, 0, enabled?0.3:0.15);
276 cairo_set_line_width (cr, width);
277 cairo_stroke (cr);
278 }
279
280 static void
281 osm_gps_map_osd_render(OsmGpsMapPrivate *priv) {
282
283 /* first fill with transparency */
284 cairo_t *cr = cairo_create(priv->osd.overlay);
285 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
286 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
287 cairo_paint(cr);
288
289 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
290
291 /* --------- draw zoom and dpad shape shadow ----------- */
292 gint x = 0, y = 0;
293
294 osm_gps_map_osd_zoom_shape(cr, x + OSD_SHADOW, y + OSD_SHADOW);
295 osm_gps_map_osd_shape_shadow(cr);
296 osm_gps_map_osd_dpad_shape(cr, x + OSD_SHADOW, y + OSD_SHADOW);
297 osm_gps_map_osd_shape_shadow(cr);
298
299 /* --------- draw zoom and dpad shape ----------- */
300
301 osm_gps_map_osd_zoom_shape(cr, x, y);
302 osm_gps_map_osd_shape(cr);
303 osm_gps_map_osd_dpad_shape(cr, x, y);
304 osm_gps_map_osd_shape(cr);
305
306 /* --------- draw zoom and dpad labels --------- */
307
308 osm_gps_map_osd_zoom_labels(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW);
309 osm_gps_map_osd_dpad_labels(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW);
310 osm_gps_map_osd_labels_shadow(cr, Z_RAD/3, TRUE);
311 osm_gps_map_osd_dpad_gps(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW);
312 osm_gps_map_osd_labels_shadow(cr, Z_RAD/6, priv->osd.cb != NULL);
313
314 osm_gps_map_osd_zoom_labels(cr, x, y);
315 osm_gps_map_osd_dpad_labels(cr, x, y);
316 osm_gps_map_osd_labels(cr, Z_RAD/3, TRUE);
317 osm_gps_map_osd_dpad_gps(cr, x, y);
318 osm_gps_map_osd_labels(cr, Z_RAD/6, priv->osd.cb != NULL);
319
320 cairo_destroy(cr);
321 }
322
323 static void
324 osm_gps_map_osd_draw_controls (OsmGpsMap *map, gint xoffset, gint yoffset)
325 {
326 OsmGpsMapPrivate *priv = map->priv;
327
328 /* backup previous contents */
329 if(!priv->osd.backup)
330 priv->osd.backup = gdk_pixmap_new(priv->pixmap, OSD_W+2, OSD_H+2, -1);
331
332 gint x = OSD_X + EXTRA_BORDER + xoffset;
333 gint y = OSD_Y + EXTRA_BORDER + yoffset;
334
335 /* create backup of background */
336 gdk_draw_drawable(priv->osd.backup,
337 GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(GTK_WIDGET(map))],
338 priv->pixmap, x-1, y-1, 0, 0, OSD_W+2, OSD_H+2);
339
340 priv->osd.backup_x = x-1;
341 priv->osd.backup_y = y-1;
342
343
344 #ifdef USE_CAIRO
345 /* OSD itself uses some off-screen rendering, so check if the */
346 /* offscreen buffer is present and create it if not */
347 if(!priv->osd.overlay) {
348 /* create overlay ... */
349 priv->osd.overlay =
350 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W, OSD_H);
351 /* ... and render it */
352 osm_gps_map_osd_render(priv);
353 }
354
355 // now draw this onto the original context
356 cairo_t *cr = gdk_cairo_create(priv->pixmap);
357 cairo_set_source_surface(cr, priv->osd.overlay, x, y);
358 cairo_paint(cr);
359 cairo_destroy(cr);
360
361 #else
362 #warning "OSD control display lacks a non-cairo implementation!"
363 #endif
364 }
365
366 static void
367 osm_gps_map_osd_restore (OsmGpsMap *map)
368 {
369 OsmGpsMapPrivate *priv = map->priv;
370
371 /* restore backup of previous contents */
372 if(priv->osd.backup) {
373 /* create backup of background */
374 gdk_draw_drawable(priv->pixmap,
375 GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(GTK_WIDGET(map))],
376 priv->osd.backup, 0, 0,
377 priv->osd.backup_x, priv->osd.backup_y, OSD_W+2, OSD_H+2);
378 }
379 }
380
381 #endif
382
383 static gboolean
384 osm_gps_map_map_redraw (OsmGpsMap *map)
385 {
386 OsmGpsMapPrivate *priv = map->priv;
387
388 priv->idle_map_redraw = 0;
389
390 /* the motion_notify handler uses priv->pixmap to redraw the area; if we
391 * change it while we are dragging, we will end up showing it in the wrong
392 * place. This could be fixed by carefully recompute the coordinates, but
393 * for now it's easier just to disable redrawing the map while dragging */
394 if (priv->dragging)
395 return FALSE;
396
397 priv->redraw_cycle++;
398
399 /* draw white background to initialise pixmap */
400 gdk_draw_rectangle (
401 priv->pixmap,
402 GTK_WIDGET(map)->style->white_gc,
403 TRUE,
404 0, 0,
405 GTK_WIDGET(map)->allocation.width + EXTRA_BORDER * 2,
406 GTK_WIDGET(map)->allocation.height + EXTRA_BORDER * 2);
407
408 osm_gps_map_fill_tiles_pixel(map);
409
410 osm_gps_map_print_tracks(map);
411 osm_gps_map_draw_gps_point(map);
412 osm_gps_map_print_images(map);
413 #ifdef ENABLE_BALLOON
414 osm_gps_map_draw_balloon_int(map);
415 #endif
416 #ifdef ENABLE_OSD
417 osm_gps_map_osd_draw_controls(map, 0, 0);
418 #endif
419
420 //osm_gps_map_osd_speed(map, 1.5);
421 osm_gps_map_purge_cache(map);
422 gtk_widget_queue_draw (GTK_WIDGET (map));
423
424 return FALSE;
425 }
426
427 static void
428 osm_gps_map_map_redraw_idle (OsmGpsMap *map)
429 {
430 OsmGpsMapPrivate *priv = map->priv;
431
432 if (priv->idle_map_redraw == 0)
433 priv->idle_map_redraw = g_idle_add ((GSourceFunc)osm_gps_map_map_redraw, map);
434 }
435
436 static void
437 osm_gps_map_init (OsmGpsMap *object)
438 {
439 OsmGpsMapPrivate *priv;
440
441 priv = G_TYPE_INSTANCE_GET_PRIVATE (object, OSM_TYPE_GPS_MAP, OsmGpsMapPrivate);
442 object->priv = priv;
443
444 priv->pixmap = NULL;
445
446 priv->trip_history = NULL;
447 priv->gps = g_new0(coord_t, 1);
448 priv->gps_valid = FALSE;
449
450 #ifdef ENABLE_BALLOON
451 priv->balloon.coo = g_new0(coord_t, 1);
452 priv->balloon.valid = FALSE;
453 priv->balloon.cb = NULL;
454 #endif
455
456 #ifdef ENABLE_OSD
457 priv->osd.backup = NULL;
458 priv->osd.overlay = NULL;
459 priv->osd.cb = NULL;
460 #endif
461
462 priv->tracks = NULL;
463 priv->images = NULL;
464
465 priv->drag_counter = 0;
466 priv->drag_mouse_dx = 0;
467 priv->drag_mouse_dy = 0;
468 priv->drag_start_mouse_x = 0;
469 priv->drag_start_mouse_y = 0;
470
471 priv->uri_format = 0;
472 priv->the_google = FALSE;
473
474 priv->map_source = -1;
475
476 #ifndef LIBSOUP22
477 //Change naumber of concurrent connections option?
478 priv->soup_session = soup_session_async_new_with_options(
479 SOUP_SESSION_USER_AGENT,
480 "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11",
481 NULL);
482 #else
483 /* libsoup-2.2 seems not to be able to set the user agent */
484 priv->soup_session = soup_session_async_new();
485 #endif
486
487 //Hash table which maps tile d/l URIs to SoupMessage requests
488 priv->tile_queue = g_hash_table_new (g_str_hash, g_str_equal);
489
490 //Some mapping providers (Google) have varying degrees of tiles at multiple
491 //zoom levels
492 priv->missing_tiles = g_hash_table_new (g_str_hash, g_str_equal);
493
494 /* memory cache for most recently used tiles */
495 priv->tile_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
496 g_free, (GDestroyNotify)cached_tile_free);
497 priv->max_tile_cache_size = 20;
498
499 gtk_widget_add_events (GTK_WIDGET (object),
500 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
501 GDK_POINTER_MOTION_MASK |
502 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
503 GTK_WIDGET_SET_FLAGS (object, GTK_CAN_FOCUS);
504
505 g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, my_log_handler, NULL);
506 }
507
508 #ifndef G_CHECKSUM_MD5
509 /* simple hash algorithm hack if md5 is not present */
510 static char *simple_hash(char *str) {
511 union {
512 char str[4];
513 gulong val;
514 } hash = { .val = 0x55555555 };
515
516 while(*str) {
517 hash.str[(int)str & 3] ^= *str;
518 str++;
519 }
520 return g_strdup_printf("%08lX", hash.val);
521 }
522 #endif
523
524 static GObject *
525 osm_gps_map_constructor (GType gtype, guint n_properties, GObjectConstructParam *properties)
526 {
527 GObject *object;
528 OsmGpsMapPrivate *priv;
529 OsmGpsMap *map;
530 const char *uri;
531
532 //Always chain up to the parent constructor
533 object = G_OBJECT_CLASS(osm_gps_map_parent_class)->constructor(gtype, n_properties, properties);
534 map = OSM_GPS_MAP(object);
535 priv = OSM_GPS_MAP_PRIVATE(object);
536
537 //user can specify a map source ID, or a repo URI as the map source
538 uri = osm_gps_map_source_get_repo_uri(OSM_GPS_MAP_SOURCE_NULL);
539 if ( (priv->map_source == 0) || (strcmp(priv->repo_uri, uri) == 0) ) {
540 g_debug("Using null source");
541 priv->map_source = OSM_GPS_MAP_SOURCE_NULL;
542
543 priv->null_tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 256, 256);
544 gdk_pixbuf_fill(priv->null_tile, 0xcccccc00);
545 }
546 else if (priv->map_source >= 0) {
547 //check if the source given is valid
548 uri = osm_gps_map_source_get_repo_uri(priv->map_source);
549 if (uri) {
550 g_debug("Setting map source from ID");
551 g_free(priv->repo_uri);
552
553 priv->repo_uri = g_strdup(uri);
554 priv->image_format = g_strdup(
555 osm_gps_map_source_get_image_format(priv->map_source));
556 priv->max_zoom = osm_gps_map_source_get_max_zoom(priv->map_source);
557 priv->min_zoom = osm_gps_map_source_get_min_zoom(priv->map_source);
558 }
559 }
560
561 if (!priv->cache_dir_is_full_path) {
562 #ifdef G_CHECKSUM_MD5
563 char *md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, priv->repo_uri, -1);
564 #else
565 char *md5 = simple_hash(priv->repo_uri);
566 #endif
567
568 if (priv->cache_dir) {
569 char *old = priv->cache_dir;
570 //the new cachedir is the given cache dir + the md5 of the repo_uri
571 priv->cache_dir = g_strdup_printf("%s%c%s", old, G_DIR_SEPARATOR, md5);
572 g_debug("Adjusting cache dir %s -> %s", old, priv->cache_dir);
573 g_free(old);
574 } else {
575 //the new cachedir is the current dir + the md5 of the repo_uri
576 priv->cache_dir = g_strdup(md5);
577 }
578
579 g_free(md5);
580 }
581
582 inspect_map_uri(map);
583
584 return object;
585 }
586
587 static void
588 osm_gps_map_dispose (GObject *object)
589 {
590 OsmGpsMap *map = OSM_GPS_MAP(object);
591 OsmGpsMapPrivate *priv = map->priv;
592
593 if (priv->is_disposed)
594 return;
595
596 priv->is_disposed = TRUE;
597
598 soup_session_abort(priv->soup_session);
599 g_object_unref(priv->soup_session);
600
601 g_hash_table_destroy(priv->tile_queue);
602 g_hash_table_destroy(priv->missing_tiles);
603 g_hash_table_destroy(priv->tile_cache);
604
605 osm_gps_map_free_images(map);
606
607 if(priv->pixmap)
608 g_object_unref (priv->pixmap);
609
610 if (priv->null_tile)
611 g_object_unref (priv->null_tile);
612
613 if(priv->gc_map)
614 g_object_unref(priv->gc_map);
615
616 if (priv->idle_map_redraw != 0)
617 g_source_remove (priv->idle_map_redraw);
618
619 g_free(priv->gps);
620
621 #ifdef ENABLE_BALLOON
622 g_free(priv->balloon.coo);
623 #endif
624
625 #ifdef ENABLE_OSD
626 if (priv->osd.backup)
627 g_object_unref(priv->osd.backup);
628
629 if (priv->osd.overlay)
630 cairo_surface_destroy(priv->osd.overlay);
631 #endif
632
633 G_OBJECT_CLASS (osm_gps_map_parent_class)->dispose (object);
634 }
635
636 static void
637 osm_gps_map_finalize (GObject *object)
638 {
639 OsmGpsMap *map = OSM_GPS_MAP(object);
640 OsmGpsMapPrivate *priv = map->priv;
641
642 g_free(priv->cache_dir);
643 g_free(priv->repo_uri);
644 g_free(priv->image_format);
645
646 osm_gps_map_free_trip(map);
647 osm_gps_map_free_tracks(map);
648
649 G_OBJECT_CLASS (osm_gps_map_parent_class)->finalize (object);
650 }
651
652 static void
653 osm_gps_map_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
654 {
655 g_return_if_fail (OSM_IS_GPS_MAP (object));
656 OsmGpsMap *map = OSM_GPS_MAP(object);
657 OsmGpsMapPrivate *priv = map->priv;
658
659 switch (prop_id)
660 {
661 case PROP_AUTO_CENTER:
662 priv->map_auto_center = g_value_get_boolean (value);
663 break;
664 case PROP_RECORD_TRIP_HISTORY:
665 priv->record_trip_history = g_value_get_boolean (value);
666 break;
667 case PROP_SHOW_TRIP_HISTORY:
668 priv->show_trip_history = g_value_get_boolean (value);
669 break;
670 case PROP_AUTO_DOWNLOAD:
671 priv->map_auto_download = g_value_get_boolean (value);
672 break;
673 case PROP_REPO_URI:
674 priv->repo_uri = g_value_dup_string (value);
675 break;
676 case PROP_PROXY_URI:
677 if ( g_value_get_string(value) ) {
678 priv->proxy_uri = g_value_dup_string (value);
679 g_debug("Setting proxy server: %s", priv->proxy_uri);
680
681 #ifndef LIBSOUP22
682 GValue val = {0};
683
684 SoupURI* uri = soup_uri_new(priv->proxy_uri);
685 g_value_init(&val, SOUP_TYPE_URI);
686 g_value_take_boxed(&val, uri);
687
688 g_object_set_property(G_OBJECT(priv->soup_session),SOUP_SESSION_PROXY_URI,&val);
689 #else
690 SoupUri* uri = soup_uri_new(priv->proxy_uri);
691 g_object_set(G_OBJECT(priv->soup_session), SOUP_SESSION_PROXY_URI, uri, NULL);
692 #endif
693 } else
694 priv->proxy_uri = NULL;
695
696 break;
697 case PROP_TILE_CACHE_DIR:
698 if ( g_value_get_string(value) )
699 priv->cache_dir = g_value_dup_string (value);
700 break;
701 case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
702 priv->cache_dir_is_full_path = g_value_get_boolean (value);
703 break;
704 case PROP_ZOOM:
705 priv->map_zoom = g_value_get_int (value);
706 break;
707 case PROP_MAX_ZOOM:
708 priv->max_zoom = g_value_get_int (value);
709 break;
710 case PROP_MIN_ZOOM:
711 priv->min_zoom = g_value_get_int (value);
712 break;
713 case PROP_MAP_X:
714 priv->map_x = g_value_get_int (value);
715 priv->center_coord_set = FALSE;
716 break;
717 case PROP_MAP_Y:
718 priv->map_y = g_value_get_int (value);
719 priv->center_coord_set = FALSE;
720 break;
721 case PROP_GPS_TRACK_WIDTH:
722 priv->ui_gps_track_width = g_value_get_int (value);
723 break;
724 case PROP_GPS_POINT_R1:
725 priv->ui_gps_point_inner_radius = g_value_get_int (value);
726 break;
727 case PROP_GPS_POINT_R2:
728 priv->ui_gps_point_outer_radius = g_value_get_int (value);
729 break;
730 case PROP_MAP_SOURCE:
731 priv->map_source = g_value_get_int (value);
732 break;
733 case PROP_IMAGE_FORMAT:
734 priv->image_format = g_value_dup_string (value);
735 break;
736 default:
737 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
738 break;
739 }
740 }
741
742 static void
743 osm_gps_map_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
744 {
745 g_return_if_fail (OSM_IS_GPS_MAP (object));
746 float lat,lon;
747 OsmGpsMap *map = OSM_GPS_MAP(object);
748 OsmGpsMapPrivate *priv = map->priv;
749
750 switch (prop_id)
751 {
752 case PROP_AUTO_CENTER:
753 g_value_set_boolean(value, priv->map_auto_center);
754 break;
755 case PROP_RECORD_TRIP_HISTORY:
756 g_value_set_boolean(value, priv->record_trip_history);
757 break;
758 case PROP_SHOW_TRIP_HISTORY:
759 g_value_set_boolean(value, priv->show_trip_history);
760 break;
761 case PROP_AUTO_DOWNLOAD:
762 g_value_set_boolean(value, priv->map_auto_download);
763 break;
764 case PROP_REPO_URI:
765 g_value_set_string(value, priv->repo_uri);
766 break;
767 case PROP_PROXY_URI:
768 g_value_set_string(value, priv->proxy_uri);
769 break;
770 case PROP_TILE_CACHE_DIR:
771 g_value_set_string(value, priv->cache_dir);
772 break;
773 case PROP_TILE_CACHE_DIR_IS_FULL_PATH:
774 g_value_set_boolean(value, priv->cache_dir_is_full_path);
775 break;
776 case PROP_ZOOM:
777 g_value_set_int(value, priv->map_zoom);
778 break;
779 case PROP_MAX_ZOOM:
780 g_value_set_int(value, priv->max_zoom);
781 break;
782 case PROP_MIN_ZOOM:
783 g_value_set_int(value, priv->min_zoom);
784 break;
785 case PROP_LATITUDE:
786 lat = pixel2lat(priv->map_zoom,
787 priv->map_y + (GTK_WIDGET(map)->allocation.height / 2));
788 g_value_set_float(value, rad2deg(lat));
789 break;
790 case PROP_LONGITUDE:
791 lon = pixel2lon(priv->map_zoom,
792 priv->map_x + (GTK_WIDGET(map)->allocation.width / 2));
793 g_value_set_float(value, rad2deg(lon));
794 break;
795 case PROP_MAP_X:
796 g_value_set_int(value, priv->map_x);
797 break;
798 case PROP_MAP_Y:
799 g_value_set_int(value, priv->map_y);
800 break;
801 case PROP_TILES_QUEUED:
802 g_value_set_int(value, g_hash_table_size(priv->tile_queue));
803 break;
804 case PROP_GPS_TRACK_WIDTH:
805 g_value_set_int(value, priv->ui_gps_track_width);
806 break;
807 case PROP_GPS_POINT_R1:
808 g_value_set_int(value, priv->ui_gps_point_inner_radius);
809 break;
810 case PROP_GPS_POINT_R2:
811 g_value_set_int(value, priv->ui_gps_point_outer_radius);
812 break;
813 case PROP_MAP_SOURCE:
814 g_value_set_int(value, priv->map_source);
815 break;
816 case PROP_IMAGE_FORMAT:
817 g_value_set_string(value, priv->image_format);
818 break;
819 default:
820 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
821 break;
822 }
823 }
824
825 static gboolean
826 osm_gps_map_scroll_event (GtkWidget *widget, GdkEventScroll *event)
827 {
828 OsmGpsMap *map = OSM_GPS_MAP(widget);
829 OsmGpsMapPrivate *priv = map->priv;
830
831 if (event->direction == GDK_SCROLL_UP)
832 {
833 osm_gps_map_set_zoom(map, priv->map_zoom+1);
834 }
835 else
836 {
837 osm_gps_map_set_zoom(map, priv->map_zoom-1);
838 }
839
840 return FALSE;
841 }
842
843 static gboolean
844 osm_gps_map_button_press (GtkWidget *widget, GdkEventButton *event)
845 {
846 OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
847
848 #ifdef ENABLE_BALLOON
849 /* don't drag if the user clicked within the balloon */
850 if (osm_gps_map_in_balloon(priv,
851 event->x + EXTRA_BORDER,
852 event->y + EXTRA_BORDER))
853 {
854 priv->drag_counter = -1;
855 return FALSE;
856 }
857 #endif
858
859 #ifdef ENABLE_OSD
860 #define SCROLL_STEP 10
861
862 /* pressed inside OSD control? */
863 osd_button_t but = osm_gps_map_osd_check(event->x, event->y);
864 if(but != OSD_NONE)
865 {
866 priv->drag_counter = -1;
867
868 switch(but) {
869 case OSD_GPS:
870 priv->osd.cb(priv->osd.data);
871 break;
872
873 case OSD_UP:
874 priv->map_y -= GTK_WIDGET(widget)->allocation.height/SCROLL_STEP;
875 priv->center_coord_set = FALSE;
876 break;
877
878 case OSD_DOWN:
879 priv->map_y += GTK_WIDGET(widget)->allocation.height/SCROLL_STEP;
880 priv->center_coord_set = FALSE;
881 break;
882
883 case OSD_LEFT:
884 priv->map_x -= GTK_WIDGET(widget)->allocation.width/SCROLL_STEP;
885 priv->center_coord_set = FALSE;
886 break;
887
888 case OSD_RIGHT:
889 priv->map_x += GTK_WIDGET(widget)->allocation.width/SCROLL_STEP;
890 priv->center_coord_set = FALSE;
891 break;
892
893 case OSD_IN:
894 osm_gps_map_set_zoom(OSM_GPS_MAP(widget), priv->map_zoom+1);
895 break;
896
897 case OSD_OUT:
898 osm_gps_map_set_zoom(OSM_GPS_MAP(widget), priv->map_zoom-1);
899 break;
900
901 default:
902 break;
903 }
904
905 osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
906
907 return FALSE;
908 }
909 #endif
910
911 priv->drag_counter = 0;
912 priv->drag_start_mouse_x = (int) event->x;
913 priv->drag_start_mouse_y = (int) event->y;
914 priv->drag_start_map_x = priv->map_x;
915 priv->drag_start_map_y = priv->map_y;
916
917 return FALSE;
918 }
919
920 static gboolean
921 osm_gps_map_button_release (GtkWidget *widget, GdkEventButton *event)
922 {
923 OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
924
925 #ifdef ENABLE_BALLOON
926 /* released inside the balloon? */
927 if (osm_gps_map_in_balloon(priv,
928 event->x + EXTRA_BORDER,
929 event->y + EXTRA_BORDER))
930 {
931 osm_gps_map_handle_balloon_click(OSM_GPS_MAP(widget),
932 event->x - priv->balloon.rect.x + EXTRA_BORDER,
933 event->y - priv->balloon.rect.y + EXTRA_BORDER);
934 }
935 #endif
936
937 if (priv->dragging)
938 {
939 priv->dragging = FALSE;
940
941 priv->map_x = priv->drag_start_map_x;
942 priv->map_y = priv->drag_start_map_y;
943
944 priv->map_x += (priv->drag_start_mouse_x - (int) event->x);
945 priv->map_y += (priv->drag_start_mouse_y - (int) event->y);
946
947 priv->center_coord_set = FALSE;
948
949 osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget));
950 }
951
952 priv->drag_mouse_dx = 0;
953 priv->drag_mouse_dy = 0;
954 priv->drag_counter = -1;
955
956 return FALSE;
957 }
958
959 static gboolean
960 osm_gps_map_expose (GtkWidget *widget, GdkEventExpose *event);
961
962 static gboolean
963 osm_gps_map_motion_notify (GtkWidget *widget, GdkEventMotion *event)
964 {
965 int x, y;
966 GdkModifierType state;
967 OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
968
969 if (event->is_hint)
970 gdk_window_get_pointer (event->window, &x, &y, &state);
971 else
972 {
973 x = event->x;
974 y = event->y;
975 state = event->state;
976 }
977
978 // are we being dragged
979 if (!(state & GDK_BUTTON1_MASK))
980 return FALSE;
981
982 if (priv->drag_counter < 0)
983 return FALSE;
984
985 priv->drag_counter++;
986
987 // we havent dragged more than 6 pixels
988 if (priv->drag_counter < 6)
989 return FALSE;
990
991 #ifdef USE_MAEMO
992 /* reduce update frequency on maemo to keep screen update fluid */
993 static guint32 last_time = 0;
994
995 if(event->time - last_time < 100) return FALSE;
996 last_time = event->time;
997 #endif
998
999 priv->dragging = TRUE;
1000
1001 if (priv->map_auto_center)
1002 g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL);
1003
1004 priv->drag_mouse_dx = x - priv->drag_start_mouse_x;
1005 priv->drag_mouse_dy = y - priv->drag_start_mouse_y;
1006
1007 #ifdef ENABLE_OSD
1008 /* undo OSD */
1009 osm_gps_map_osd_restore (OSM_GPS_MAP(widget));
1010
1011 /* draw new OSD */
1012 osm_gps_map_osd_draw_controls (OSM_GPS_MAP(widget),
1013 -priv->drag_mouse_dx,
1014 -priv->drag_mouse_dy);
1015 #endif
1016
1017 osm_gps_map_expose (widget, NULL);
1018
1019
1020 return FALSE;
1021 }
1022
1023 static gboolean
1024 osm_gps_map_configure (GtkWidget *widget, GdkEventConfigure *event)
1025 {
1026 OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1027
1028 /* create pixmap */
1029 if (priv->pixmap)
1030 g_object_unref (priv->pixmap);
1031
1032 priv->pixmap = gdk_pixmap_new (
1033 widget->window,
1034 widget->allocation.width + EXTRA_BORDER * 2,
1035 widget->allocation.height + EXTRA_BORDER * 2,
1036 -1);
1037
1038 /* and gc, used for clipping (I think......) */
1039 if(priv->gc_map)
1040 g_object_unref(priv->gc_map);
1041
1042 priv->gc_map = gdk_gc_new(priv->pixmap);
1043
1044 osm_gps_map_map_redraw(OSM_GPS_MAP(widget));
1045
1046 return FALSE;
1047 }
1048
1049 static gboolean
1050 osm_gps_map_expose (GtkWidget *widget, GdkEventExpose *event)
1051 {
1052 OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget);
1053
1054 gdk_draw_drawable (
1055 widget->window,
1056 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1057 priv->pixmap,
1058 0,0,
1059 priv->drag_mouse_dx - EXTRA_BORDER,
1060 priv->drag_mouse_dy - EXTRA_BORDER,
1061 -1,-1);
1062
1063 //Paint white outside of the map if dragging. Its less
1064 //ugly than painting the corrupted map
1065 if(priv->drag_mouse_dx>EXTRA_BORDER) {
1066 gdk_draw_rectangle (
1067 widget->window,
1068 widget->style->white_gc,
1069 TRUE,
1070 0, 0,
1071 priv->drag_mouse_dx - EXTRA_BORDER,
1072 widget->allocation.height);
1073 }
1074 else if (-priv->drag_mouse_dx > EXTRA_BORDER)
1075 {
1076 gdk_draw_rectangle (
1077 widget->window,
1078 widget->style->white_gc,
1079 TRUE,
1080 priv->drag_mouse_dx + widget->allocation.width + EXTRA_BORDER, 0,
1081 -priv->drag_mouse_dx - EXTRA_BORDER,
1082 widget->allocation.height);
1083 }
1084
1085 if (priv->drag_mouse_dy>EXTRA_BORDER) {
1086 gdk_draw_rectangle (
1087 widget->window,
1088 widget->style->white_gc,
1089 TRUE,
1090 0, 0,
1091 widget->allocation.width,
1092 priv->drag_mouse_dy - EXTRA_BORDER);
1093 }
1094 else if (-priv->drag_mouse_dy > EXTRA_BORDER)
1095 {
1096 gdk_draw_rectangle (
1097 widget->window,
1098 widget->style->white_gc,
1099 TRUE,
1100 0, priv->drag_mouse_dy + widget->allocation.height + EXTRA_BORDER,
1101 widget->allocation.width,
1102 -priv->drag_mouse_dy - EXTRA_BORDER);
1103 }
1104 #if 0
1105 if(!priv->dragging)
1106 gdk_draw_drawable (
1107 widget->window,
1108 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1109 priv->pixmap,
1110 event->area.x + EXTRA_BORDER,
1111 event->area.y + EXTRA_BORDER,
1112 event->area.x, event->area.y,
1113 event->area.width, event->area.height);
1114 #endif
1115 return FALSE;
1116 }
1117
1118 static void
1119 osm_gps_map_class_init (OsmGpsMapClass *klass)
1120 {
1121 GObjectClass* object_class = G_OBJECT_CLASS (klass);
1122 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1123
1124 g_type_class_add_private (klass, sizeof (OsmGpsMapPrivate));
1125
1126 object_class->dispose = osm_gps_map_dispose;
1127 object_class->finalize = osm_gps_map_finalize;
1128 object_class->constructor = osm_gps_map_constructor;
1129 object_class->set_property = osm_gps_map_set_property;
1130 object_class->get_property = osm_gps_map_get_property;
1131
1132 widget_class->expose_event = osm_gps_map_expose;
1133 widget_class->configure_event = osm_gps_map_configure;
1134 widget_class->button_press_event = osm_gps_map_button_press;
1135 widget_class->button_release_event = osm_gps_map_button_release;
1136 widget_class->motion_notify_event = osm_gps_map_motion_notify;
1137 widget_class->scroll_event = osm_gps_map_scroll_event;
1138
1139 g_object_class_install_property (object_class,
1140 PROP_AUTO_CENTER,
1141 g_param_spec_boolean ("auto-center",
1142 "auto center",
1143 "map auto center",
1144 TRUE,
1145 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1146
1147 g_object_class_install_property (object_class,
1148 PROP_RECORD_TRIP_HISTORY,
1149 g_param_spec_boolean ("record-trip-history",
1150 "record trip history",
1151 "should all gps points be recorded in a trip history",
1152 TRUE,
1153 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1154
1155 g_object_class_install_property (object_class,
1156 PROP_SHOW_TRIP_HISTORY,
1157 g_param_spec_boolean ("show-trip-history",
1158 "show trip history",
1159 "should the recorded trip history be shown on the map",
1160 TRUE,
1161 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1162
1163 g_object_class_install_property (object_class,
1164 PROP_AUTO_DOWNLOAD,
1165 g_param_spec_boolean ("auto-download",
1166 "auto download",
1167 "map auto download",
1168 TRUE,
1169 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1170
1171 g_object_class_install_property (object_class,
1172 PROP_REPO_URI,
1173 g_param_spec_string ("repo-uri",
1174 "repo uri",
1175 "map source tile repository uri",
1176 OSM_REPO_URI,
1177 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1178
1179 g_object_class_install_property (object_class,
1180 PROP_PROXY_URI,
1181 g_param_spec_string ("proxy-uri",
1182 "proxy uri",
1183 "http proxy uri on NULL",
1184 NULL,
1185 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1186
1187 g_object_class_install_property (object_class,
1188 PROP_TILE_CACHE_DIR,
1189 g_param_spec_string ("tile-cache",
1190 "tile cache",
1191 "osm local tile cache dir",
1192 NULL,
1193 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1194
1195 g_object_class_install_property (object_class,
1196 PROP_TILE_CACHE_DIR_IS_FULL_PATH,
1197 g_param_spec_boolean ("tile-cache-is-full-path",
1198 "tile cache is full path",
1199 "if true, the path passed to tile-cache is interpreted as the full cache path",
1200 FALSE,
1201 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1202
1203 g_object_class_install_property (object_class,
1204 PROP_ZOOM,
1205 g_param_spec_int ("zoom",
1206 "zoom",
1207 "zoom level",
1208 MIN_ZOOM, /* minimum property value */
1209 MAX_ZOOM, /* maximum property value */
1210 3,
1211 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1212
1213 g_object_class_install_property (object_class,
1214 PROP_MAX_ZOOM,
1215 g_param_spec_int ("max-zoom",
1216 "max zoom",
1217 "maximum zoom level",
1218 MIN_ZOOM, /* minimum property value */
1219 MAX_ZOOM, /* maximum property value */
1220 OSM_MAX_ZOOM,
1221 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1222
1223 g_object_class_install_property (object_class,
1224 PROP_MIN_ZOOM,
1225 g_param_spec_int ("min-zoom",
1226 "min zoom",
1227 "minimum zoom level",
1228 MIN_ZOOM, /* minimum property value */
1229 MAX_ZOOM, /* maximum property value */
1230 OSM_MIN_ZOOM,
1231 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1232
1233 g_object_class_install_property (object_class,
1234 PROP_LATITUDE,
1235 g_param_spec_float ("latitude",
1236 "latitude",
1237 "latitude in degrees",
1238 -90.0, /* minimum property value */
1239 90.0, /* maximum property value */
1240 0,
1241 G_PARAM_READABLE));
1242
1243 g_object_class_install_property (object_class,
1244 PROP_LONGITUDE,
1245 g_param_spec_float ("longitude",
1246 "longitude",
1247 "longitude in degrees",
1248 -180.0, /* minimum property value */
1249 180.0, /* maximum property value */
1250 0,
1251 G_PARAM_READABLE));
1252
1253 g_object_class_install_property (object_class,
1254 PROP_MAP_X,
1255 g_param_spec_int ("map-x",
1256 "map-x",
1257 "initial map x location",
1258 G_MININT, /* minimum property value */
1259 G_MAXINT, /* maximum property value */
1260 890,
1261 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1262
1263 g_object_class_install_property (object_class,
1264 PROP_MAP_Y,
1265 g_param_spec_int ("map-y",
1266 "map-y",
1267 "initial map y location",
1268 G_MININT, /* minimum property value */
1269 G_MAXINT, /* maximum property value */
1270 515,
1271 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1272
1273 g_object_class_install_property (object_class,
1274 PROP_TILES_QUEUED,
1275 g_param_spec_int ("tiles-queued",
1276 "tiles-queued",
1277 "number of tiles currently waiting to download",
1278 G_MININT, /* minimum property value */
1279 G_MAXINT, /* maximum property value */
1280 0,
1281 G_PARAM_READABLE));
1282
1283 g_object_class_install_property (object_class,
1284 PROP_GPS_TRACK_WIDTH,
1285 g_param_spec_int ("gps-track-width",
1286 "gps-track-width",
1287 "width of the lines drawn for the gps track",
1288 1, /* minimum property value */
1289 G_MAXINT, /* maximum property value */
1290 4,
1291 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1292
1293 g_object_class_install_property (object_class,
1294 PROP_GPS_POINT_R1,
1295 g_param_spec_int ("gps-track-point-radius",
1296 "gps-track-point-radius",
1297 "radius of the gps point inner circle",
1298 0, /* minimum property value */
1299 G_MAXINT, /* maximum property value */
1300 10,
1301 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1302
1303 g_object_class_install_property (object_class,
1304 PROP_GPS_POINT_R2,
1305 g_param_spec_int ("gps-track-highlight-radius",
1306 "gps-track-highlight-radius",
1307 "radius of the gps point highlight circle",
1308 0, /* minimum property value */
1309 G_MAXINT, /* maximum property value */
1310 20,
1311 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
1312
1313 g_object_class_install_property (object_class,
1314 PROP_MAP_SOURCE,
1315 g_param_spec_int ("map-source",
1316 "map source",
1317 "map source ID",
1318 -1, /* minimum property value */
1319 G_MAXINT, /* maximum property value */
1320 -1,
1321 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1322
1323 g_object_class_install_property (object_class,
1324 PROP_IMAGE_FORMAT,
1325 g_param_spec_string ("image-format",
1326 "image format",
1327 "map source tile repository image format (jpg, png)",
1328 OSM_IMAGE_FORMAT,
1329 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1330 }
1331
1332 const char*
1333 osm_gps_map_source_get_friendly_name(OsmGpsMapSource_t source)
1334 {
1335 switch(source)
1336 {
1337 case OSM_GPS_MAP_SOURCE_NULL:
1338 return "None";
1339 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
1340 return "OpenStreetMap";
1341 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
1342 return "OpenStreetMap Renderer";
1343 case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
1344 return "OpenAerialMap";
1345 case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
1346 return "Maps-For-Free";
1347 case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
1348 return "Google Maps";
1349 case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
1350 return "Google Satellite";
1351 case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
1352 return "Google Hybrid";
1353 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
1354 return "Virtual Earth";
1355 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
1356 return "Virtual Earth Satellite";
1357 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
1358 return "Virtual Earth Hybrid";
1359 case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
1360 return "Yahoo Maps";
1361 case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
1362 return "Yahoo Satellite";
1363 case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
1364 return "Yahoo Hybrid";
1365 default:
1366 return NULL;
1367 }
1368 return NULL;
1369 }
1370
1371 //http://www.internettablettalk.com/forums/showthread.php?t=5209
1372 //https://garage.maemo.org/plugins/scmsvn/viewcvs.php/trunk/src/maps.c?root=maemo-mapper&view=markup
1373 //http://www.ponies.me.uk/maps/GoogleTileUtils.java
1374 //http://www.mgmaps.com/cache/MapTileCacher.perl
1375 const char*
1376 osm_gps_map_source_get_repo_uri(OsmGpsMapSource_t source)
1377 {
1378 switch(source)
1379 {
1380 case OSM_GPS_MAP_SOURCE_NULL:
1381 return "none://";
1382 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
1383 return OSM_REPO_URI;
1384 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
1385 return "http://tah.openstreetmap.org/Tiles/tile/#Z/#X/#Y.png";
1386 case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
1387 return "http://tile.openaerialmap.org/tiles/1.0.0/openaerialmap-900913/#Z/#X/#Y.jpg";
1388 case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
1389 return "http://maps-for-free.com/layer/relief/z#Z/row#Y/#Z_#X-#Y.jpg";
1390 case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
1391 return "http://mt#R.google.com/vt/v=w2.97&x=#X&y=#Y&z=#Z";
1392 case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
1393 return "http://khm#R.google.com/kh?n=404&v=3&t=#Q";
1394 case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
1395 return NULL; /* No longer working "http://mt#R.google.com/mt?n=404&v=w2t.99&x=#X&y=#Y&zoom=#S" */
1396 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
1397 return "http://a#R.ortho.tiles.virtualearth.net/tiles/r#W.jpeg?g=50";
1398 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
1399 return "http://a#R.ortho.tiles.virtualearth.net/tiles/a#W.jpeg?g=50";
1400 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
1401 return "http://a#R.ortho.tiles.virtualearth.net/tiles/h#W.jpeg?g=50";
1402 case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
1403 case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
1404 case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
1405 /* TODO: Implement signed Y, aka U
1406 * http://us.maps3.yimg.com/aerial.maps.yimg.com/ximg?v=1.7&t=a&s=256&x=%d&y=%-d&z=%d
1407 * x = tilex,
1408 * y = (1 << (MAX_ZOOM - zoom)) - tiley - 1,
1409 * z = zoom - (MAX_ZOOM - 17));
1410 */
1411 return NULL;
1412 default:
1413 return NULL;
1414 }
1415 return NULL;
1416 }
1417
1418 const char *
1419 osm_gps_map_source_get_image_format(OsmGpsMapSource_t source)
1420 {
1421 switch(source) {
1422 case OSM_GPS_MAP_SOURCE_NULL:
1423 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
1424 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
1425 return "png";
1426 case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
1427 case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
1428 case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
1429 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
1430 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
1431 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
1432 case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
1433 case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
1434 case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
1435 case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
1436 case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
1437 return "jpg";
1438 default:
1439 return "bin";
1440 }
1441 return "bin";
1442 }
1443
1444
1445 int
1446 osm_gps_map_source_get_min_zoom(OsmGpsMapSource_t source)
1447 {
1448 return 1;
1449 }
1450
1451 int
1452 osm_gps_map_source_get_max_zoom(OsmGpsMapSource_t source)
1453 {
1454 switch(source) {
1455 case OSM_GPS_MAP_SOURCE_NULL:
1456 return 18;
1457 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP:
1458 return OSM_MAX_ZOOM;
1459 case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER:
1460 case OSM_GPS_MAP_SOURCE_OPENAERIALMAP:
1461 case OSM_GPS_MAP_SOURCE_GOOGLE_STREET:
1462 case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID:
1463 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET:
1464 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE:
1465 case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID:
1466 case OSM_GPS_MAP_SOURCE_YAHOO_STREET:
1467 case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE:
1468 case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID:
1469 return 17;
1470 case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE:
1471 return 11;
1472 case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE:
1473 return 18;
1474 default:
1475 return 17;
1476 }
1477 return 17;
1478 }
1479
1480 void
1481 osm_gps_map_download_maps (OsmGpsMap *map, coord_t *pt1, coord_t *pt2, int zoom_start, int zoom_end)
1482 {
1483 int i,j,zoom,num_tiles;
1484 OsmGpsMapPrivate *priv = map->priv;
1485
1486 if (pt1 && pt2)
1487 {
1488 gchar *filename;
1489 num_tiles = 0;
1490 zoom_end = CLAMP(zoom_end, priv->min_zoom, priv->max_zoom);
1491 g_debug("Download maps: z:%d->%d",zoom_start, zoom_end);
1492
1493 for(zoom=zoom_start; zoom<=zoom_end; zoom++)
1494 {
1495 int x1,y1,x2,y2;
1496
1497 x1 = (int)floor((float)lon2pixel(zoom, pt1->rlon) / (float)TILESIZE);
1498 y1 = (int)floor((float)lat2pixel(zoom, pt1->rlat) / (float)TILESIZE);
1499
1500 x2 = (int)floor((float)lon2pixel(zoom, pt2->rlon) / (float)TILESIZE);
1501 y2 = (int)floor((float)lat2pixel(zoom, pt2->rlat) / (float)TILESIZE);
1502
1503 // loop x1-x2
1504 for(i=x1; i<=x2; i++)
1505 {
1506 // loop y1 - y2
1507 for(j=y1; j<=y2; j++)
1508 {
1509 // x = i, y = j
1510 filename = g_strdup_printf("%s%c%d%c%d%c%d.%s",
1511 priv->cache_dir, G_DIR_SEPARATOR,
1512 zoom, G_DIR_SEPARATOR,
1513 i, G_DIR_SEPARATOR,
1514 j,
1515 priv->image_format);
1516 if (!g_file_test(filename, G_FILE_TEST_EXISTS))
1517 {
1518 osm_gps_map_download_tile(map, zoom, i, j, FALSE);
1519 num_tiles++;
1520 }
1521 g_free(filename);
1522 }
1523 }
1524 g_debug("DL @Z:%d = %d tiles",zoom,num_tiles);
1525 }
1526 }
1527 }
1528
1529 void
1530 osm_gps_map_get_bbox (OsmGpsMap *map, coord_t *pt1, coord_t *pt2)
1531 {
1532 OsmGpsMapPrivate *priv = map->priv;
1533
1534 if (pt1 && pt2) {
1535 pt1->rlat = pixel2lat(priv->map_zoom, priv->map_y);
1536 pt1->rlon = pixel2lon(priv->map_zoom, priv->map_x);
1537 pt2->rlat = pixel2lat(priv->map_zoom, priv->map_y + GTK_WIDGET(map)->allocation.height);
1538 pt2->rlon = pixel2lon(priv->map_zoom, priv->map_x + GTK_WIDGET(map)->allocation.width);
1539
1540 g_debug("BBOX: %f %f %f %f", pt1->rlat, pt1->rlon, pt2->rlat, pt2->rlon);
1541 }
1542 }
1543
1544 void
1545 osm_gps_map_set_mapcenter (OsmGpsMap *map, float latitude, float longitude, int zoom)
1546 {
1547 osm_gps_map_set_center (map, latitude, longitude);
1548 osm_gps_map_set_zoom (map, zoom);
1549 }
1550
1551 void
1552 osm_gps_map_set_center (OsmGpsMap *map, float latitude, float longitude)
1553 {
1554 int pixel_x, pixel_y;
1555 OsmGpsMapPrivate *priv;
1556
1557 g_return_if_fail (OSM_IS_GPS_MAP (map));
1558 priv = map->priv;
1559
1560 priv->center_rlat = deg2rad(latitude);
1561 priv->center_rlon = deg2rad(longitude);
1562 priv->center_coord_set = TRUE;
1563
1564 // pixel_x,y, offsets
1565 pixel_x = lon2pixel(priv->map_zoom, priv->center_rlon);
1566 pixel_y = lat2pixel(priv->map_zoom, priv->center_rlat);
1567
1568 priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
1569 priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
1570
1571 osm_gps_map_map_redraw_idle(map);
1572 }
1573
1574 int
1575 osm_gps_map_set_zoom (OsmGpsMap *map, int zoom)
1576 {
1577 int zoom_old;
1578 double factor = 0.0;
1579 int width_center, height_center;
1580 OsmGpsMapPrivate *priv;
1581
1582 g_return_val_if_fail (OSM_IS_GPS_MAP (map), 0);
1583 priv = map->priv;
1584
1585 if (zoom != priv->map_zoom)
1586 {
1587 width_center = GTK_WIDGET(map)->allocation.width / 2;
1588 height_center = GTK_WIDGET(map)->allocation.height / 2;
1589
1590 zoom_old = priv->map_zoom;
1591 //constrain zoom min_zoom -> max_zoom
1592 priv->map_zoom = CLAMP(zoom, priv->min_zoom, priv->max_zoom);
1593
1594 if (priv->center_coord_set)
1595 {
1596 priv->map_x = lon2pixel(priv->map_zoom, priv->center_rlon) - width_center;
1597 priv->map_y = lat2pixel(priv->map_zoom, priv->center_rlat) - height_center;
1598 }
1599 else
1600 {
1601 factor = exp(priv->map_zoom * M_LN2)/exp(zoom_old * M_LN2);
1602 priv->map_x = ((priv->map_x + width_center) * factor) - width_center;
1603 priv->map_y = ((priv->map_y + height_center) * factor) - height_center;
1604 }
1605
1606 g_debug("Zoom changed from %d to %d factor:%f x:%d",
1607 zoom_old, priv->map_zoom, factor, priv->map_x);
1608
1609 osm_gps_map_map_redraw_idle(map);
1610 }
1611 return priv->map_zoom;
1612 }
1613
1614 void
1615 osm_gps_map_add_track (OsmGpsMap *map, GSList *track)
1616 {
1617 OsmGpsMapPrivate *priv;
1618
1619 g_return_if_fail (OSM_IS_GPS_MAP (map));
1620 priv = map->priv;
1621
1622 if (track) {
1623 priv->tracks = g_slist_append(priv->tracks, track);
1624 osm_gps_map_map_redraw_idle(map);
1625 }
1626 }
1627
1628 void
1629 osm_gps_map_clear_tracks (OsmGpsMap *map)
1630 {
1631 g_return_if_fail (OSM_IS_GPS_MAP (map));
1632
1633 osm_gps_map_free_tracks(map);
1634 osm_gps_map_map_redraw_idle(map);
1635 }
1636
1637 void
1638 osm_gps_map_add_image (OsmGpsMap *map, float latitude, float longitude, GdkPixbuf *image)
1639 {
1640 g_return_if_fail (OSM_IS_GPS_MAP (map));
1641
1642 if (image) {
1643 OsmGpsMapPrivate *priv = map->priv;
1644 image_t *im;
1645
1646 //cache w/h for speed, and add image to list
1647 im = g_new0(image_t,1);
1648 im->w = gdk_pixbuf_get_width(image);
1649 im->h = gdk_pixbuf_get_height(image);
1650 im->pt.rlat = deg2rad(latitude);
1651 im->pt.rlon = deg2rad(longitude);
1652
1653 g_object_ref(image);
1654 im->image = image;
1655
1656 priv->images = g_slist_append(priv->images, im);
1657
1658 osm_gps_map_map_redraw_idle(map);
1659 }
1660 }
1661
1662 gboolean
1663 osm_gps_map_remove_image (OsmGpsMap *map, GdkPixbuf *image)
1664 {
1665 OsmGpsMapPrivate *priv = map->priv;
1666 if (priv->images) {
1667 GSList *list;
1668 for(list = priv->images; list != NULL; list = list->next)
1669 {
1670 image_t *im = list->data;
1671 if (im->image == image)
1672 {
1673 priv->images = g_slist_remove_link(priv->images, list);
1674 g_object_unref(im->image);
1675 g_free(im);
1676 osm_gps_map_map_redraw_idle(map);
1677 return TRUE;
1678 }
1679 }
1680 }
1681 return FALSE;
1682 }
1683
1684 void
1685 osm_gps_map_clear_images (OsmGpsMap *map)
1686 {
1687 g_return_if_fail (OSM_IS_GPS_MAP (map));
1688
1689 osm_gps_map_free_images(map);
1690 osm_gps_map_map_redraw_idle(map);
1691 }
1692
1693 void
1694 osm_gps_map_osd_speed (OsmGpsMap *map, float speed)
1695 {
1696 OsmGpsMapPrivate *priv;
1697
1698 PangoContext *context = NULL;
1699 PangoLayout *layout = NULL;
1700 PangoFontDescription *desc = NULL;
1701
1702 GdkColor color;
1703 GdkGC *gc;
1704
1705 gchar *buffer;
1706 //static int x = 10, y = 10;
1707 static int width = 0, height = 0;
1708
1709 g_return_if_fail (OSM_IS_GPS_MAP (map));
1710 priv = map->priv;
1711
1712 buffer = g_strdup_printf("%.0f", speed);
1713
1714 /* pango initialisation */
1715 context = gtk_widget_get_pango_context (GTK_WIDGET(map));
1716 layout = pango_layout_new (context);
1717 desc = pango_font_description_new();
1718
1719 pango_font_description_set_size (desc, 40 * PANGO_SCALE);
1720 pango_layout_set_font_description (layout, desc);
1721 pango_layout_set_text (layout, buffer, strlen(buffer));
1722
1723 gc = gdk_gc_new (GTK_WIDGET(map)->window);
1724
1725 color.red = (0 > 50) ? 0xffff : 0;
1726 color.green = 0;
1727 color.blue = 0;
1728
1729 gdk_gc_set_rgb_fg_color (gc, &color);
1730
1731 /* faster / less flicker alternative:*/
1732 gdk_draw_drawable (
1733 GTK_WIDGET(map)->window,
1734 GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(map)],
1735 priv->pixmap,
1736 0,0,
1737 0,0,
1738 width+10,width+10);
1739
1740 gdk_draw_layout(GTK_WIDGET(map)->window,
1741 gc,
1742 0, 0,
1743 layout);
1744
1745 /* set width and height */
1746 pango_layout_get_pixel_size(layout, &width, &height);
1747
1748 g_free(buffer);
1749 pango_font_description_free (desc);
1750 g_object_unref (layout);
1751 g_object_unref (gc);
1752 }
1753
1754 void
1755 osm_gps_map_draw_gps (OsmGpsMap *map, float latitude, float longitude, float heading)
1756 {
1757 int pixel_x, pixel_y;
1758 OsmGpsMapPrivate *priv;
1759
1760 g_return_if_fail (OSM_IS_GPS_MAP (map));
1761 priv = map->priv;
1762
1763 priv->gps->rlat = deg2rad(latitude);
1764 priv->gps->rlon = deg2rad(longitude);
1765 priv->gps_valid = TRUE;
1766
1767 // pixel_x,y, offsets
1768 pixel_x = lon2pixel(priv->map_zoom, priv->gps->rlon);
1769 pixel_y = lat2pixel(priv->map_zoom, priv->gps->rlat);
1770
1771 //If trip marker add to list of gps points.
1772 if (priv->record_trip_history) {
1773 coord_t *tp = g_new0(coord_t,1);
1774 tp->rlat = priv->gps->rlat;
1775 tp->rlon = priv->gps->rlon;
1776 priv->trip_history = g_slist_append(priv->trip_history, tp);
1777 }
1778
1779 // dont draw anything if we are dragging
1780 if (priv->dragging) {
1781 g_debug("Dragging");
1782 return;
1783 }
1784
1785 //Automatically center the map if the track approaches the edge
1786 if(priv->map_auto_center) {
1787 int x = pixel_x - priv->map_x;
1788 int y = pixel_y - priv->map_y;
1789 int width = GTK_WIDGET(map)->allocation.width;
1790 int height = GTK_WIDGET(map)->allocation.height;
1791 if( x < (width/2 - width/8) || x > (width/2 + width/8) ||
1792 y < (height/2 - height/8) || y > (height/2 + height/8)) {
1793
1794 priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2;
1795 priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2;
1796 priv->center_coord_set = FALSE;
1797 }
1798 }
1799
1800 // this redraws the map (including the gps track, and adjusts the
1801 // map center if it was changed
1802 osm_gps_map_map_redraw_idle(map);
1803 }
1804
1805 void
1806 osm_gps_map_clear_gps (OsmGpsMap *map)
1807 {
1808 osm_gps_map_free_trip(map);
1809 osm_gps_map_map_redraw_idle(map);
1810 }
1811
1812 coord_t
1813 osm_gps_map_get_co_ordinates (OsmGpsMap *map, int pixel_x, int pixel_y)
1814 {
1815 coord_t coord;
1816 OsmGpsMapPrivate *priv = map->priv;
1817
1818 coord.rlat = pixel2lat(priv->map_zoom, priv->map_y + pixel_y);
1819 coord.rlon = pixel2lon(priv->map_zoom, priv->map_x + pixel_x);
1820 return coord;
1821 }
1822
1823 GtkWidget *
1824 osm_gps_map_new (void)
1825 {
1826 return g_object_new (OSM_TYPE_GPS_MAP, NULL);
1827 }
1828
1829 void
1830 osm_gps_map_screen_to_geographic (OsmGpsMap *map, gint pixel_x, gint pixel_y,
1831 gfloat *latitude, gfloat *longitude)
1832 {
1833 OsmGpsMapPrivate *priv;
1834
1835 g_return_if_fail (OSM_IS_GPS_MAP (map));
1836 priv = map->priv;
1837
1838 if (latitude)
1839 *latitude = rad2deg(pixel2lat(priv->map_zoom, priv->map_y + pixel_y));
1840 if (longitude)
1841 *longitude = rad2deg(pixel2lon(priv->map_zoom, priv->map_x + pixel_x));
1842 }
1843
1844 void
1845 osm_gps_map_geographic_to_screen (OsmGpsMap *map,
1846 gfloat latitude, gfloat longitude,
1847 gint *pixel_x, gint *pixel_y)
1848 {
1849 OsmGpsMapPrivate *priv;
1850
1851 g_return_if_fail (OSM_IS_GPS_MAP (map));
1852 priv = map->priv;
1853
1854 if (pixel_x)
1855 *pixel_x = lon2pixel(priv->map_zoom, deg2rad(longitude)) - priv->map_x;
1856 if (pixel_y)
1857 *pixel_y = lat2pixel(priv->map_zoom, deg2rad(latitude)) - priv->map_y;
1858 }
1859
1860 void
1861 osm_gps_map_scroll (OsmGpsMap *map, gint dx, gint dy)
1862 {
1863 OsmGpsMapPrivate *priv;
1864
1865 g_return_if_fail (OSM_IS_GPS_MAP (map));
1866 priv = map->priv;
1867
1868 priv->center_coord_set = FALSE;
1869 priv->map_x += dx;
1870 priv->map_y += dy;
1871
1872 osm_gps_map_map_redraw_idle (map);
1873 }
1874
1875 float
1876 osm_gps_map_get_scale(OsmGpsMap *map)
1877 {
1878 OsmGpsMapPrivate *priv;
1879
1880 g_return_val_if_fail (OSM_IS_GPS_MAP (map), OSM_NAN);
1881 priv = map->priv;
1882
1883 return osm_gps_map_get_scale_at_point(priv->map_zoom, priv->center_rlat, priv->center_rlon);
1884 }
1885
1886 #ifdef ENABLE_BALLOON
1887 void
1888 osm_gps_map_draw_balloon (OsmGpsMap *map, float latitude, float longitude,
1889 OsmGpsMapBalloonCallback cb, gpointer data)
1890 {
1891 OsmGpsMapPrivate *priv;
1892
1893 /* remove and previously installed balloon */
1894 osm_gps_map_clear_balloon (map);
1895
1896 g_return_if_fail (OSM_IS_GPS_MAP (map));
1897 priv = map->priv;
1898
1899 priv->balloon.coo->rlat = deg2rad(latitude);
1900 priv->balloon.coo->rlon = deg2rad(longitude);
1901 priv->balloon.valid = TRUE;
1902
1903 priv->balloon.cb = cb;
1904 priv->balloon.data = data;
1905
1906 // this redraws the map
1907 osm_gps_map_map_redraw_idle(map);
1908 }
1909
1910 void
1911 osm_gps_map_clear_balloon (OsmGpsMap *map)
1912 {
1913 OsmGpsMapPrivate *priv;
1914
1915 g_return_if_fail (OSM_IS_GPS_MAP (map));
1916 priv = map->priv;
1917
1918 priv->balloon.valid = FALSE;
1919
1920 osm_gps_map_map_redraw_idle(map);
1921 }
1922 #endif
1923
1924 #ifdef ENABLE_OSD
1925 void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdGpsCallback cb, gpointer data) {
1926 OsmGpsMapPrivate *priv;
1927
1928 g_return_if_fail (OSM_IS_GPS_MAP (map));
1929 priv = map->priv;
1930
1931 priv->osd.cb = cb;
1932 priv->osd.data = data;
1933
1934 /* this may have changed the state of the gps button */
1935 /* we thus re-render the overlay */
1936 osm_gps_map_osd_render(priv);
1937
1938 osm_gps_map_map_redraw_idle(map);
1939 }
1940 #endif