14 #include <glib/gstdio.h>
17 #include <libgnomevfs/gnome-vfs.h>
18 #include <gconf/gconf-client.h>
19 #include <libxml/parser.h>
20 #include <curl/multi.h>
25 #include "hildon-mapper.h"
36 #include "mapper-types.h"
37 #include "ui-common.h"
40 Point _min_center = { -1, -1 };
41 Point _max_center = { -1, -1 };
42 Point _focus = { -1, -1 };
44 /** The "base tile" is the upper-left tile in the pixmap. */
45 guint _base_tilex = -5;
46 guint _base_tiley = -5;
48 guint _zoom = 3; /* zoom level, from 0 to MAX_ZOOM. */
49 Point _center = { -1, -1 }; /* current center location, X. */
51 static guint press[2] = { 0, 0 };
52 static guint release[2] = { 0, 0 };
53 static guint before[2] = { 0, 0 };
56 static gint _zoom_timeout_sid=0;
57 static gint _map_mode=0;
59 guint _num_downloads=0;
60 guint _curr_download=0;
62 static osm_location map_loc = {NULL, NULL, NULL};
63 static GHashTable *map_tile_hash = NULL;
65 typedef struct _map_tile_rdata map_tile_rdata;
66 struct _map_tile_rdata {
67 guint tilex, tiley, zoom;
71 void map_render_paths();
72 void map_force_redraw();
73 gboolean curl_download_timeout();
74 gchar *map_construct_url(guint tilex, guint tiley, guint zoom);
75 gboolean map_download_idle_refresh(ProgressUpdateInfo * pui);
76 static void map_update_location(gint x, gint y);
78 gboolean get_next_pui(gpointer key, gpointer value, ProgressUpdateInfo ** data)
84 gboolean curl_download_timeout()
86 static guint destroy_counter = 50;
87 gint num_transfers = 0, num_msgs = 0;
88 gint deletes_left = 50; /* only do 50 deletes at a time. */
90 vprintf("%s()\n", __PRETTY_FUNCTION__);
92 if (_curl_multi && CURLM_CALL_MULTI_PERFORM == curl_multi_perform(_curl_multi, &num_transfers))
93 return TRUE; /* Give UI a chance first. */
95 while (_curl_multi && (msg = curl_multi_info_read(_curl_multi, &num_msgs))) {
96 if (msg->msg == CURLMSG_DONE) {
97 if (msg->easy_handle == _autoroute_data.curl_easy) {
98 /* This is the autoroute download. */
99 /* Now, parse the autoroute and update the display. */
100 if (_autoroute_data.enabled
101 && parse_gpx(&_route,
102 _autoroute_data.rdl_data.bytes,
103 _autoroute_data.rdl_data.
105 /* Find the nearest route point, if we're connected. */
106 route_find_nearest_point();
109 cancel_autoroute(TRUE); /* We're done. Clean up. */
111 ProgressUpdateInfo *pui = g_hash_table_lookup(_pui_by_easy, msg->easy_handle);
112 g_queue_push_head(_curl_easy_queue, msg->easy_handle);
113 g_hash_table_remove(_pui_by_easy, msg->easy_handle);
115 if (msg->data.result != CURLE_OK)
116 g_unlink(pui->dest_str); /* Delete so we try again. */
117 curl_multi_remove_handle(_curl_multi,
119 g_idle_add_full(G_PRIORITY_HIGH_IDLE,
121 map_download_idle_refresh, pui,
127 /* Up to 1 transfer per tile. */
128 while (num_transfers < (BUF_WIDTH_TILES * BUF_HEIGHT_TILES)
129 && g_tree_nnodes(_pui_tree)) {
130 ProgressUpdateInfo *pui;
131 g_tree_foreach(_pui_tree, (GTraverseFunc) get_next_pui, &pui);
134 /* This is a download. */
136 g_tree_steal(_pui_tree, pui);
137 g_tree_insert(_downloading_tree, pui, pui);
139 pui->src_str = map_construct_url(pui->tilex, pui->tiley, pui->zoom);
140 pui->dest_str = g_strdup_printf("%s/%u/%u/%u.jpg",
141 pui->repo->cache_dir, pui->zoom,
142 pui->tilex, pui->tiley);
145 /* Failed to generate URL. */
146 g_idle_add_full(G_PRIORITY_HIGH_IDLE,
147 (GSourceFunc)map_download_idle_refresh, pui,
152 /* Check to see if we need to overwrite. */
153 if (pui->retries > 0) {
154 /* We're not updating - check if file already exists. */
155 if (g_file_test(pui->dest_str, G_FILE_TEST_EXISTS)) {
156 g_idle_add_full(G_PRIORITY_HIGH_IDLE,
158 map_download_idle_refresh,
164 /* Attempt to open the file for writing. */
165 if (!(f = g_fopen(pui->dest_str, "w"))
166 && errno == ENOENT) {
167 /* Directory doesn't exist yet - create it, then we'll retry */
168 gchar buffer[BUFFER_SIZE];
169 snprintf(buffer, sizeof(buffer), "%s/%u/%u",
170 pui->repo->cache_dir, pui->zoom,
172 g_mkdir_with_parents(buffer, 0775);
173 f = g_fopen(pui->dest_str, "w");
179 curl_easy = g_queue_pop_tail(_curl_easy_queue);
181 /* Need a new curl_easy. */
182 MACRO_CURL_EASY_INIT(curl_easy);
184 curl_easy_setopt(curl_easy, CURLOPT_URL, pui->src_str);
185 curl_easy_setopt(curl_easy, CURLOPT_WRITEDATA, f);
186 g_hash_table_insert(_pui_by_easy, curl_easy, pui);
188 /* Initialize CURL. */
189 _curl_multi = curl_multi_init();
190 /*curl_multi_setopt(_curl_multi, CURLMOPT_PIPELINING, 1); */
192 curl_multi_add_handle(_curl_multi, curl_easy);
195 /* Unable to download file. */
196 gchar buffer[BUFFER_SIZE];
197 snprintf(buffer, sizeof(buffer), "%s:\n%s",
198 _("Failed to open file for writing"),
200 MACRO_BANNER_SHOW_INFO(_window, buffer);
201 g_idle_add_full(G_PRIORITY_HIGH_IDLE,
203 map_download_idle_refresh, pui,
207 } else if (--deletes_left) {
208 /* This is a delete. */
209 gchar buffer[BUFFER_SIZE];
210 g_tree_steal(_pui_tree, pui);
211 g_tree_insert(_downloading_tree, pui, pui);
213 snprintf(buffer, sizeof(buffer), "%s/%u/%u/%u.jpg",
214 pui->repo->cache_dir, pui->zoom, pui->tilex,
217 g_idle_add_full(G_PRIORITY_HIGH_IDLE,
218 (GSourceFunc) map_download_idle_refresh,
224 if (!(num_transfers || g_tree_nnodes(_pui_tree))) {
225 /* Destroy curl after 50 counts (5 seconds). */
226 if (--destroy_counter) {
229 while ((curr = g_queue_pop_tail(_curl_easy_queue)))
230 curl_easy_cleanup(curr);
232 curl_multi_cleanup(_curl_multi);
236 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
240 destroy_counter = 50;
242 vprintf("%s(): return TRUE (%d, %d)\n", __PRETTY_FUNCTION__,
243 num_transfers, g_tree_nnodes(_pui_tree));
248 * Draw the current mark (representing the current GPS location) onto
249 * _map_widget. This method does not queue the draw area.
253 printf("%s()\n", __PRETTY_FUNCTION__);
255 gdk_draw_arc(_map_widget->window, _conn_state == RCVR_FIXED ? _gc[COLORABLE_MARK] : _gc[COLORABLE_MARK_OLD], FALSE, /* not filled. */
256 _mark_x1 - _draw_width, _mark_y1 - _draw_width,
257 2 * _draw_width, 2 * _draw_width, 0, 360 * 64);
258 gdk_draw_line(_map_widget->window,
259 _conn_state == RCVR_FIXED ? (_show_velvec ? _gc[COLORABLE_MARK_VELOCITY] : _gc[COLORABLE_MARK]) : _gc[COLORABLE_MARK_OLD],
260 _mark_x1, _mark_y1, _mark_x2, _mark_y2);
262 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
266 * "Set" the mark, which translates the current GPS position into on-screen
267 * units in preparation for drawing the mark with map_draw_mark().
271 printf("%s()\n", __PRETTY_FUNCTION__);
273 _mark_x1 = unit2x(_pos.unitx);
274 _mark_y1 = unit2y(_pos.unity);
275 _mark_x2 = _mark_x1 + (_show_velvec ? _vel_offsetx : 0);
276 _mark_y2 = _mark_y1 + (_show_velvec ? _vel_offsety : 0);
277 _mark_minx = MIN(_mark_x1, _mark_x2) - (2 * _draw_width);
278 _mark_miny = MIN(_mark_y1, _mark_y2) - (2 * _draw_width);
279 _mark_width = abs(_mark_x1 - _mark_x2) + (4 * _draw_width);
280 _mark_height = abs(_mark_y1 - _mark_y2) + (4 * _draw_width);
282 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
286 * Do an in-place scaling of a pixbuf's pixels at the given ratio from the
287 * given source location. It would have been nice if gdk_pixbuf provided
288 * this method, but I guess it's not general-purpose enough.
291 map_pixbuf_scale_inplace(GdkPixbuf * pixbuf, guint ratio_p2,
292 guint src_x, guint src_y)
294 guint dest_x = 0, dest_y = 0, dest_dim = TILE_SIZE_PIXELS;
295 guint rowstride = gdk_pixbuf_get_rowstride(pixbuf);
296 guint n_channels = gdk_pixbuf_get_n_channels(pixbuf);
297 guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
299 vprintf("%s(%d, %d, %d)\n", __PRETTY_FUNCTION__, ratio_p2, src_x, src_y);
301 /* Sweep through the entire dest area, copying as necessary, but
302 * DO NOT OVERWRITE THE SOURCE AREA. We'll copy it afterward. */
304 guint src_dim = dest_dim >> ratio_p2;
305 guint src_endx = src_x - dest_x + src_dim;
308 for (y = dest_dim - 1; y >= 0; y--) {
309 guint src_offset_y, dest_offset_y;
311 src_offset_y = (src_y + (y >> ratio_p2)) * rowstride;
312 dest_offset_y = (dest_y + y) * rowstride;
315 if ((unsigned)(dest_y + y - src_y) < src_dim
316 && (unsigned)(dest_x + x - src_x) < src_dim)
319 for (; x >= 0; x--) {
320 guint src_offset, dest_offset, i;
322 src_offset = src_offset_y + (src_x + (x >> ratio_p2)) * n_channels;
323 dest_offset = dest_offset_y + (dest_x + x) * n_channels;
325 pixels[dest_offset] = pixels[src_offset];
326 for (i = n_channels - 1; i; i--)
327 pixels[dest_offset + i] = pixels[src_offset + i];
329 if ((unsigned)(dest_y + y - src_y) < src_dim && x == src_endx)
333 /* Reuse src_dim and src_endx to store new src_x and src_y. */
334 src_dim = src_x + ((src_x - dest_x) >> ratio_p2);
335 src_endx = src_y + ((src_y - dest_y) >> ratio_p2);
341 while ((dest_dim >>= ratio_p2) > 1);
343 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
347 * Trim pixbufs that are bigger than tiles. (Those pixbufs result, when
348 * captions should be cut off.)
350 GdkPixbuf *pixbuf_trim(GdkPixbuf * pixbuf)
352 vprintf("%s()\n", __PRETTY_FUNCTION__);
353 GdkPixbuf *mpixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(pixbuf),
354 8, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
356 gdk_pixbuf_copy_area(pixbuf,
357 (gdk_pixbuf_get_width(pixbuf) - TILE_SIZE_PIXELS) / 2,
358 (gdk_pixbuf_get_height(pixbuf) - TILE_SIZE_PIXELS) / 2,
360 TILE_SIZE_PIXELS, mpixbuf, 0, 0);
362 g_object_unref(pixbuf);
363 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
368 * Given a wms uri pattern, compute the coordinate transformation and
370 * 'proj' is used for the conversion
372 gchar *map_convert_wms_to_wms(gint tilex, gint tiley, gint zoomlevel,
376 gchar cmd[BUFFER_SIZE], srs[BUFFER_SIZE];
379 gfloat lon1, lat1, lon2, lat2;
381 gchar *widthstr = strcasestr(uri, "WIDTH=");
382 gchar *heightstr = strcasestr(uri, "HEIGHT=");
383 gchar *srsstr = strcasestr(uri, "SRS=EPSG");
384 gchar *srsstre = strchr(srsstr, '&');
385 vprintf("%s()\n", __PRETTY_FUNCTION__);
387 /* missing: test if found */
389 strncpy(srs + 4, srsstr + 8, 256);
390 /* missing: test srsstre-srsstr < 526 */
391 srs[srsstre - srsstr - 4] = 0;
392 /* convert to lower, as WMC is EPSG and cs2cs is epsg */
394 gint dwidth = widthstr ? atoi(widthstr + 6) - TILE_SIZE_PIXELS : 0;
395 gint dheight = heightstr ? atoi(heightstr + 7) - TILE_SIZE_PIXELS : 0;
397 unit2latlon(tile2zunit(tilex, zoomlevel)
398 - pixel2zunit(dwidth / 2, zoomlevel),
399 tile2zunit(tiley + 1, zoomlevel)
400 + pixel2zunit((dheight + 1) / 2, zoomlevel), lat1, lon1);
402 unit2latlon(tile2zunit(tilex + 1, zoomlevel)
403 + pixel2zunit((dwidth + 1) / 2, zoomlevel),
404 tile2zunit(tiley, zoomlevel)
405 - pixel2zunit(dheight / 2, zoomlevel), lat2, lon2);
407 setlocale(LC_NUMERIC, "C");
409 snprintf(cmd, sizeof(cmd),
410 "(echo \"%.6f %.6f\"; echo \"%.6f %.6f\") | "
411 "/usr/bin/cs2cs +proj=longlat +datum=WGS84 +to +init=%s -f %%.6f "
412 " > /tmp/tmpcs2cs ", lon1, lat1, lon2, lat2, srs);
413 vprintf("Running command: %s\n", cmd);
414 system_retcode = system(cmd);
417 g_printerr("cs2cs returned error code %d\n",
418 WEXITSTATUS(system_retcode));
419 else if (!(in = g_fopen("/tmp/tmpcs2cs", "r")))
420 g_printerr("Cannot open results of conversion\n");
422 fscanf(in, "%f %f %s %f %f", &lon1, &lat1, cmd, &lon2,
424 g_printerr("Wrong conversion\n");
428 ret = g_strdup_printf(uri, lon1, lat1, lon2, lat2);
431 setlocale(LC_NUMERIC, "");
433 vprintf("%s(): return %s\n", __PRETTY_FUNCTION__, ret);
438 * Given the xyz coordinates of our map coordinate system, write the qrst
439 * quadtree coordinates to buffer.
442 map_convert_coords_to_quadtree_string(gint x, gint y, gint zoomlevel,
443 gchar * buffer, const gchar initial,
444 const gchar * const quadrant)
448 vprintf("%s()\n", __PRETTY_FUNCTION__);
453 for (n = 16 - zoomlevel; n >= 0; n--) {
454 gint xbit = (x >> n) & 1;
455 gint ybit = (y >> n) & 1;
456 *ptr++ = quadrant[xbit + 2 * ybit];
459 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
463 * Construct the URL that we should fetch, based on the current URI format.
464 * This method works differently depending on if a "%s" string is present in
465 * the URI format, since that would indicate a quadtree-based map coordinate
468 gchar *map_construct_url(guint tilex, guint tiley, guint zoom)
470 vprintf("%s()\n", __PRETTY_FUNCTION__);
471 switch (_curr_repo->type) {
473 return g_strdup_printf(_curr_repo->url, tilex, tiley, zoom);
475 case REPOTYPE_XYZ_INV:
476 return g_strdup_printf(_curr_repo->url, 17 - zoom, tilex,
479 case REPOTYPE_QUAD_QRST:
481 gchar location[MAX_ZOOM + 2];
482 map_convert_coords_to_quadtree_string(tilex, tiley,
485 return g_strdup_printf(_curr_repo->url, location);
488 case REPOTYPE_QUAD_ZERO:
490 /* This is a zero-based quadtree URI. */
491 gchar location[MAX_ZOOM + 2];
492 map_convert_coords_to_quadtree_string(tilex, tiley,
495 return g_strdup_printf(_curr_repo->url, location);
499 return map_convert_wms_to_wms(tilex, tiley, zoom,
505 vprintf("%s(): ERROR\n", __PRETTY_FUNCTION__);
510 void con_check_connection(void)
515 void con_check_connection(void)
517 /* NetworkManager support ? */
522 * Initiate a download of the given xyz coordinates using the given buffer
523 * as the URL. If the map already exists on disk, or if we are already
524 * downloading the map, then this method does nothing.
526 void map_initiate_download(guint tilex, guint tiley, guint zoom, gint retries)
528 ProgressUpdateInfo *pui;
529 vprintf("%s(%u, %u, %u, %d)\n", __PRETTY_FUNCTION__, tilex, tiley, zoom,
532 con_check_connection();
534 pui = g_slice_new(ProgressUpdateInfo);
538 pui->priority = (abs((gint) tilex - unit2tile(_center.unitx))
539 + abs((gint) tiley - unit2tile(_center.unity)));
541 pui->priority = -pui->priority; /* "Negative" makes them lowest pri. */
542 pui->retries = retries;
543 pui->repo = _curr_repo;
545 if (g_tree_lookup(_pui_tree, pui)
546 || g_tree_lookup(_downloading_tree, pui)) {
547 /* Already downloading. */
548 g_slice_free(ProgressUpdateInfo, pui);
552 pui->dest_str = NULL;
555 g_tree_insert(_pui_tree, pui, pui);
557 if (iap_is_connected() && !_curl_sid)
559 _curl_sid = g_timeout_add(100, (GSourceFunc) curl_download_timeout, NULL);
561 if (!_num_downloads++ && !_download_banner)
562 _download_banner = hildon_banner_show_progress(_window, NULL,_("Downloading maps"));
564 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
568 map_render_tile(guint tilex, guint tiley, guint destx, guint desty,
571 GdkPixbuf *pixbuf = NULL;
572 gchar buffer[BUFFER_SIZE];
573 GError *error = NULL;
576 vprintf("%s(%u, %u, %u, %u)\n", __PRETTY_FUNCTION__,
577 tilex, tiley, destx, desty);
579 if (tilex < _world_size_tiles && tiley < _world_size_tiles) {
580 /* The tile is possible. */
581 for (zoff = (_curr_repo->double_size ? 1 : 0);
582 !pixbuf && (_zoom + zoff) <= MAX_ZOOM
583 && zoff <= TILE_SIZE_P2; zoff += 1) {
584 snprintf(buffer, sizeof(buffer), "%s/%u/%u/%u.jpg",
585 _curr_repo->cache_dir, _zoom + zoff,
586 (tilex >> zoff), (tiley >> zoff));
588 pixbuf = gdk_pixbuf_new_from_file(buffer, &error);
589 if (error || !pixbuf) {
590 /* Delete so we try again some other day. */
595 /* Download, if we should. */
596 if (_auto_download && _curr_repo->type != REPOTYPE_NONE &&
598 (_curr_repo->double_size ? 1 : 0))
599 % _curr_repo->dl_zoom_steps)) {
601 map_initiate_download(tilex >> zoff,
604 -INITIAL_DOWNLOAD_RETRIES);
608 /* Check if we need to trim. */
609 if (gdk_pixbuf_get_width(pixbuf) != TILE_SIZE_PIXELS
610 || gdk_pixbuf_get_height(pixbuf) != TILE_SIZE_PIXELS)
611 pixbuf = pixbuf_trim(pixbuf);
612 /* Check if we need to blit. */
614 map_pixbuf_scale_inplace(pixbuf, zoff,
615 (tilex - ((tilex >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff),
616 (tiley - ((tiley >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff));
618 g_printf("I: %s (%d)\n", buffer, zoff);
625 gdk_draw_pixbuf(_map_pixmap,
627 pixbuf, 0, 0, destx, desty,
628 TILE_SIZE_PIXELS, TILE_SIZE_PIXELS,
629 GDK_RGB_DITHER_NONE, 0, 0);
630 g_object_unref(pixbuf);
632 gdk_draw_rectangle(_map_pixmap, _map_widget->style->black_gc,
633 TRUE, destx, desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
636 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
639 gboolean map_render_tile_idle(map_tile_rdata *mtr)
641 map_render_tile(mtr->tilex, mtr->tiley, mtr->destx, mtr->desty, FALSE);
642 gtk_widget_queue_draw_area(_map_widget, mtr->destx, mtr->desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
644 g_slice_free(map_tile_rdata, mtr);
648 gboolean map_download_idle_refresh(ProgressUpdateInfo * pui)
650 vprintf("%s(%p, %s)\n", __PRETTY_FUNCTION__, pui, pui->src_str);
652 /* Test if download succeeded (only if retries != 0). */
653 if (!pui->retries || g_file_test(pui->dest_str, G_FILE_TEST_EXISTS)) {
654 gint zoom_diff = pui->zoom - _zoom;
655 /* Only refresh at same or "lower" (more detailed) zoom level. */
656 if (zoom_diff >= 0) {
657 /* If zoom has changed since we first put in the request for
658 * this tile, then we may have to update more than one tile. */
659 guint tilex, tiley, tilex_end, tiley_end;
660 for (tilex = pui->tilex << zoom_diff,
661 tilex_end = tilex + (1 << zoom_diff);
662 tilex < tilex_end; tilex++) {
663 for (tiley = pui->tiley << zoom_diff,
664 tiley_end = tiley + (1 << zoom_diff);
665 tiley < tiley_end; tiley++) {
666 if ((tilex - _base_tilex) < BUF_WIDTH_TILES && (tiley - _base_tiley) < BUF_HEIGHT_TILES) {
667 map_render_tile(tilex, tiley,
668 ((tilex - _base_tilex) << TILE_SIZE_P2),
669 ((tiley - _base_tiley) << TILE_SIZE_P2),
671 MACRO_MAP_RENDER_DATA();
672 gtk_widget_queue_draw_area
674 ((tilex - _base_tilex) << TILE_SIZE_P2) - _offsetx,
675 ((tiley - _base_tiley) << TILE_SIZE_P2) - _offsety,
676 TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
682 /* Else the download failed. Update retries and maybe try again. */
684 if (pui->retries > 0)
686 else if (pui->retries < 0)
689 /* removal automatically calls progress_update_info_free(). */
690 g_tree_steal(_downloading_tree, pui);
691 g_tree_insert(_pui_tree, pui, pui);
693 if (iap_is_connected() && !_curl_sid)
695 _curl_sid = g_timeout_add(100,
697 curl_download_timeout,
699 /* Don't do anything else. */
702 /* No more retries left - something must be wrong. */
703 MACRO_BANNER_SHOW_INFO(_window,
704 _("Error in download. Check internet connection"
705 " and/or Map Repository URL Format."));
709 /* removal automatically calls progress_update_info_free(). */
710 g_tree_remove(_downloading_tree, pui);
712 if (++_curr_download == _num_downloads) {
714 gtk_widget_destroy(_download_banner);
715 _download_banner = NULL;
717 gtk_widget_hide(GTK_WIDGET(_progress_item));
719 _num_downloads = _curr_download = 0;
721 hildon_banner_set_fraction(HILDON_BANNER(_download_banner),
722 _curr_download / (double)_num_downloads);
724 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
729 * Force a redraw of the entire _map_pixmap, including fetching the
730 * background maps from disk and redrawing the tracks on top of them.
732 void map_force_redraw()
735 printf("%s()\n", __PRETTY_FUNCTION__);
737 for (new_y = 0; new_y < BUF_HEIGHT_TILES; ++new_y)
738 for (new_x = 0; new_x < BUF_WIDTH_TILES; ++new_x) {
739 map_render_tile(_base_tilex + new_x,
741 new_x * TILE_SIZE_PIXELS,
742 new_y * TILE_SIZE_PIXELS, FALSE);
744 MACRO_MAP_RENDER_DATA();
745 MACRO_QUEUE_DRAW_AREA();
747 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
751 * Set the current zoom level. If the given zoom level is the same as the
752 * current zoom level, or if the new zoom is invalid
753 * (not MIN_ZOOM <= new_zoom < MAX_ZOOM), then this method does nothing.
755 void map_set_zoom(guint new_zoom)
757 printf("%s(%d)\n", __PRETTY_FUNCTION__, _zoom);
759 /* Note that, since new_zoom is a guint and MIN_ZOOM is 0, this if
760 * condition also checks for new_zoom >= MIN_ZOOM. */
761 if (new_zoom > (MAX_ZOOM - 1))
763 if (new_zoom == _zoom)
766 _zoom = new_zoom / _curr_repo->view_zoom_steps
767 * _curr_repo->view_zoom_steps;
768 _world_size_tiles = unit2tile(WORLD_SIZE_UNITS);
770 /* If we're leading, update the center to reflect new zoom level. */
771 MACRO_RECALC_CENTER(_center.unitx, _center.unity);
773 /* Update center bounds to reflect new zoom level. */
774 _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth));
775 _min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight));
776 _max_center.unitx = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfwidth) - 1;
777 _max_center.unity = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfheight) - 1;
779 BOUND(_center.unitx, _min_center.unitx, _max_center.unitx);
780 BOUND(_center.unity, _min_center.unity, _max_center.unity);
782 _base_tilex = grid2tile((gint) pixel2grid((gint) unit2pixel((gint) _center.unitx))
783 - (gint) _screen_grids_halfwidth);
784 _base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity))
785 - _screen_grids_halfheight);
787 /* New zoom level, so we can't reuse the old buffer's pixels. */
789 /* Update state variables. */
790 MACRO_RECALC_OFFSET();
791 MACRO_RECALC_FOCUS_BASE();
792 MACRO_RECALC_FOCUS_SIZE();
797 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
801 * Center the view on the given unitx/unity.
803 void map_center_unit(guint new_center_unitx, guint new_center_unity)
805 gint new_base_tilex, new_base_tiley;
807 guint j, k, base_new_x, base_old_x, old_x, old_y, iox, ioy;
808 printf("%s(%d, %d)\n", __PRETTY_FUNCTION__,
809 new_center_unitx, new_center_unity);
811 /* Assure that _center.unitx/y are bounded. */
812 BOUND(new_center_unitx, _min_center.unitx, _max_center.unitx);
813 BOUND(new_center_unity, _min_center.unity, _max_center.unity);
815 _center.unitx = new_center_unitx;
816 _center.unity = new_center_unity;
818 new_base_tilex = grid2tile((gint) pixel2grid((gint)unit2pixel((gint) _center.unitx))
819 - (gint)_screen_grids_halfwidth);
820 new_base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity))
821 - _screen_grids_halfheight);
823 /* Same zoom level, so it's likely that we can reuse some of the old
824 * buffer's pixels. */
826 if (new_base_tilex != _base_tilex || new_base_tiley != _base_tiley) {
827 /* If copying from old parts to new parts, we need to make sure we
828 * don't overwrite the old parts when copying, so set up new_x,
829 * new_y, old_x, old_y, iox, and ioy with that in mind. */
830 if (new_base_tiley < _base_tiley) {
831 /* New is lower than old - start at bottom and go up. */
832 new_y = BUF_HEIGHT_TILES - 1;
835 /* New is higher than old - start at top and go down. */
839 if (new_base_tilex < _base_tilex) {
840 /* New is righter than old - start at right and go left. */
841 base_new_x = BUF_WIDTH_TILES - 1;
844 /* New is lefter than old - start at left and go right. */
849 /* Iterate over the y tile values. */
850 old_y = new_y + new_base_tiley - _base_tiley;
851 base_old_x = base_new_x + new_base_tilex - _base_tilex;
852 _base_tilex = new_base_tilex;
853 _base_tiley = new_base_tiley;
854 for (j = 0; j < BUF_HEIGHT_TILES;
855 ++j, new_y += ioy, old_y += ioy) {
858 /* Iterate over the x tile values. */
859 for (k = 0; k < BUF_WIDTH_TILES;
860 ++k, new_x += iox, old_x += iox) {
861 /* Can we get this grid block from the old buffer?. */
862 if (old_x >= 0 && old_x < BUF_WIDTH_TILES
863 && old_y >= 0 && old_y < BUF_HEIGHT_TILES) {
864 /* Copy from old buffer to new buffer. */
865 gdk_draw_drawable(_map_pixmap,
868 old_x * TILE_SIZE_PIXELS,
869 old_y * TILE_SIZE_PIXELS,
870 new_x * TILE_SIZE_PIXELS,
871 new_y * TILE_SIZE_PIXELS,
878 mtr=g_slice_new(map_tile_rdata);
879 mtr->tilex=new_base_tilex + new_x;
880 mtr->tiley=new_base_tiley + new_y;
881 mtr->destx=new_x * TILE_SIZE_PIXELS;
882 mtr->desty=new_y * TILE_SIZE_PIXELS;
884 gdk_draw_rectangle(_map_pixmap, _gc[COLORABLE_MARK_VELOCITY],
885 TRUE, mtr->destx, mtr->desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
887 g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_render_tile_idle, mtr,NULL);
889 map_render_tile(new_base_tilex + new_x,
890 new_base_tiley + new_y,
891 new_x * TILE_SIZE_PIXELS,
892 new_y * TILE_SIZE_PIXELS,
898 MACRO_MAP_RENDER_DATA();
901 MACRO_RECALC_OFFSET();
902 MACRO_RECALC_FOCUS_BASE();
905 MACRO_QUEUE_DRAW_AREA();
907 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
911 * Pan the view by the given number of units in the X and Y directions.
913 void map_pan(gint delta_unitx, gint delta_unity)
915 printf("%s(%d, %d)\n", __PRETTY_FUNCTION__, delta_unitx, delta_unity);
917 if (_center_mode > 0)
918 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
919 (_menu_ac_none_item), TRUE);
920 map_center_unit(_center.unitx + delta_unitx,
921 _center.unity + delta_unity);
923 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
927 * Initiate a move of the mark from the old location to the current
928 * location. This function queues the draw area of the old mark (to force
929 * drawing of the background map), then updates the mark, then queus the
930 * draw area of the new mark.
934 printf("%s()\n", __PRETTY_FUNCTION__);
936 /* Just queue the old and new draw areas. */
937 gtk_widget_queue_draw_area(_map_widget,
938 _mark_minx<0 ? 0 : _mark_minx,
939 _mark_miny<0 ? 0 : _mark_miny,
940 _mark_width, _mark_height);
942 gtk_widget_queue_draw_area(_map_widget,
943 _mark_minx<0 ? 0 : _mark_minx,
944 _mark_miny<0 ? 0 : _mark_miny,
945 _mark_width, _mark_height);
947 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
951 map_update_location_from_gps()
953 map_update_location(_pos.unitx, _pos.unity);
958 map_update_location_from_center()
960 map_update_location(_center.unitx, _center.unity);
965 * Make sure the mark is up-to-date. This function triggers a panning of
966 * the view if the mark is appropriately close to the edge of the view.
970 printf("%s()\n", __PRETTY_FUNCTION__);
972 guint new_center_unitx;
973 guint new_center_unity;
975 MACRO_RECALC_CENTER(new_center_unitx, new_center_unity);
977 if ((new_center_unitx - _focus.unitx) < _focus_unitwidth
978 && (new_center_unity - _focus.unity) < _focus_unitheight)
979 /* We're not changing the view - just move the mark. */
982 map_center_unit(new_center_unitx, new_center_unity);
984 /* Draw speed info */
989 g_idle_add((GSourceFunc)map_update_location_from_gps, NULL);
991 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
995 * Render a single track line to _map_pixmap. If either point on the line
996 * is a break (defined as unity == 0), a circle is drawn at the other point.
997 * IT IS AN ERROR FOR BOTH POINTS TO INDICATE A BREAK.
1000 map_render_segment(GdkGC * gc_norm, GdkGC * gc_alt,
1001 guint unitx1, guint unity1, guint unitx2, guint unity2)
1003 vprintf("%s()\n", __PRETTY_FUNCTION__);
1007 x2 = unit2bufx(unitx2);
1008 y2 = unit2bufy(unity2);
1009 /* Make sure this circle will be visible. */
1010 if ((x2 < BUF_WIDTH_PIXELS)
1011 && (y2 < BUF_HEIGHT_PIXELS))
1012 gdk_draw_arc(_map_pixmap, gc_alt, FALSE, /* FALSE: not filled. */
1013 x2 - _draw_width, y2 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0, /* start at 0 degrees. */
1015 } else if (!unity2) {
1017 x1 = unit2bufx(unitx1);
1018 y1 = unit2bufy(unity1);
1019 /* Make sure this circle will be visible. */
1020 if ((x1 < BUF_WIDTH_PIXELS)
1021 && ((unsigned)y1 < BUF_HEIGHT_PIXELS))
1022 gdk_draw_arc(_map_pixmap, gc_alt, FALSE, /* FALSE: not filled. */
1023 x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0, /* start at 0 degrees. */
1026 gint x1, y1, x2, y2;
1027 x1 = unit2bufx(unitx1);
1028 y1 = unit2bufy(unity1);
1029 x2 = unit2bufx(unitx2);
1030 y2 = unit2bufy(unity2);
1031 /* Make sure this line could possibly be visible. */
1032 if (!((x1 > BUF_WIDTH_PIXELS && x2 > BUF_WIDTH_PIXELS)
1033 || (x1 < 0 && x2 < 0)
1034 || (y1 > BUF_HEIGHT_PIXELS && y2 > BUF_HEIGHT_PIXELS)
1035 || (y1 < 0 && y2 < 0)))
1036 gdk_draw_line(_map_pixmap, gc_norm, x1, y1, x2, y2);
1039 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1043 * Render all track data onto the _map_pixmap. Note that this does not
1044 * clear the pixmap of previous track data (use map_force_redraw() for
1045 * that), and also note that this method does not queue any redraws, so it
1046 * is up to the caller to decide which part of the track really needs to be
1049 void map_render_path(Path * path, GdkGC ** gc)
1053 printf("%s()\n", __PRETTY_FUNCTION__);
1055 /* gc is a pointer to the first GC to use (for plain points). (gc + 1)
1056 * is a pointer to the GC to use for waypoints, and (gc + 2) is a pointer
1057 * to the GC to use for breaks. */
1059 /* else there is a route to draw. */
1060 for (curr = path->head, wcurr = path->whead; curr++ != path->tail;) {
1061 /* Draw the line from (curr - 1) to (curr). */
1062 map_render_segment(gc[0], gc[2],
1063 curr[-1].unitx, curr[-1].unity, curr->unitx,
1066 /* Now, check if curr is a waypoint. */
1067 if (wcurr && wcurr <= path->wtail && wcurr->point == curr) {
1068 guint x1 = unit2bufx(wcurr->point->unitx);
1069 guint y1 = unit2bufy(wcurr->point->unity);
1070 if ((x1 < BUF_WIDTH_PIXELS)
1071 && (y1 < BUF_HEIGHT_PIXELS)) {
1072 gdk_draw_arc(_map_pixmap, gc[1], FALSE, /* FALSE: not filled. */
1073 x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0, /* start at 0 degrees. */
1080 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1083 void map_render_paths()
1085 printf("%s()\n", __PRETTY_FUNCTION__);
1087 if ((_show_tracks & ROUTES_MASK) && _route.head != _route.tail) {
1088 map_render_path(&_route, _gc + COLORABLE_ROUTE);
1090 /* Now, draw the next waypoint on top of all other waypoints. */
1092 guint x1 = unit2bufx(_next_way->point->unitx);
1093 guint y1 = unit2bufy(_next_way->point->unity);
1094 if ((x1 < BUF_WIDTH_PIXELS)
1095 && (y1 < BUF_HEIGHT_PIXELS)) {
1096 /* Draw the next waypoint as a break. */
1097 gdk_draw_arc(_map_pixmap, _gc[COLORABLE_ROUTE_BREAK], FALSE, /* FALSE: not filled. */
1098 x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0, /* start at 0 degrees. */
1103 if (_show_tracks & TRACKS_MASK)
1104 map_render_path(&_track, _gc + COLORABLE_TRACK);
1106 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1109 static gboolean map_follow_move(GtkWidget * widget, GdkEventMotion * event)
1112 GdkModifierType state;
1116 if (event->is_hint) {
1117 gdk_window_get_pointer(event->window, &xx, &yy, &state);
1118 state = event->state;
1122 state = event->state;
1125 unitx = x2unit((gint) (xx - before[0]));
1126 unity = y2unit((gint) (yy - before[1]));
1127 cx = unit2x(_center.unitx);
1128 cy = unit2y(_center.unity);
1130 map_center_unit(x2unit((gint) (cx - (xx - before[0]))),
1131 y2unit((gint) (cy - (yy - before[1]))));
1138 gboolean map_key_zoom_timeout()
1140 printf("%s()\n", __PRETTY_FUNCTION__);
1141 if (_key_zoom_new < _zoom) {
1142 /* We're currently zooming in (_zoom is decreasing). */
1143 guint test = _key_zoom_new - _curr_repo->view_zoom_steps;
1144 if (test < MAX_ZOOM)
1145 /* We can zoom some more. Hurray! */
1146 _key_zoom_new = test;
1148 /* We can't zoom anymore. Booooo! */
1151 /* We're currently zooming out (_zoom is increasing). */
1152 guint test = _key_zoom_new + _curr_repo->view_zoom_steps;
1153 if (test < MAX_ZOOM)
1154 /* We can zoom some more. Hurray! */
1155 _key_zoom_new = test;
1157 /* We can't zoom anymore. Booooo! */
1161 /* We can zoom more - tell them how much they're zooming. */
1164 snprintf(buffer, sizeof(buffer),
1165 "%s %d", _("Zoom to Level"), _key_zoom_new);
1166 MACRO_BANNER_SHOW_INFO(_window, buffer);
1168 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1172 void map_scale_draw(GdkEventExpose *event)
1174 gdk_rectangle_intersect(&event->area, &_scale_rect, &event->area);
1176 if (event->area.width && event->area.height) {
1177 gdk_draw_rectangle(_map_widget->window,
1178 _map_widget->style->
1179 bg_gc[GTK_WIDGET_STATE(_map_widget)],
1180 TRUE, _scale_rect.x, _scale_rect.y,
1182 _scale_rect.height);
1183 gdk_draw_rectangle(_map_widget->window,
1184 _map_widget->style->
1185 fg_gc[GTK_WIDGET_STATE(_map_widget)],
1186 FALSE, _scale_rect.x, _scale_rect.y,
1188 _scale_rect.height);
1190 /* Now calculate and draw the distance. */
1194 gfloat lat1, lon1, lat2, lon2;
1197 unit2latlon(_center.unitx - pixel2unit(SCALE_WIDTH / 2 - 4),
1198 _center.unity, lat1, lon1);
1199 unit2latlon(_center.unitx + pixel2unit(SCALE_WIDTH / 2 - 4),
1200 _center.unity, lat2, lon2);
1201 distance = calculate_distance(lat1, lon1, lat2, lon2) * UNITS_CONVERT[_units];
1204 snprintf(buffer, sizeof(buffer),
1205 "%0.2f %s", distance,
1206 UNITS_TEXT[_units]);
1207 else if (distance < 10.f)
1208 snprintf(buffer, sizeof(buffer),
1209 "%0.1f %s", distance,
1210 UNITS_TEXT[_units]);
1212 snprintf(buffer, sizeof(buffer),
1213 "%0.f %s", distance,
1214 UNITS_TEXT[_units]);
1216 pango_layout_set_text(_scale_layout, buffer, -1);
1217 pango_layout_get_pixel_size(_scale_layout, &width, NULL);
1219 /* Draw the layout itself. */
1220 gdk_draw_layout(_map_widget->window,
1221 _map_widget->style->
1222 fg_gc[GTK_WIDGET_STATE
1225 (_scale_rect.width - width) / 2,
1226 _scale_rect.y, _scale_layout);
1228 /* Draw little hashes on the ends. */
1229 gdk_draw_line(_map_widget->window,
1230 _map_widget->style->
1231 fg_gc[GTK_WIDGET_STATE
1235 _scale_rect.height / 2 - 4,
1238 _scale_rect.height / 2 + 4);
1239 gdk_draw_line(_map_widget->window,
1240 _map_widget->style->
1241 fg_gc[GTK_WIDGET_STATE
1245 _scale_rect.height / 2,
1247 (_scale_rect.width - width) / 2 -
1250 _scale_rect.height / 2);
1251 gdk_draw_line(_map_widget->window,
1252 _map_widget->style->
1253 fg_gc[GTK_WIDGET_STATE
1256 _scale_rect.width - 4,
1258 _scale_rect.height / 2 - 4,
1260 _scale_rect.width - 4,
1262 _scale_rect.height / 2 + 4);
1263 gdk_draw_line(_map_widget->window,
1264 _map_widget->style->
1265 fg_gc[GTK_WIDGET_STATE
1268 _scale_rect.width - 4,
1270 _scale_rect.height / 2,
1272 (_scale_rect.width + width) / 2 +
1275 _scale_rect.height / 2);
1280 gboolean map_cb_expose(GtkWidget * widget, GdkEventExpose * event)
1282 printf("%s(%d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
1283 event->area.x, event->area.y,
1284 event->area.width, event->area.height);
1286 gdk_draw_drawable(GDK_DRAWABLE(_map_widget->window),
1287 _gc[COLORABLE_MARK],
1289 event->area.x + _offsetx, event->area.y + _offsety,
1290 event->area.x, event->area.y,
1291 event->area.width, event->area.height);
1294 /* Draw scale, if necessary. */
1296 map_scale_draw(event);
1298 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1302 int map_zoom(gint zdir)
1307 nzoom = _zoom + zdir;
1309 if ((nzoom > 0) && (nzoom < MAX_ZOOM - 1)) {
1310 snprintf(buffer, sizeof(buffer), "%s %d",
1311 _("Zoom to Level"), nzoom);
1312 MACRO_BANNER_SHOW_INFO(_window, buffer);
1313 map_set_zoom(nzoom);
1322 static gfloat z=5.0;
1326 if (_zoom_timeout_sid==0)
1329 z=(z+_gps.speed+1)/3;
1330 if (z>5) z=5.0; else if (z<1) z=1.0;
1332 g_printf("Setting autozoom to: %f %d S:%f\n", z, iz, _gps.speed);
1344 map_set_autozoom(gboolean az)
1347 _zoom_timeout_sid=g_timeout_add(_gps.speed<5 ? 2000 : 5000, (GSourceFunc) map_autozoomer, NULL);
1350 if (_zoom_timeout_sid) {
1351 g_source_remove(_zoom_timeout_sid);
1352 _zoom_timeout_sid=0;
1360 map_draw_route(gint x, gint y)
1362 cmenu_route_add_way(x, y);
1366 map_draw_track(gint x, gint y)
1368 _pos.unitx = x2unit((gint) (x + 0.5));
1369 _pos.unity = y2unit((gint) (y + 0.5));
1370 unit2latlon(_pos.unitx, _pos.unity, _gps.lat, _gps.lon);
1373 track_add(time(NULL), FALSE);
1378 map_set_place_information(osm_way *s, osm_place *mp, osm_place *sp)
1382 if (!s && !mp && !sp) {
1383 snprintf(buffer, sizeof(buffer), "Unknown location");
1386 snprintf(buffer, sizeof(buffer), "On %s%s%s%s%s%s%s%s%s",
1387 s ? s->name ? s->name : _("unknown") : "?",
1388 s ? s->ref ? ", " : "" : "",
1389 s ? s->ref ? s->ref : "" : "",
1390 s ? s->int_ref ? ", " : "" : "",
1391 s ? s->int_ref ? s->int_ref : "" : "",
1392 (sp && sp->name) ? " in " : "",
1393 (sp && sp->name) ? sp->name : "",
1394 (mp && mp->name) ? " in " : "",
1395 (mp && mp->name) ? mp->name : "");
1397 gtk_label_set_label(GTK_LABEL(info_banner.location), buffer);
1400 /* XXX: Move this to osm-db.c */
1402 map_update_location(gint x, gint y)
1405 gdouble lat,lon, dist;
1406 gboolean fs, check_place=FALSE;
1407 static gboolean inp=FALSE;
1409 /* We run the gtk mainloop in progress callback so we can be called again, we don't like that */
1414 unit2latlon(x, y, lat, lon);
1415 ilat=lat2mp_int(lat);
1416 ilon=lon2mp_int(lon);
1418 osm_progress_set_widget(_db, _progress_item);
1420 /* Check if we are still near the same way as last time */
1421 if (map_loc.street && osm_way_distance(ilat, ilon, map_loc.street->node_f, map_loc.street->node_t, &dist)==TRUE) {
1423 osm_way_free(map_loc.street);
1424 map_loc.street=osm_find_nearest_way(ilat, ilon);
1430 osm_way_free(map_loc.street);
1431 map_loc.street=osm_find_nearest_way(ilat, ilon);
1435 _map_location_known=map_loc.street ? TRUE : FALSE;
1436 _map_location_dist=map_loc.street ? map_loc.street->dist : 900000.0;
1438 if (check_place==TRUE) {
1440 fs=osm_find_nearest_place(NODE_PLACE_SUBURB, ilat, ilon, &map_loc.secondary);
1441 if (fs==TRUE && map_loc.secondary && map_loc.secondary->isin!=0) {
1442 if (osm_place_get(map_loc.secondary->isin, ilat, ilon, &map_loc.primary)==FALSE) {
1443 if (osm_find_nearest_place(NODE_PLACE_CITY, ilat, ilon, &map_loc.primary)==TRUE)
1444 g_printf("Near city: %s\n", map_loc.primary->name);
1445 else if (osm_find_nearest_place(NODE_PLACE_TOWN, ilat, ilon, &map_loc.primary)==TRUE)
1446 g_printf("Near town: %s\n", map_loc.primary->name);
1448 g_printf("Unknown\n");
1450 g_printf("In: %s\n", map_loc.primary ? map_loc.primary->name : "?");
1452 } else if (map_loc.street && map_loc.street->isin!=0) {
1453 if (osm_place_get(map_loc.street->isin, ilat, ilon, &map_loc.primary)==FALSE) {
1454 g_printf("Street location not know.\n");
1456 g_printf("Street is in: %s\n", map_loc.primary ? map_loc.primary->name : "?");
1459 if (osm_find_nearest_place(NODE_PLACE_CITY, ilat, ilon, &map_loc.primary)==TRUE)
1460 g_printf("Near city: %s\n", map_loc.primary->name);
1461 else if (osm_find_nearest_place(NODE_PLACE_TOWN, ilat, ilon, &map_loc.primary)==TRUE)
1462 g_printf("Near town: %s\n", map_loc.primary->name);
1464 g_printf("Unknown\n");
1469 map_set_place_information(map_loc.street, map_loc.primary, map_loc.secondary);
1470 osm_progress_set_widget(_db, NULL);
1474 gboolean map_cb_scroll_event(GtkWidget * widget, GdkEventScroll * event)
1477 if (event->direction == GDK_SCROLL_UP) {
1478 map_center_unit(x2unit((gint) (event->x + 0.5)),
1479 y2unit((gint) (event->y + 0.5)));
1480 map_set_zoom(_zoom - 1);
1481 } else if (event->direction == GDK_SCROLL_DOWN) {
1482 map_center_unit(x2unit((gint) (event->x + 0.5)),
1483 y2unit((gint) (event->y + 0.5)));
1484 map_set_zoom(_zoom + 1);
1490 gboolean map_cb_button_press(GtkWidget * widget, GdkEventButton * event)
1492 printf("%s()\n", __PRETTY_FUNCTION__);
1494 _cmenu_position_x = event->x + 0.5;
1495 _cmenu_position_y = event->y + 0.5;
1497 switch (event->button) {
1499 if (event->type == GDK_2BUTTON_PRESS) {
1500 map_set_zoom(_zoom - 1);
1503 if (event->type == GDK_3BUTTON_PRESS)
1507 press[0] = event->x;
1508 press[1] = event->y;
1509 before[0] = press[0];
1510 before[1] = press[1];
1512 _id = g_signal_connect(G_OBJECT(_map_widget),
1513 "motion_notify_event",
1514 G_CALLBACK(map_follow_move), NULL);
1518 gtk_menu_popup(GTK_MENU(_menu_map), NULL, NULL, NULL, NULL,
1519 event->button, gtk_get_current_event_time());
1524 /* Return FALSE to allow context menu to work. */
1525 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1529 gboolean map_cb_button_release(GtkWidget * widget, GdkEventButton * event)
1531 printf("%s()\n", __PRETTY_FUNCTION__);
1533 switch (event->button) {
1535 if (_center_mode > 0)
1536 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
1537 (_menu_ac_none_item),
1540 switch (_map_mode) {
1541 case MAP_MODE_DRAW_TRACK:
1542 map_draw_track(event->x, event->y);
1544 case MAP_MODE_DRAW_ROUTE:
1545 map_draw_route(event->x, event->y);
1547 case MAP_MODE_SET_ROUTE_FROM:
1548 case MAP_MODE_SET_ROUTE_POINT:
1549 case MAP_MODE_SET_ROUTE_TO:
1554 ux = x2unit((gint) (event->x + 0.5));
1555 uy = y2unit((gint) (event->y + 0.5));
1556 map_update_location(ux, uy);
1559 g_idle_add((GSourceFunc)map_update_location_from_center, NULL);
1561 map_center_unit(x2unit((gint) (event->x + 0.5)),
1562 y2unit((gint) (event->y + 0.5)));
1566 release[0] = event->x;
1567 release[1] = event->y;
1569 g_signal_handler_disconnect(G_OBJECT(_map_widget), _id);
1582 /* Return FALSE to avoid context menu (if it hasn't popped up already). */
1583 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);