14 #include <glib/gstdio.h>
20 #include "hildon-mapper.h"
32 #include "mapper-types.h"
33 #include "ui-common.h"
39 #include "map-download.h"
41 #include "gtkcompass.h"
43 /** The "base tile" is the upper-left tile in the pixmap. */
44 guint _base_tilex = -5;
45 guint _base_tiley = -5;
47 guint _zoom = 3; /* zoom level, from 0 to MAX_ZOOM. */
49 Point _min_center = { -1, -1 };
50 Point _max_center = { -1, -1 };
51 Point _focus = { -1, -1 };
52 Point _center = { -1, -1 }; /* current center location, X. */
54 CenterMode _center_mode = CENTER_LEAD;
57 static guint press[2] = { 0, 0 };
58 static guint release[2] = { 0, 0 };
59 static guint before[2] = { 0, 0 };
60 static guint map_drag_id = 0;
62 /** VARIABLES FOR ACCESSING THE LOCATION/BOUNDS OF THE CURRENT MARK. */
67 static gint _mark_minx;
68 static gint _mark_miny;
69 static gint _mark_width;
70 static gint _mark_height;
72 static gint zoom_timeout_sid=0;
73 static gint map_mode=0;
74 static gboolean map_data_needs_refresh=FALSE;
76 static osm_location map_loc = {NULL, NULL, NULL, FALSE, FALSE, 0, 0, 0.0, 0.0, 0 };
78 typedef struct _map_tile_rdata map_tile_rdata;
79 struct _map_tile_rdata {
80 guint tilex, tiley, zoom;
84 /* Tile max age, 1 week */
85 #define TILE_MAX_AGE (604800)
87 void map_render_paths();
88 void map_force_redraw();
89 static void map_update_location(gdouble lat, gdouble lon, gboolean force);
90 void map_draw_position_icon(Position *pos);
92 /******************************************************************************/
97 return GTK_WIDGET(gtk_drawing_area_new());
101 map_cb_configure(GtkWidget *widget, GdkEventConfigure *event)
103 _screen_width_pixels = _map_widget->allocation.width;
104 _screen_height_pixels = _map_widget->allocation.height;
105 _screen_grids_halfwidth = pixel2grid(_screen_width_pixels) / 2;
106 _screen_grids_halfheight = pixel2grid(_screen_height_pixels) / 2;
108 /* Set _scale_rect. */
109 _scale_rect.x = (_screen_width_pixels - SCALE_WIDTH) / 2;
110 _scale_rect.width = SCALE_WIDTH;
111 pango_layout_set_text(_scale_layout, "0", -1);
112 pango_layout_get_pixel_size(_scale_layout, NULL, &_scale_rect.height);
113 _scale_rect.y = _screen_height_pixels - _scale_rect.height - 1;
115 MACRO_RECALC_FOCUS_BASE();
116 MACRO_RECALC_FOCUS_SIZE();
118 _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth));
119 _min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight));
120 _max_center.unitx = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfwidth) - 1;
121 _max_center.unity = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfheight) - 1;
123 map_center_unit(_center.unitx, _center.unity);
129 * Draw the current mark (representing the current GPS location) onto
130 * _map_widget. This method does not queue the draw area.
135 gdk_draw_arc(_map_widget->window, _conn_state == RCVR_FIXED ? _gc[COLORABLE_MARK] : _gc[COLORABLE_MARK_OLD], FALSE,
136 _mark_x1 - _draw_width, _mark_y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0, 360 * 64);
137 gdk_draw_line(_map_widget->window, _conn_state == RCVR_FIXED ? (_show_velvec ? _gc[COLORABLE_MARK_VELOCITY] : _gc[COLORABLE_MARK]) : _gc[COLORABLE_MARK_OLD],
138 _mark_x1, _mark_y1, _mark_x2, _mark_y2);
142 * "Set" the mark, which translates the current GPS position into on-screen
143 * units in preparation for drawing the mark with map_draw_mark().
148 _mark_x1 = unit2x(_pos.unitx);
149 _mark_y1 = unit2y(_pos.unity);
150 _mark_x2 = _mark_x1 + (_show_velvec ? _gps.vel_offsetx : 0);
151 _mark_y2 = _mark_y1 + (_show_velvec ? _gps.vel_offsety : 0);
152 _mark_minx = MIN(_mark_x1, _mark_x2) - (2 * _draw_width);
153 _mark_miny = MIN(_mark_y1, _mark_y2) - (2 * _draw_width);
154 _mark_width = abs(_mark_x1 - _mark_x2) + (4 * _draw_width);
155 _mark_height = abs(_mark_y1 - _mark_y2) + (4 * _draw_width);
159 * Do an in-place scaling of a pixbuf's pixels at the given ratio from the
160 * given source location. It would have been nice if gdk_pixbuf provided
161 * this method, but I guess it's not general-purpose enough.
164 map_pixbuf_scale_inplace(GdkPixbuf * pixbuf, guint ratio_p2, guint src_x, guint src_y)
166 guint dest_x = 0, dest_y = 0, dest_dim = TILE_SIZE_PIXELS;
167 guint rowstride = gdk_pixbuf_get_rowstride(pixbuf);
168 guint n_channels = gdk_pixbuf_get_n_channels(pixbuf);
169 guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
171 /* Sweep through the entire dest area, copying as necessary, but
172 * DO NOT OVERWRITE THE SOURCE AREA. We'll copy it afterward. */
174 guint src_dim = dest_dim >> ratio_p2;
175 guint src_endx = src_x - dest_x + src_dim;
178 for (y = dest_dim - 1; y >= 0; y--) {
179 guint src_offset_y, dest_offset_y;
181 src_offset_y = (src_y + (y >> ratio_p2)) * rowstride;
182 dest_offset_y = (dest_y + y) * rowstride;
185 if ((unsigned)(dest_y + y - src_y) < src_dim && (unsigned)(dest_x + x - src_x) < src_dim)
188 for (; x >= 0; x--) {
189 guint src_offset, dest_offset, i;
191 src_offset = src_offset_y + (src_x + (x >> ratio_p2)) * n_channels;
192 dest_offset = dest_offset_y + (dest_x + x) * n_channels;
194 pixels[dest_offset] = pixels[src_offset];
195 for (i = n_channels - 1; i; i--)
196 pixels[dest_offset + i] = pixels[src_offset + i];
198 if ((unsigned)(dest_y + y - src_y) < src_dim && x == src_endx)
203 /* Reuse src_dim and src_endx to store new src_x and src_y. */
204 src_dim = src_x + ((src_x - dest_x) >> ratio_p2);
205 src_endx = src_y + ((src_y - dest_y) >> ratio_p2);
211 while ((dest_dim >>= ratio_p2) > 1);
215 * Trim pixbufs that are bigger than tiles. (Those pixbufs result, when
216 * captions should be cut off.)
219 pixbuf_trim(GdkPixbuf * pixbuf)
221 GdkPixbuf *mpixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(pixbuf),
222 8, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
224 gdk_pixbuf_copy_area(pixbuf,
225 (gdk_pixbuf_get_width(pixbuf) - TILE_SIZE_PIXELS) / 2,
226 (gdk_pixbuf_get_height(pixbuf) - TILE_SIZE_PIXELS) / 2,
228 TILE_SIZE_PIXELS, mpixbuf, 0, 0);
230 g_object_unref(pixbuf);
235 map_tile_load(guint tilex, guint tiley, gint zoff, gboolean download)
238 GError *error = NULL;
239 gchar buffer[BUFFER_SIZE];
243 snprintf(buffer, sizeof(buffer), "%s/%u/%u/%u.jpg", _curr_repo->cache_dir, _zoom + zoff, (tilex >> zoff), (tiley >> zoff));
245 pixbuf=gdk_pixbuf_new_from_file(buffer, &error);
246 if (error || !pixbuf) {
249 if (_auto_download && _curr_repo->type != REPOTYPE_NONE && !((_zoom + zoff - (_curr_repo->double_size ? 1 : 0)) % _curr_repo->dl_zoom_steps)) {
251 map_initiate_download(tilex >> zoff, tiley >> zoff, _zoom + zoff, -INITIAL_DOWNLOAD_RETRIES);
257 /* Check tile age, if file date is ower a week old, redownload if autodownload enabled */
258 se=stat(buffer, &tstat);
262 if (t-tstat.st_mtime>TILE_MAX_AGE) {
263 if (_auto_download && _curr_repo->type != REPOTYPE_NONE && !((_zoom + zoff - (_curr_repo->double_size ? 1 : 0)) % _curr_repo->dl_zoom_steps)) {
265 g_printf("Tile: %s is old, re-downloading\n", buffer);
266 map_initiate_download(tilex >> zoff, tiley >> zoff, _zoom + zoff, -INITIAL_DOWNLOAD_RETRIES);
276 map_render_tile(guint tilex, guint tiley, guint destx, guint desty, gboolean fast_fail)
278 GdkPixbuf *pixbuf=NULL;
281 if (tilex<_world_size_tiles && tiley<_world_size_tiles) {
282 /* The tile is possible. */
283 for (zoff = (_curr_repo->double_size ? 1 : 0); !pixbuf && (_zoom + zoff) <= MAX_ZOOM && zoff <= TILE_SIZE_P2; zoff += 1) {
284 pixbuf=map_tile_load(tilex, tiley, zoff, !fast_fail);
289 /* Check if we need to trim. */
290 if (gdk_pixbuf_get_width(pixbuf) != TILE_SIZE_PIXELS || gdk_pixbuf_get_height(pixbuf) != TILE_SIZE_PIXELS)
291 pixbuf = pixbuf_trim(pixbuf);
293 /* Check if we need to blit. */
295 map_pixbuf_scale_inplace(pixbuf, zoff,
296 (tilex - ((tilex >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff),
297 (tiley - ((tiley >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff));
304 gdk_draw_pixbuf(_map_pixmap, _gc[COLORABLE_MARK], pixbuf, 0, 0, destx, desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS, GDK_RGB_DITHER_NONE, 0, 0);
305 g_object_unref(pixbuf);
308 gdk_draw_rectangle(_map_pixmap, _map_widget->style->black_gc, TRUE, destx, desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
312 map_render_tile_idle(map_tile_rdata *mtr)
314 map_render_tile(mtr->tilex, mtr->tiley, mtr->destx, mtr->desty, FALSE);
315 gtk_widget_queue_draw_area(_map_widget, mtr->destx, mtr->desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
317 g_slice_free(map_tile_rdata, mtr);
322 map_render_data(void)
324 /* Don't update if dragging, too much lag */
329 map_draw_position_icon(&_home);
337 * Force a redraw of the entire _map_pixmap, including fetching the
338 * background maps from disk and redrawing the tracks on top of them.
341 map_force_redraw(void)
345 for (new_y = 0; new_y < BUF_HEIGHT_TILES; ++new_y)
346 for (new_x = 0; new_x < BUF_WIDTH_TILES; ++new_x) {
347 map_render_tile(_base_tilex + new_x,
349 new_x * TILE_SIZE_PIXELS,
350 new_y * TILE_SIZE_PIXELS, FALSE);
353 MACRO_QUEUE_DRAW_AREA();
357 * Set the current zoom level. If the given zoom level is the same as the
358 * current zoom level, or if the new zoom is invalid
359 * (not MIN_ZOOM <= new_zoom < MAX_ZOOM), then this method does nothing.
362 map_set_zoom(guint new_zoom)
364 /* Note that, since new_zoom is a guint and MIN_ZOOM is 0, this if
365 * condition also checks for new_zoom >= MIN_ZOOM. */
366 if (new_zoom > (MAX_ZOOM - 1))
368 if (new_zoom == _zoom)
371 _zoom = new_zoom / _curr_repo->view_zoom_steps * _curr_repo->view_zoom_steps;
372 _world_size_tiles = unit2tile(WORLD_SIZE_UNITS);
374 /* If we're leading, update the center to reflect new zoom level. */
375 MACRO_RECALC_CENTER(_center.unitx, _center.unity);
377 /* Update center bounds to reflect new zoom level. */
378 _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth));
379 _min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight));
380 _max_center.unitx = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfwidth) - 1;
381 _max_center.unity = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfheight) - 1;
383 BOUND(_center.unitx, _min_center.unitx, _max_center.unitx);
384 BOUND(_center.unity, _min_center.unity, _max_center.unity);
386 _base_tilex = grid2tile((gint) pixel2grid((gint) unit2pixel((gint) _center.unitx)) - (gint) _screen_grids_halfwidth);
387 _base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity)) - _screen_grids_halfheight);
389 /* New zoom level, so we can't reuse the old buffer's pixels. */
390 /* Update state variables. */
391 MACRO_RECALC_OFFSET();
392 MACRO_RECALC_FOCUS_BASE();
393 MACRO_RECALC_FOCUS_SIZE();
400 * Center the view on the given unitx/unity.
403 map_center_unit(guint new_center_unitx, guint new_center_unity)
405 gint new_base_tilex, new_base_tiley;
407 guint j, k, base_new_x, base_old_x, old_x, old_y, iox, ioy;
409 /* Assure that _center.unitx/y are bounded. */
410 BOUND(new_center_unitx, _min_center.unitx, _max_center.unitx);
411 BOUND(new_center_unity, _min_center.unity, _max_center.unity);
413 _center.unitx = new_center_unitx;
414 _center.unity = new_center_unity;
416 new_base_tilex = grid2tile((gint) pixel2grid((gint)unit2pixel((gint) _center.unitx))
417 - (gint)_screen_grids_halfwidth);
418 new_base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity))
419 - _screen_grids_halfheight);
421 /* Same zoom level, so it's likely that we can reuse some of the old
422 * buffer's pixels. */
424 if (new_base_tilex != _base_tilex || new_base_tiley != _base_tiley) {
425 /* If copying from old parts to new parts, we need to make sure we
426 * don't overwrite the old parts when copying, so set up new_x,
427 * new_y, old_x, old_y, iox, and ioy with that in mind. */
428 if (new_base_tiley < _base_tiley) {
429 /* New is lower than old - start at bottom and go up. */
430 new_y = BUF_HEIGHT_TILES - 1;
433 /* New is higher than old - start at top and go down. */
437 if (new_base_tilex < _base_tilex) {
438 /* New is righter than old - start at right and go left. */
439 base_new_x = BUF_WIDTH_TILES - 1;
442 /* New is lefter than old - start at left and go right. */
447 /* Iterate over the y tile values. */
448 old_y = new_y + new_base_tiley - _base_tiley;
449 base_old_x = base_new_x + new_base_tilex - _base_tilex;
450 _base_tilex = new_base_tilex;
451 _base_tiley = new_base_tiley;
452 for (j = 0; j < BUF_HEIGHT_TILES; ++j, new_y += ioy, old_y += ioy) {
455 /* Iterate over the x tile values. */
456 for (k = 0; k < BUF_WIDTH_TILES;
457 ++k, new_x += iox, old_x += iox) {
458 /* Can we get this grid block from the old buffer?. */
459 if (old_x >= 0 && old_x < BUF_WIDTH_TILES
460 && old_y >= 0 && old_y < BUF_HEIGHT_TILES) {
461 /* Copy from old buffer to new buffer. */
462 gdk_draw_drawable(_map_pixmap,
465 old_x * TILE_SIZE_PIXELS,
466 old_y * TILE_SIZE_PIXELS,
467 new_x * TILE_SIZE_PIXELS,
468 new_y * TILE_SIZE_PIXELS,
472 map_render_tile(new_base_tilex + new_x,
473 new_base_tiley + new_y,
474 new_x * TILE_SIZE_PIXELS,
475 new_y * TILE_SIZE_PIXELS,
483 MACRO_RECALC_OFFSET();
484 MACRO_RECALC_FOCUS_BASE();
487 MACRO_QUEUE_DRAW_AREA();
491 * Pan the view by the given number of units in the X and Y directions.
494 map_pan(gint delta_unitx, gint delta_unity)
496 if (_center_mode > 0)
497 set_action_activate("autocenter_none", TRUE);
499 map_center_unit(_center.unitx + delta_unitx, _center.unity + delta_unity);
503 * Helper to center map on given lat/lon
506 map_center_latlon(gdouble lat, gdouble lon)
510 latlon2unit(lat, lon, unitx, unity);
511 map_center_unit(unitx, unity);
515 * Helper to goto given point and update location
519 map_goto_position(Position *pos)
521 if (pos->valid==FALSE)
524 _center_mode=CENTER_MANUAL;
525 map_center_latlon(pos->lat, pos->lon);
526 map_set_autozoom(FALSE);
527 g_idle_add((GSourceFunc)map_update_location_from_center, NULL);
532 * Initiate a move of the mark from the old location to the current
533 * location. This function queues the draw area of the old mark (to force
534 * drawing of the background map), then updates the mark, then queus the
535 * draw area of the new mark.
540 /* Just queue the old and new draw areas. */
541 gtk_widget_queue_draw_area(_map_widget,
542 _mark_minx<0 ? 0 : _mark_minx,
543 _mark_miny<0 ? 0 : _mark_miny,
544 _mark_width, _mark_height);
546 gtk_widget_queue_draw_area(_map_widget,
547 _mark_minx<0 ? 0 : _mark_minx,
548 _mark_miny<0 ? 0 : _mark_miny,
549 _mark_width, _mark_height);
553 map_update_location_from_gps(void)
555 map_update_location(_gps.lat, _gps.lon, FALSE);
560 map_update_location_from_center(void)
563 /* Force re-validation of place if user is clicking around */
565 /* XXX: hmm, not the right place for this */
567 _pos.unitx=_center.unitx;
568 _pos.unity=_center.unity;
569 unit2latlon(_pos.unitx, _pos.unity, _gps.lat, _gps.lon);
571 unit2latlon(_center.unitx, _center.unity, lat, lon);
572 map_update_location(lat, lon, TRUE);
577 * Draw given pixbuf on map, centered on x,y
580 map_draw_pixbuf(guint unitx, guint unity, GdkPixbuf *p)
583 x = unit2bufx(unitx);
584 y = unit2bufy(unity);
586 gdk_draw_pixbuf(_map_pixmap, _gc[COLORABLE_POI],
588 x - gdk_pixbuf_get_width(p) / 2,
589 y - gdk_pixbuf_get_height(p) / 2,
590 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
594 * Draw an icon on given Position.
596 * XXX: don't hardcode as home.png !
599 map_draw_position_icon(Position *pos)
604 latlon2unit(pos->lat, pos->lon, x, y);
608 if ((x1 > BUF_WIDTH_PIXELS) || (y1 > BUF_HEIGHT_PIXELS))
611 p=gdk_pixbuf_new_from_file(DATADIR "/pixmaps/mapper/home.png", NULL);
613 map_draw_pixbuf(x,y,p);
620 * Make sure the mark is up-to-date. This function triggers a panning of
621 * the view if the mark is appropriately close to the edge of the view.
624 map_refresh_mark(void)
626 guint new_center_unitx;
627 guint new_center_unity;
629 MACRO_RECALC_CENTER(new_center_unitx, new_center_unity);
631 if ((new_center_unitx - _focus.unitx) < _focus_unitwidth
632 && (new_center_unity - _focus.unity) < _focus_unitheight)
633 /* We're not changing the view - just move the mark. */
636 map_center_unit(new_center_unitx, new_center_unity);
638 /* Draw speed info */
643 g_idle_add((GSourceFunc)map_update_location_from_gps, NULL);
647 * Render a single track line to _map_pixmap. If either point on the line
648 * is a break (defined as unity == 0), a circle is drawn at the other point.
649 * IT IS AN ERROR FOR BOTH POINTS TO INDICATE A BREAK.
652 map_render_segment(GdkGC * gc_norm, GdkGC * gc_alt, guint unitx1, guint unity1, guint unitx2, guint unity2)
656 x2 = unit2bufx(unitx2);
657 y2 = unit2bufy(unity2);
658 /* Make sure this circle will be visible. */
659 if ((x2 < BUF_WIDTH_PIXELS) && (y2 < BUF_HEIGHT_PIXELS))
660 gdk_draw_arc(_map_pixmap, gc_alt, FALSE, /* FALSE: not filled. */
661 x2 - _draw_width, y2 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0, /* start at 0 degrees. */
663 } else if (!unity2) {
665 x1 = unit2bufx(unitx1);
666 y1 = unit2bufy(unity1);
667 /* Make sure this circle will be visible. */
668 if ((x1 < BUF_WIDTH_PIXELS) && ((unsigned)y1 < BUF_HEIGHT_PIXELS))
669 gdk_draw_arc(_map_pixmap, gc_alt, FALSE, /* FALSE: not filled. */
670 x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0, /* start at 0 degrees. */
674 x1 = unit2bufx(unitx1);
675 y1 = unit2bufy(unity1);
676 x2 = unit2bufx(unitx2);
677 y2 = unit2bufy(unity2);
678 /* Make sure this line could possibly be visible. */
679 if (!((x1 > BUF_WIDTH_PIXELS && x2 > BUF_WIDTH_PIXELS)
680 || (x1 < 0 && x2 < 0)
681 || (y1 > BUF_HEIGHT_PIXELS && y2 > BUF_HEIGHT_PIXELS)
682 || (y1 < 0 && y2 < 0)))
683 gdk_draw_line(_map_pixmap, gc_norm, x1, y1, x2, y2);
688 * Render all track data onto the _map_pixmap. Note that this does not
689 * clear the pixmap of previous track data (use map_force_redraw() for
690 * that), and also note that this method does not queue any redraws, so it
691 * is up to the caller to decide which part of the track really needs to be
695 map_render_path(Path * path, GdkGC ** gc)
700 /* gc is a pointer to the first GC to use (for plain points). (gc + 1)
701 * is a pointer to the GC to use for waypoints, and (gc + 2) is a pointer
702 * to the GC to use for breaks. */
704 /* else there is a route to draw. */
705 for (curr = path->head, wcurr = path->whead; curr++ != path->tail;) {
706 /* Draw the line from (curr - 1) to (curr). */
707 map_render_segment(gc[0], gc[2],
708 curr[-1].unitx, curr[-1].unity, curr->unitx,
711 /* Now, check if curr is a waypoint. */
712 if (wcurr && wcurr <= path->wtail && wcurr->point == curr) {
713 guint x1 = unit2bufx(wcurr->point->unitx);
714 guint y1 = unit2bufy(wcurr->point->unity);
715 if ((x1 < BUF_WIDTH_PIXELS)
716 && (y1 < BUF_HEIGHT_PIXELS)) {
717 gdk_draw_arc(_map_pixmap, gc[1], FALSE, /* FALSE: not filled. */
718 x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0, /* start at 0 degrees. */
727 map_render_paths(void)
729 if ((_show_tracks & ROUTES_MASK) && _route.head != _route.tail) {
730 map_render_path(&_route, _gc + COLORABLE_ROUTE);
732 /* Now, draw the next waypoint on top of all other waypoints. */
734 guint x1 = unit2bufx(_next_way->point->unitx);
735 guint y1 = unit2bufy(_next_way->point->unity);
737 if ((x1 < BUF_WIDTH_PIXELS) && (y1 < BUF_HEIGHT_PIXELS)) {
738 /* Draw the next waypoint as a break. */
739 gdk_draw_arc(_map_pixmap, _gc[COLORABLE_ROUTE_BREAK], FALSE, /* FALSE: not filled. */
740 x1 - _draw_width, y1 - _draw_width, 4 * _draw_width, 4 * _draw_width, 0, /* start at 0 degrees. */
745 if (_show_tracks & TRACKS_MASK)
746 map_render_path(&_track, _gc + COLORABLE_TRACK);
754 map_follow_move_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data)
756 GdkModifierType state;
761 if (!(event->state & GDK_BUTTON1_MASK))
764 if (event->is_hint) {
765 gdk_window_get_pointer(event->window, &xx, &yy, &state);
766 state = event->state;
770 state = event->state;
773 unitx = x2unit((gint) (xx - before[0]));
774 unity = y2unit((gint) (yy - before[1]));
775 cx = unit2x(_center.unitx);
776 cy = unit2y(_center.unity);
778 map_center_unit(x2unit((gint) (cx - (xx - before[0]))), y2unit((gint) (cy - (yy - before[1]))));
779 map_data_needs_refresh=TRUE;
787 map_key_zoom_timeout(void)
789 if (_key_zoom_new < _zoom) {
790 /* We're currently zooming in (_zoom is decreasing). */
791 guint test = _key_zoom_new - _curr_repo->view_zoom_steps;
793 _key_zoom_new = test;
797 /* We're currently zooming out (_zoom is increasing). */
798 guint test = _key_zoom_new + _curr_repo->view_zoom_steps;
800 _key_zoom_new = test;
809 map_scale_draw(GdkEventExpose *event)
813 gdouble lat1, lon1, lat2, lon2;
816 gdk_rectangle_intersect(&event->area, &_scale_rect, &event->area);
818 if (event->area.width && event->area.height) {
819 gdk_draw_rectangle(_map_widget->window,
821 bg_gc[GTK_WIDGET_STATE(_map_widget)],
822 TRUE, _scale_rect.x, _scale_rect.y,
825 gdk_draw_rectangle(_map_widget->window,
827 fg_gc[GTK_WIDGET_STATE(_map_widget)],
828 FALSE, _scale_rect.x, _scale_rect.y,
832 /* Now calculate and draw the distance. */
833 unit2latlon(_center.unitx - pixel2unit(SCALE_WIDTH / 2 - 4), _center.unity, lat1, lon1);
834 unit2latlon(_center.unitx + pixel2unit(SCALE_WIDTH / 2 - 4), _center.unity, lat2, lon2);
835 distance = calculate_distance(lat1, lon1, lat2, lon2) * UNITS_CONVERT[_units];
838 snprintf(buffer, sizeof(buffer), "%0.2f %s", distance, UNITS_TEXT[_units]);
839 else if (distance < 10.f)
840 snprintf(buffer, sizeof(buffer), "%0.1f %s", distance, UNITS_TEXT[_units]);
842 snprintf(buffer, sizeof(buffer), "%0.f %s", distance, UNITS_TEXT[_units]);
844 pango_layout_set_text(_scale_layout, buffer, -1);
845 pango_layout_get_pixel_size(_scale_layout, &width, NULL);
847 /* Draw the layout itself. */
848 gdk_draw_layout(_map_widget->window,
850 fg_gc[GTK_WIDGET_STATE(_map_widget)],
852 (_scale_rect.width - width) / 2,
853 _scale_rect.y, _scale_layout);
855 /* Draw little hashes on the ends. */
856 gdk_draw_line(_map_widget->window,
858 fg_gc[GTK_WIDGET_STATE(_map_widget)],
861 _scale_rect.height / 2 - 4,
864 _scale_rect.height / 2 + 4);
865 gdk_draw_line(_map_widget->window,
867 fg_gc[GTK_WIDGET_STATE(_map_widget)],
870 _scale_rect.height / 2,
872 (_scale_rect.width - width) / 2 - 4,
874 _scale_rect.height / 2);
875 gdk_draw_line(_map_widget->window,
877 fg_gc[GTK_WIDGET_STATE(_map_widget)],
879 _scale_rect.width - 4,
881 _scale_rect.height / 2 - 4,
883 _scale_rect.width - 4,
885 _scale_rect.height / 2 + 4);
886 gdk_draw_line(_map_widget->window,
888 fg_gc[GTK_WIDGET_STATE(_map_widget)],
890 _scale_rect.width - 4,
892 _scale_rect.height / 2,
894 (_scale_rect.width + width) / 2 + 4,
896 _scale_rect.height / 2);
901 map_cb_expose(GtkWidget * widget, GdkEventExpose * event)
903 gdk_draw_drawable(GDK_DRAWABLE(_map_widget->window),
906 event->area.x + _offsetx, event->area.y + _offsety,
907 event->area.x, event->area.y,
908 event->area.width, event->area.height);
911 /* Draw scale, if necessary. */
913 map_scale_draw(event);
924 if ((nzoom > 0) && (nzoom < MAX_ZOOM - 1)) {
937 if (zoom_timeout_sid==0)
940 z=(z+_gps.speed+1)/5;
941 if (z>5) z=5.0; else if (z<1) z=1.0;
944 g_printf("Setting autozoom to: %f %d S:%f\n", z, iz, _gps.speed);
957 map_set_autozoom(gboolean az)
960 MACRO_BANNER_SHOW_INFO(_window, "Autozoom enabled");
961 zoom_timeout_sid=g_timeout_add(_gps.speed<5 ? 2000 : 5000, (GSourceFunc) map_autozoomer, NULL);
964 if (zoom_timeout_sid) {
965 g_source_remove(zoom_timeout_sid);
967 MACRO_BANNER_SHOW_INFO(_window, "Autozoom disabled");
975 map_draw_route(gint x, gint y)
977 cmenu_route_add_way(x, y);
981 map_draw_track(gint x, gint y)
983 _pos.unitx = x2unit((gint) (x + 0.5));
984 _pos.unity = y2unit((gint) (y + 0.5));
985 unit2latlon(_pos.unitx, _pos.unity, _gps.lat, _gps.lon);
987 integerize_data(&_gps, &_pos);
988 track_add(time(NULL), FALSE);
993 map_set_place_information(osm_way *s, osm_place *mp, osm_place *sp)
997 if (!s && !mp && !sp) {
998 snprintf(buffer, sizeof(buffer), _("Unknown location"));
1001 snprintf(buffer, sizeof(buffer), "%s%s%s%s%s%s%s%s%s%s",
1002 s ? s->name ? s->name : "" : "",
1003 s ? s->ref ? ", " : "" : "",
1004 s ? s->ref ? s->ref : "" : "",
1005 s ? s->int_ref ? " (" : "" : "",
1006 s ? s->int_ref ? s->int_ref : "" : "",
1007 s ? s->int_ref ? ") " : "" : "",
1008 (sp && sp->name) ? " in " : "",
1009 (sp && sp->name) ? sp->name : "",
1010 (mp && mp->name) ? " in " : "",
1011 (mp && mp->name) ? mp->name : "");
1013 gtk_label_set_label(GTK_LABEL(info_banner.location), buffer);
1016 #define KM10KNOTS 5.39956803
1019 map_update_destination(gdouble lat, gdouble lon)
1023 static gdouble prev_dt=99999.0;
1024 static gboolean dest_reached=FALSE;
1028 dt=calculate_distance(lat, lon, _dest.lat, _dest.lon);
1029 dh=calculate_course(lat, lon, _dest.lat, _dest.lon);
1030 snprintf(buffer, sizeof(buffer), "%.02f %s (%0.02f)", dt * UNITS_CONVERT[_units], UNITS_TEXT[_units], dh<0 ? 360+dh : dh);
1031 gtk_label_set_label(GTK_LABEL(info_banner.distance), buffer);
1032 if (dt<0.005 && dest_reached==FALSE) {
1034 speak_text("You have reached your destination.");
1036 } else if (dt<prev_dt-KM10KNOTS) {
1037 if (_center_mode>0) {
1038 snprintf(buffer, sizeof(buffer), "Distance to destination: %.02f %s", dt * UNITS_CONVERT[_units], UNITS_TEXT[_units]);
1042 } else if (dt>prev_dt+KM10KNOTS/4) {
1049 g_printf("%f (Prev:%f)\n", prev_dt, dt);
1054 gtk_label_set_label(GTK_LABEL(info_banner.distance), "");
1056 gtk_compass_set_dest_heading(_gps_compass, _dest.valid, (gfloat)dh);
1057 gtk_compass_set_dest_heading(_tab_compass, _dest.valid, (gfloat)dh);
1059 if (_next_way && _next_way->point) {
1060 gdouble wp_lat, wp_lon;
1063 unit2latlon(_next_way->point->unitx,_next_way->point->unity, wp_lat, wp_lon);
1064 wc=calculate_course(lat, lon, wp_lat, wp_lon);
1065 gtk_compass_set_way_heading(_gps_compass, TRUE, wc);
1066 gtk_compass_set_way_heading(_tab_compass, TRUE, wc);
1068 gtk_compass_set_way_heading(_gps_compass, FALSE, 0);
1069 gtk_compass_set_way_heading(_tab_compass, FALSE, 0);
1075 map_update_location(gdouble lat, gdouble lon, gboolean force)
1078 static gboolean inp=FALSE;
1080 /* We run the gtk mainloop in progress callback so we can be called again, we don't like that */
1085 ilat=lat2mp_int(lat);
1086 ilon=lon2mp_int(lon);
1088 if (_gps.fix>1 && !force)
1089 osm_set_way_range_from_speed(_gps.speed);
1091 osm_set_way_range(OSM_RANGE_WAY/4);
1093 osm_progress_set_widget(_db, _progress_item);
1094 _map_location_known=osm_get_location_data(ilat, ilon, &map_loc);
1095 _map_location_dist=map_loc.street ? map_loc.street->dist : 900000.0;
1097 map_set_place_information(map_loc.street, map_loc.primary, map_loc.secondary);
1099 map_set_place_information(NULL, NULL, NULL);
1100 osm_progress_set_widget(_db, NULL);
1102 map_update_destination(lat, lon);
1108 * Mouse scroller zoom in/out callback
1111 map_cb_scroll_event(GtkWidget * widget, GdkEventScroll * event)
1113 if (event->direction == GDK_SCROLL_UP) {
1114 map_center_unit(x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
1115 map_set_zoom(_zoom - 1);
1116 } else if (event->direction == GDK_SCROLL_DOWN) {
1117 map_center_unit(x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
1118 map_set_zoom(_zoom + 1);
1124 * Start map drag operation
1127 map_drag_start(gint x,gint y)
1131 before[0] = press[0];
1132 before[1] = press[1];
1133 _center_mode=CENTER_MANUAL;
1135 g_signal_handler_disconnect(G_OBJECT(_map_widget), map_drag_id);
1137 map_drag_id=g_signal_connect(G_OBJECT(_map_widget), "motion_notify_event", G_CALLBACK(map_follow_move_cb), NULL);
1141 * Stop map drag operation
1144 map_drag_stop(gint x, gint y)
1149 g_signal_handler_disconnect(G_OBJECT(_map_widget), map_drag_id);
1162 /* Workaround hildon content menu problem */
1164 map_cb_show_poi_info_dialog(gpointer data)
1166 guint poi_id=GPOINTER_TO_INT(data);
1167 if (poi_info_dialog(poi_id)==FALSE)
1168 g_printerr("Huh? Failed to display info dialog\n");
1173 map_cb_button_press(GtkWidget * widget, GdkEventButton * event)
1179 _cmenu_position_x = event->x + 0.5;
1180 _cmenu_position_y = event->y + 0.5;
1182 g_printf("BtnPress\n");
1183 switch (event->button) {
1185 if (event->type==GDK_2BUTTON_PRESS) {
1186 map_center_unit(x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
1187 map_set_zoom(_zoom-1);
1188 map_data_needs_refresh=TRUE;
1191 if (event->type==GDK_3BUTTON_PRESS) {
1195 ux=x2unit(_cmenu_position_x);
1196 uy=y2unit(_cmenu_position_y);
1198 unit2latlon(ux, uy, lat, lon);
1199 if (map_poi_find_at_latlon(lat, lon, &poi_id)==TRUE) {
1200 g_printf("POI: %d\n", poi_id);
1201 g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_cb_show_poi_info_dialog, GINT_TO_POINTER(poi_id), NULL);
1203 map_drag_start(event->x, event->y);
1208 map_set_zoom(_zoom - 1);
1212 gtk_menu_popup(GTK_MENU(_menu_map), NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time());
1216 /* Return FALSE to allow context menu to work. */
1221 map_cb_button_release(GtkWidget *widget, GdkEventButton *event)
1223 g_printf("BtnRelease\n");
1224 switch (event->button) {
1227 set_action_activate("autocenter_none", TRUE);
1230 case MAP_MODE_DRAW_TRACK:
1231 map_draw_track(event->x, event->y);
1233 case MAP_MODE_DRAW_ROUTE:
1234 map_draw_route(event->x, event->y);
1236 case MAP_MODE_SET_ROUTE_FROM:
1237 case MAP_MODE_SET_ROUTE_POINT:
1238 case MAP_MODE_SET_ROUTE_TO:
1241 map_drag_stop(event->x, event->y);
1242 if (map_data_needs_refresh==TRUE) {
1243 map_data_needs_refresh=FALSE;
1245 g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_update_location_from_center, NULL, NULL);
1257 /* Return FALSE to avoid context menu (if it hasn't popped up already). */