#include "announcements.h"
#include "gtkcompass.h"
#include "dialogs.h"
+#include "image-cache.h"
#define DEBUG_MAP_TIME 1
+#define MAP_THUMB_MARGIN_X (100)
+#define MAP_THUMB_MARGIN_Y (75)
+
/* Initial size */
#define BUF_WIDTH_TILES (4)
#define BUF_HEIGHT_TILES (3)
/** The backing pixmap of map_widget. */
static GdkPixmap *map_pixmap;
+/* Tile cache, this might need some adjustment */
+#define MAP_CACHE_MAX (64)
+static ImageCache *map_ic;
+
/** The "base tile" is the upper-left tile in the pixmap. */
guint _base_tilex = -5;
guint _base_tiley = -5;
+static guint map_max_zoom=MAX_ZOOM;
guint _zoom = 3; /* zoom level, from 0 to MAX_ZOOM. */
Point _min_center = { -1, -1 };
static guint release[2] = { 0, 0 };
static guint before[2] = { 0, 0 };
static guint map_drag_id = 0;
+static gboolean map_dragged;
/** VARIABLES FOR ACCESSING THE LOCATION/BOUNDS OF THE CURRENT MARK. */
static gint mark_x1;
#define KM10KNOTS (5.39956803)
-void map_render_paths(void);
-void map_force_redraw(void);
-void map_draw_position_icon(Position *pos);
-
static void map_update_location(gdouble lat, gdouble lon, gboolean force);
static void map_speed_draw(void);
static gboolean map_cb_after_realize(GtkWidget *map_widget, gpointer data);
map_widget=gtk_drawing_area_new();
map_timer=g_timer_new();
+map_ic=image_cache_new(MAP_CACHE_MAX);
+
+gtk_widget_set_extension_events(GTK_WIDGET(map_widget), GDK_EXTENSION_EVENTS_ALL);
+
#ifdef WITH_GL
map_gl_config=gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH);
if (map_gl_config) {
g_signal_connect(G_OBJECT(map_widget), "scroll_event", G_CALLBACK(map_cb_scroll_event), NULL);
gtk_widget_add_events(map_widget, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
+ | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
map_poi_init(map_widget);
tw=TILE_SIZE_PIXELS*((widget->allocation.width/TILE_SIZE_PIXELS)+2);
th=TILE_SIZE_PIXELS*((widget->allocation.height/TILE_SIZE_PIXELS)+2);
-g_debug("MAP: configure %d %d -> (%d,%d)", widget->allocation.width, widget->allocation.height, tw, th);
-
if (map_pixmap==NULL) {
- g_debug("Initial buffer pixmap");
map_pixmap=gdk_pixmap_new(widget->window, tw, th, -1);
} else {
if (tw>buf_width_pixels || th>buf_height_pixels) {
- g_debug("MC: Large buffer");
g_object_unref(map_pixmap);
map_pixmap=gdk_pixmap_new(widget->window, tw, th, -1);
- } else if (tw<buf_width_pixels-TILE_SIZE_PIXELS || th<buf_height_pixels-TILE_SIZE_PIXELS) {
- g_debug("MC: Small buffer");
+ } else if (tw<buf_width_pixels-(TILE_SIZE_PIXELS*2) || th<buf_height_pixels-(TILE_SIZE_PIXELS*2)) {
g_object_unref(map_pixmap);
map_pixmap=gdk_pixmap_new(widget->window, tw, th, -1);
- } else {
- g_debug("MC: No change ?");
}
}
+g_assert(map_pixmap);
buf_width_pixels=tw;
buf_height_pixels=th;
scale_rect.x = (_screen_width_pixels - SCALE_WIDTH) / 2;
scale_rect.width = SCALE_WIDTH;
-MACRO_RECALC_FOCUS_BASE();
-MACRO_RECALC_FOCUS_SIZE();
+MACRO_RECALC_FOCUS_BASE(_center_ratio);
+MACRO_RECALC_FOCUS_SIZE(_center_ratio);
_min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth));
_min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight));
* this method, but I guess it's not general-purpose enough.
*/
static void
-map_pixbuf_scale_inplace(GdkPixbuf * pixbuf, guint ratio_p2, guint src_x, guint src_y)
+map_pixbuf_scale_inplace(GdkPixbuf *pixbuf, guint ratio_p2, guint src_x, guint src_y)
{
guint dest_x = 0, dest_y = 0, dest_dim = TILE_SIZE_PIXELS;
guint rowstride = gdk_pixbuf_get_rowstride(pixbuf);
GdkPixbuf *mpixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(pixbuf),
8, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
+g_debug("TRIM!!");
gdk_pixbuf_copy_area(pixbuf,
(gdk_pixbuf_get_width(pixbuf) - TILE_SIZE_PIXELS) / 2,
(gdk_pixbuf_get_height(pixbuf) - TILE_SIZE_PIXELS) / 2,
return map_pixmap;
}
-
static GdkPixbuf *
map_tile_load(guint tilex, guint tiley, gint zoff, gboolean download)
{
GdkPixbuf *pixbuf;
GError *error = NULL;
gchar buffer[BUFFER_SIZE];
+gchar key[BUFFER_SIZE];
struct stat tstat;
gint se;
g_snprintf(buffer, sizeof(buffer), "%s/%u/%u/%u.jpg", _curr_repo->cache_dir, _zoom + zoff, (tilex >> zoff), (tiley >> zoff));
+g_snprintf(key, sizeof(key), "%s/%u/%u/%u", _curr_repo->cache_dir, _zoom + zoff, (tilex >> zoff), (tiley >> zoff));
-pixbuf=gdk_pixbuf_new_from_file(buffer, &error);
-if (error || !pixbuf) {
+pixbuf=image_cache_get(map_ic, key, buffer);
+if (!pixbuf) {
g_unlink(buffer);
if (_auto_download && _curr_repo->type != REPOTYPE_NONE && !((_zoom + zoff - (_curr_repo->double_size ? 1 : 0)) % _curr_repo->dl_zoom_steps)) {
if (download)
map_initiate_download(tilex >> zoff, tiley >> zoff, _zoom + zoff, -INITIAL_DOWNLOAD_RETRIES);
}
-
return NULL;
}
+g_object_ref(pixbuf);
+
+#if 0
+/* Check if we need to trim. */
+if (gdk_pixbuf_get_width(pixbuf) != TILE_SIZE_PIXELS || gdk_pixbuf_get_height(pixbuf) != TILE_SIZE_PIXELS)
+ pixbuf=pixbuf_trim(pixbuf);
+#endif
+
/* Check tile age, if file date is ower a week old, redownload if autodownload enabled */
se=stat(buffer, &tstat);
if (se==0) {
if (download) {
g_debug("Tile: %s is old, re-downloading\n", buffer);
map_initiate_download(tilex >> zoff, tiley >> zoff, _zoom + zoff, -INITIAL_DOWNLOAD_RETRIES);
+ image_cache_invalidate(map_ic, key);
}
}
}
GdkPixbuf *pixbuf=NULL;
gint zoff;
-g_debug("MAP RT: %d %d %d %d (%d, %d)", tilex, tiley, destx, desty, buf_width_tiles, buf_height_tiles);
-
if (destx > buf_width_pixels || desty > buf_height_pixels)
return FALSE;
-if (tilex<_world_size_tiles && tiley<_world_size_tiles) {
- /* The tile is possible. */
- for (zoff = (_curr_repo->double_size ? 1 : 0); !pixbuf && (_zoom + zoff) <= MAX_ZOOM && zoff <= TILE_SIZE_P2; zoff += 1) {
- pixbuf=map_tile_load(tilex, tiley, zoff, !fast_fail);
- if (!pixbuf) {
- if (!fast_fail)
- fast_fail=TRUE;
- } else {
- /* Check if we need to trim. */
- if (gdk_pixbuf_get_width(pixbuf) != TILE_SIZE_PIXELS || gdk_pixbuf_get_height(pixbuf) != TILE_SIZE_PIXELS)
- pixbuf = pixbuf_trim(pixbuf);
-
- /* Check if we need to blit. */
- if (zoff) {
- map_pixbuf_scale_inplace(pixbuf, zoff,
- (tilex - ((tilex >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff),
- (tiley - ((tiley >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff));
- }
+if (tilex > _world_size_tiles || tiley > _world_size_tiles)
+ return FALSE;
+
+g_debug("MAP RT: %u %u (%u) %u %u (%u, %u)", tilex, tiley, _world_size_tiles, destx, desty, buf_width_tiles, buf_height_tiles);
+
+/* The tile is possible. */
+for (zoff = (_curr_repo->double_size ? 1 : 0); !pixbuf && (_zoom + zoff) <= map_max_zoom && zoff <= TILE_SIZE_P2; zoff += 1) {
+ pixbuf=map_tile_load(tilex, tiley, zoff, !fast_fail);
+ if (!pixbuf) {
+ if (!fast_fail)
+ fast_fail=TRUE;
+ } else {
+ /* Check if we need to blit. */
+ if (zoff) {
+ map_pixbuf_scale_inplace(pixbuf, zoff,
+ (tilex - ((tilex >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff),
+ (tiley - ((tiley >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff));
+ image_cache_invalidate_by_image(map_ic, pixbuf);
}
}
}
if (map_drag_id>0)
return;
-if (_home.valid)
- map_draw_position_icon(&_home);
if(_show_poi)
map_render_all_pois(buf_width_pixels, buf_height_pixels);
+
+if (_home.valid)
+ map_draw_position_icon(&_home, "home");
+
+if (_dest.valid)
+ map_draw_position_icon(&_dest, "destination");
+
if(_show_tracks>0)
map_render_paths();
}
map_force_redraw(void)
{
guint new_x, new_y;
-#ifdef DEBUG_MAP_TIME
+#ifdef DEBUG
gulong tms;
g_timer_start(map_timer);
map_render_tile(_base_tilex + new_x, _base_tiley + new_y,
new_x * TILE_SIZE_PIXELS, new_y * TILE_SIZE_PIXELS, FALSE);
}
-#ifdef DEBUG_MAP_TIME
+#ifdef DEBUG
g_timer_stop(map_timer);
g_debug("Full redraw: %f sec\n", g_timer_elapsed(map_timer, &tms));
#endif
+
map_render_data();
MACRO_QUEUE_DRAW_AREA();
}
+guint
+map_get_zoom(void)
+{
+return _zoom;
+}
+
/**
* Set the current zoom level. If the given zoom level is the same as the
* current zoom level, or if the new zoom is invalid
* (not MIN_ZOOM <= new_zoom < MAX_ZOOM), then this method does nothing.
*/
-void
+gboolean
map_set_zoom(guint new_zoom)
{
/* Note that, since new_zoom is a guint and MIN_ZOOM is 0, this if
* condition also checks for new_zoom >= MIN_ZOOM. */
-if (new_zoom > (MAX_ZOOM - 1))
- return;
+if (new_zoom > (map_max_zoom - 1))
+ return FALSE;
if (new_zoom == _zoom)
- return;
+ return FALSE;
_zoom = new_zoom / _curr_repo->view_zoom_steps * _curr_repo->view_zoom_steps;
_world_size_tiles = unit2tile(WORLD_SIZE_UNITS);
/* New zoom level, so we can't reuse the old buffer's pixels. */
/* Update state variables. */
MACRO_RECALC_OFFSET();
-MACRO_RECALC_FOCUS_BASE();
-MACRO_RECALC_FOCUS_SIZE();
+MACRO_RECALC_FOCUS_BASE(_center_ratio);
+MACRO_RECALC_FOCUS_SIZE(_center_ratio);
map_set_mark(&_gps->data);
map_force_redraw();
+return TRUE;
}
/**
base_old_x = base_new_x + new_base_tilex - _base_tilex;
_base_tilex = new_base_tilex;
_base_tiley = new_base_tiley;
+
for (j = 0; j < buf_height_tiles; ++j, new_y += ioy, old_y += ioy) {
new_x = base_new_x;
old_x = base_old_x;
}
MACRO_RECALC_OFFSET();
-MACRO_RECALC_FOCUS_BASE();
+MACRO_RECALC_FOCUS_BASE(_center_ratio);
map_set_mark(&_gps->data);
MACRO_QUEUE_DRAW_AREA();
return FALSE;
}
-/**
- * Draw given pixbuf on map, centered on x,y
- */
void
-map_draw_pixbuf(guint unitx, guint unity, GdkPixbuf *p)
+map_draw_pixbuf_on_buffer(gint x, gint y, GdkPixbuf *p)
{
-gint x,y;
-x = unit2bufx(unitx);
-y = unit2bufy(unity);
+if ((x < 0) || (y < 0))
+ return;
+
+if ((x > buf_width_pixels) || (y > buf_height_pixels))
+ return;
gdk_draw_pixbuf(map_pixmap, _gc[COLORABLE_POI],
p, 0, 0,
}
/**
- * Draw an icon on given Position.
- *
- * XXX: don't hardcode as home.png !
+ * Draw given pixbuf on map, centered on x,y
*/
void
-map_draw_position_icon(Position *pos)
+map_draw_pixbuf(guint unitx, guint unity, GdkPixbuf *p)
{
-guint x,y, x1,y1;
+gint x,y;
+x=unit2bufx(unitx);
+y=unit2bufy(unity);
+
+map_draw_pixbuf_on_buffer(x, y, p);
+}
+
+/**
+ * Draw an icon at given Position.
+ *
+ */
+static void
+map_draw_position_icon(Position *pos, const gchar *icon)
+{
+gint x, y;
+guint ux, uy;
+gchar buffer[128];
GdkPixbuf *p;
-latlon2unit(pos->lat, pos->lon, x, y);
-x1=unit2bufx(x);
-y1=unit2bufy(y);
+latlon2unit(pos->lat, pos->lon, ux, uy);
+x=unit2bufx(ux);
+y=unit2bufy(uy);
-if ((x1 > buf_width_pixels) || (y1 > buf_height_pixels))
+if ((x < 0) || (y < 0))
return;
-p=gdk_pixbuf_new_from_file(DATADIR "/pixmaps/mapper/home.png", NULL);
-if (p) {
- map_draw_pixbuf(x,y,p);
- g_object_unref(p);
-}
+if ((x > buf_width_pixels) || (y > buf_height_pixels))
+ return;
+
+g_snprintf(buffer, sizeof(buffer), "%s/pixmaps/mapper/%s.png", DATADIR, icon);
+p=gdk_pixbuf_new_from_file(buffer, NULL);
+if (!p)
+ return;
+map_draw_pixbuf_on_buffer(x, y, p);
+g_object_unref(p);
}
/**
guint unitx, unity, nunitx, nunity;
guint cx, cy;
-if (!(event->state & GDK_BUTTON1_MASK))
- return FALSE;
+if (!(event->state & GDK_BUTTON1_MASK)) {
+ map_drag_id=0;
+ return TRUE;
+}
if (event->is_hint) {
gdk_window_get_pointer(event->window, &xx, &yy, &state);
map_center_unit(nunitx, nunity);
+map_dragged=TRUE;
g_debug("MAP PAN: %d %d %d %d (%d, %d)", cx, cy, xx, yy, nunitx, nunity);
before[0] = xx;
before[1] = yy;
if (_key_zoom_new < _zoom) {
/* We're currently zooming in (_zoom is decreasing). */
guint test = _key_zoom_new - _curr_repo->view_zoom_steps;
- if (test < MAX_ZOOM)
+ if (test < map_max_zoom)
_key_zoom_new = test;
else
return FALSE;
} else {
/* We're currently zooming out (_zoom is increasing). */
guint test = _key_zoom_new + _curr_repo->view_zoom_steps;
- if (test < MAX_ZOOM)
+ if (test < map_max_zoom)
_key_zoom_new = test;
else
return FALSE;
}
static gboolean
-map_cb_expose(GtkWidget * widget, GdkEventExpose * event)
+map_cb_expose(GtkWidget *widget, GdkEventExpose *event)
{
gdk_draw_drawable(GDK_DRAWABLE(_map_widget->window),
_gc[COLORABLE_MARK],
gint nzoom;
nzoom=_zoom+zdir;
-if ((nzoom > 0) && (nzoom < MAX_ZOOM - 1)) {
+if ((nzoom >= 0) && (nzoom < map_max_zoom - 1)) {
+ image_cache_clear(map_ic);
map_set_zoom(nzoom);
}
return nzoom;
{
static gfloat z=5.0;
static gint last=5;
+gint b=0;
gint iz;
if (zoom_timeout_sid==0)
return FALSE;
+/* If location is known, use road type and speed for zoom setting */
+if (map_loc.valid && map_loc.street) {
+ switch (map_loc.street->type) {
+ case WAY_MOTORWAY:
+ case WAY_TRUNK:
+ b=2;
+ break;
+ case WAY_PRIMARY:
+ case WAY_SECONDARY:
+ b=1;
+ break;
+ default:
+ b=0;
+ break;
+ }
+}
+
z=(z+_gps->data.speed+1)/5;
+z+=b;
if (z>5) z=5.0; else if (z<1) z=1.0;
iz=(gint)roundf(z);
-#ifdef DEBUG
g_debug("Setting autozoom to: %f %d S:%f\n", z, iz, _gps->data.speed);
-#endif
if (iz>last)
iz=last+1;
else if (iz<last)
map_set_autozoom(gboolean az, gfloat speed)
{
if (az==TRUE) {
- MACRO_BANNER_SHOW_INFO(_window, "Autozoom enabled");
zoom_timeout_sid=g_timeout_add(speed<5 ? 2000 : 5000, (GSourceFunc) map_autozoomer, NULL);
+ MACRO_BANNER_SHOW_INFO(_window, "Autozoom enabled");
return;
} else {
if (zoom_timeout_sid) {
_gps->data.speed=20.f;
gps_data_integerize(&_gps->data);
_gps->data.time=time(NULL);
-track_add(&_gps->data);
+track_add(_track, &_gps->data);
map_refresh_mark();
}
announce_destination_reached();
dest_reached=TRUE;
} else if (dt<prev_dt-KM10KNOTS) {
- if (_center_mode>0)
+ if (_center_mode>0 && _announce_destination==TRUE)
announce_distance_to_destination(cdist, UNITS_TEXT[_units], KM10KNOTS);
prev_dt=dt;
} else if (dt>prev_dt+KM10KNOTS/4) {
prev_dt=99999.0;
gtk_label_set_label(GTK_LABEL(info_banner.distance), "");
}
+
gtk_compass_set_dest_heading(_gps_compass, _dest.valid, (gfloat)dh);
gtk_compass_set_dest_heading(_tab_compass, _dest.valid, (gfloat)dh);
static gboolean inp=FALSE;
/* We run the gtk mainloop in progress callback so we can be called again, we don't like that */
-if (inp==TRUE)
+if (inp==TRUE) {
+ g_debug("LOC: Query in progress");
return;
+}
inp=TRUE;
ilat=lat2mp_int(lat);
if (_gps->data.fix>1 && !force)
osm_set_way_range_from_speed(_gps->data.speed);
else
- osm_set_way_range(OSM_RANGE_WAY/8);
-
-osm_progress_set_widget(_db, _progress_item);
-osm_get_location_data(ilat, ilon, _gps->data.heading, &map_loc);
-if (map_loc.valid)
- map_set_place_information(map_loc.street, map_loc.primary, map_loc.secondary);
-else
- map_set_place_information(NULL, NULL, NULL);
-osm_progress_set_widget(_db, NULL);
+ osm_set_way_range(2800);
map_update_destination(lat, lon);
+if (osm_check_location(&map_loc, ilat, ilon)==FALSE) {
+ osm_progress_set_widget(_db, _progress_item);
+ osm_get_location_data(ilat, ilon, _gps->data.heading, &map_loc);
+ if (map_loc.valid)
+ map_set_place_information(map_loc.street, map_loc.primary, map_loc.secondary);
+ else
+ map_set_place_information(NULL, NULL, NULL);
+ osm_progress_set_widget(_db, NULL);
+} else g_debug("IN PLACE");
+
inp=FALSE;
}
_center_mode=CENTER_MANUAL;
if (map_drag_id!=0)
g_signal_handler_disconnect(G_OBJECT(_map_widget), map_drag_id);
-
+map_dragged=FALSE;
map_drag_id=g_signal_connect(G_OBJECT(_map_widget), "motion_notify_event", G_CALLBACK(map_follow_move_cb), NULL);
}
before[0]=0;
before[1]=0;
map_drag_id=0;
-map_force_redraw();
+if (map_dragged==TRUE)
+ map_force_redraw();
}
/* Workaround hildon content menu problem */
map_cb_show_poi_info_dialog(gpointer data)
{
guint poi_id=GPOINTER_TO_INT(data);
-if (poi_info_dialog(poi_id)==FALSE)
+if (poi_info_dialog(_window, poi_id)==FALSE)
g_printerr("Huh? Failed to display info dialog\n");
return FALSE;
}
static gboolean
-map_cb_button_press(GtkWidget * widget, GdkEventButton * event)
+map_cb_button_press(GtkWidget *widget, GdkEventButton *event)
{
_cmenu_position_x = event->x + 0.5;
_cmenu_position_y = event->y + 0.5;
map_set_zoom(_zoom-1);
#endif
map_data_needs_refresh=TRUE;
+ map_drag_stop(event->x, event->y);
return FALSE;
}
if (event->type==GDK_3BUTTON_PRESS) {
+ map_drag_stop(event->x, event->y);
return FALSE;
}
return FALSE;
break;
case 2:
- map_set_zoom(_zoom - 1);
+ /* */
break;
case 3:
#ifndef WITH_HILDON
return FALSE;
}
+static gboolean
+map_pan_click_check(gint x, gint y)
+{
+gint pns=0, pew=0;
+
+if (x<MAP_THUMB_MARGIN_X)
+ pns=-PAN_UNITS;
+else if (x>(_screen_width_pixels-MAP_THUMB_MARGIN_X))
+ pns=PAN_UNITS;
+
+if (y<MAP_THUMB_MARGIN_Y)
+ pew=-PAN_UNITS;
+else if (y>(_screen_height_pixels-MAP_THUMB_MARGIN_Y))
+ pew=PAN_UNITS;
+
+if (pns!=0 || pew!=0) {
+ map_pan(pns, pew);
+ g_debug("MPAN: %d, %d", pns, pew);
+ return TRUE;
+}
+return FALSE;
+}
+
static gboolean
map_cb_button_release(GtkWidget *widget, GdkEventButton *event)
{
switch (event->button) {
case 1:
- if (_center_mode>0)
- set_action_activate("autocenter_none", TRUE);
+
+#if defined(WITH_HILDON_NEW)
+ if (hildon_helper_event_button_is_finger(event)) {
+ g_debug("Thumb down");
+ if (map_pan_click_check(event->x, event->y))
+ return FALSE;
+ }
+#endif
switch (map_mode) {
case MAP_MODE_DRAW_TRACK:
unit2latlon(ux, uy, lat, lon);
if (map_poi_find_at_latlon(lat, lon, &poi_id)==TRUE) {
- g_debug("POI: %d\n", poi_id);
g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_cb_show_poi_info_dialog, GINT_TO_POINTER(poi_id), NULL);
}
if (map_data_needs_refresh==TRUE) {
map_render_data();
g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_update_location_from_center, NULL, NULL);
}
+ if (_center_mode>0)
+ set_action_activate("autocenter_none", TRUE);
map_drag_stop(event->x, event->y);
break;
}
break;
case 2:
- /* */
+ if (map_pan_click_check(event->x, event->y)==FALSE)
+ map_set_zoom(_zoom-1);
+ return FALSE;
break;
case 3:
+ /* */
break;
}