nzjrs / osm-gps-map

A Gtk+ Widget for Displaying OpenStreetMap tiles LOOKING FOR A NEW MAINTAINER
http://nzjrs.github.com/osm-gps-map
GNU General Public License v2.0
135 stars 58 forks source link

center-hopping #101

Open DTJF opened 1 year ago

DTJF commented 1 year ago

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