Perhaps you're aware of the center-hopping issue: at certain points, when zooming in the map, the map center swaps between two positions, mostly between zoom levels 17, 18 and 19. (ie @ N0°0.0, E15°24.6065). This is due to a rounding issue computing float variables (6.5 digits accuracy), while the global map needs 7 digits accuracy.
I fixed this bug. But sorry, filing a pull request is over my head. So I post a patch here:
From 348ef5fc230641f442f79297e0be4894e199cf28 Mon Sep 17 00:00:00 2001
From: Thomas Freiherr <Thomas[ dot )Freiherr{ at ]gmx( dot }net>
Date: Fri, 25 Aug 2023 13:20:36 +0200
Subject: [PATCH] fix center hopping
Signed-off-by: Thomas Freiherr
---
src/converter.c | 45 ++++++++++++----------------------------
src/osm-gps-map-widget.c | 16 +++++++-------
2 files changed, 21 insertions(+), 40 deletions(-)
diff --git a/src/converter.c b/src/converter.c
index cfa7093..d822577 100644
--- a/src/converter.c
+++ b/src/converter.c
@@ -9,7 +9,7 @@
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
@@ -55,11 +55,6 @@ int
lat2pixel( int zoom,
float lat)
{
- float lat_m;
- int pixel_y;
-
- lat_m = atanh(sin(lat));
-
/* the formula is
*
* some more notes
@@ -67,11 +62,8 @@ lat2pixel( int zoom,
*
* pixel_y = -(2^zoom * TILESIZE * lat_m) / 2PI + (2^zoom * TILESIZE) / 2
*/
- pixel_y = -(int)( (lat_m * TILESIZE * (1 << zoom) ) / (2*M_PI)) +
- ((1 << zoom) * (TILESIZE/2) );
-
-
- return pixel_y;
+ float y = atanh(sin(lat)) * TILESIZE * (1 << zoom) / (2*M_PI);
+ return -(int)(y) + (((1 << zoom) * TILESIZE) >> 1);
}
@@ -79,41 +71,28 @@ int
lon2pixel( int zoom,
float lon)
{
- int pixel_x;
-
/* the formula is
*
* pixel_x = (2^zoom * TILESIZE * lon) / 2PI + (2^zoom * TILESIZE) / 2
*/
- pixel_x = (int)(( lon * TILESIZE * (1 << zoom) ) / (2*M_PI)) +
- ( (1 << zoom) * (TILESIZE/2) );
- return pixel_x;
+ float x = lon * TILESIZE * (1 << zoom) / (2*M_PI);
+ return (int)(x) + (((1 << zoom) * TILESIZE) >> 1);
}
float
pixel2lon( float zoom,
int pixel_x)
{
- float lon;
-
- lon = ((pixel_x - ( exp(zoom * M_LN2) * (TILESIZE/2) ) ) *2*M_PI) /
- (TILESIZE * exp(zoom * M_LN2) );
-
- return lon;
+ float x = exp(zoom * M_LN2) * TILESIZE;
+ return 2*M_PI * (pixel_x - (x / 2)) / x;
}
float
pixel2lat( float zoom,
int pixel_y)
{
- float lat, lat_m;
-
- lat_m = (-( pixel_y - ( exp(zoom * M_LN2) * (TILESIZE/2) ) ) * (2*M_PI)) /
- (TILESIZE * exp(zoom * M_LN2));
-
- lat = asin(tanh(lat_m));
-
- return lat;
+ float y = exp(zoom * M_LN2) * TILESIZE;
+ return asin(tanh(2*M_PI * ((y / 2) - pixel_y) / y));
}
int
@@ -126,7 +105,9 @@ latlon2zoom(int pix_height,
{
float lat1_m = atanh(sin(lat1));
float lat2_m = atanh(sin(lat2));
- int zoom_lon = LOG2((double)(2 * pix_width * M_PI) / (TILESIZE * (lon2 - lon1)));
- int zoom_lat = LOG2((double)(2 * pix_height * M_PI) / (TILESIZE * (lat2_m - lat1_m)));
+ float d_lon = (TILESIZE * (lon2 - lon1));
+ float d_lat = (TILESIZE * (lat2_m - lat1_m));
+ int zoom_lon = (d_lon) ? LOG2((double)(2*M_PI * pix_width ) / d_lon) : 1;
+ int zoom_lat = (d_lat) ? LOG2((double)(2*M_PI * pix_height) / d_lat) : 1;
return MIN(zoom_lon, zoom_lat);
}
diff --git a/src/osm-gps-map-widget.c b/src/osm-gps-map-widget.c
index e3158cd..a374aab 100644
--- a/src/osm-gps-map-widget.c
+++ b/src/osm-gps-map-widget.c
@@ -1122,8 +1122,8 @@ osm_gps_map_fill_tiles_pixel (OsmGpsMap *map, cairo_t *cr)
tiles_nx = (allocation.width - offset_x) / TILESIZE + 1;
tiles_ny = (allocation.height - offset_y) / TILESIZE + 1;
- tile_x0 = floorf((float)priv->map_x / (float)TILESIZE);
- tile_y0 = floorf((float)priv->map_y / (float)TILESIZE);
+ tile_x0 = priv->map_x / TILESIZE;
+ tile_y0 = priv->map_y / TILESIZE;
for (i=tile_x0; i<(tile_x0+tiles_nx);i++)
{
@@ -1491,10 +1491,10 @@ osm_gps_map_map_redraw (OsmGpsMap *map)
priv->redraw_cycle++;
- /* clear white background */
- w = gtk_widget_get_allocated_width (widget);
- h = gtk_widget_get_allocated_width (widget);
- draw_white_rectangle(cr, 0, 0, w + EXTRA_BORDER * 2, h + EXTRA_BORDER * 2);
+ ///* clear white background */
+ //w = gtk_widget_get_allocated_width (widget);
+ //h = gtk_widget_get_allocated_width (widget);
+ //draw_white_rectangle(cr, 0, 0, w + EXTRA_BORDER * 2, h + EXTRA_BORDER * 2);
osm_gps_map_fill_tiles_pixel(map, cr);
@@ -3117,8 +3117,8 @@ osm_gps_map_set_center (OsmGpsMap *map, float latitude, float longitude)
pixel_x = lon2pixel(priv->map_zoom, priv->center_rlon);
pixel_y = lat2pixel(priv->map_zoom, priv->center_rlat);
- priv->map_x = pixel_x - allocation.width/2;
- priv->map_y = pixel_y - allocation.height/2;
+ priv->map_x = pixel_x - (allocation.width >> 1);
+ priv->map_y = pixel_y - (allocation.height >> 1);
osm_gps_map_map_redraw_idle(map);
--
2.34.1
Perhaps you're aware of the center-hopping issue: at certain points, when zooming in the map, the map center swaps between two positions, mostly between zoom levels 17, 18 and 19. (ie @ N0°0.0, E15°24.6065). This is due to a rounding issue computing float variables (6.5 digits accuracy), while the global map needs 7 digits accuracy.
I fixed this bug. But sorry, filing a pull request is over my head. So I post a patch here: