[libshumate] map-layer: Implement continuous zoom



commit 96b55dbfea5ac6cab81824b7a14bd92b1dfe8be2
Author: James Westman <james jwestman net>
Date:   Tue May 4 14:28:27 2021 -0500

    map-layer: Implement continuous zoom
    
    The map layer now accepts fractional zoom levels by scaling the entire
    map layer with gtk_snapshot_scale().

 shumate/shumate-map-layer.c  | 25 ++++++++++++++++++++++---
 shumate/shumate-map-source.c | 22 +++++++++++++---------
 shumate/shumate-scale.c      |  2 +-
 shumate/shumate-view.c       |  8 ++++----
 shumate/shumate-viewport.c   | 20 +++++++++++++-------
 5 files changed, 53 insertions(+), 24 deletions(-)
---
diff --git a/shumate/shumate-map-layer.c b/shumate/shumate-map-layer.c
index 13fb7bf..7cdabb1 100644
--- a/shumate/shumate-map-layer.c
+++ b/shumate/shumate-map-layer.c
@@ -501,7 +501,7 @@ shumate_map_layer_size_allocate (GtkWidget *widget,
 
   viewport = shumate_layer_get_viewport (SHUMATE_LAYER (self));
   tile_size = shumate_map_source_get_tile_size (self->map_source);
-  zoom_level = shumate_viewport_get_zoom_level (viewport);
+  zoom_level = (guint) shumate_viewport_get_zoom_level (viewport);
   latitude = shumate_location_get_latitude (SHUMATE_LOCATION (viewport));
   longitude = shumate_location_get_longitude (SHUMATE_LOCATION (viewport));
   latitude_y = (guint) shumate_map_source_get_y (self->map_source, zoom_level, latitude);
@@ -606,13 +606,13 @@ shumate_map_layer_measure (GtkWidget      *widget,
   if (natural)
     {
       ShumateViewport *viewport;
-      guint tile_size;
+      double tile_size;
       guint zoom_level;
       guint count;
 
       viewport = shumate_layer_get_viewport (SHUMATE_LAYER (self));
-      tile_size = shumate_map_source_get_tile_size (self->map_source);
       zoom_level = shumate_viewport_get_zoom_level (viewport);
+      tile_size = shumate_map_source_get_tile_size (self->map_source) * (fmod (zoom_level, 1.0) + 1.0);
       if (orientation == GTK_ORIENTATION_HORIZONTAL)
         count = shumate_map_source_get_column_count (self->map_source, zoom_level);
       else
@@ -622,6 +622,24 @@ shumate_map_layer_measure (GtkWidget      *widget,
     }
 }
 
+static void
+shumate_map_layer_snapshot (GtkWidget *widget, GtkSnapshot *snapshot)
+{
+  ShumateMapLayer *self = SHUMATE_MAP_LAYER (widget);
+  ShumateViewport *viewport = shumate_layer_get_viewport (SHUMATE_LAYER (self));
+  double zoom_level = shumate_viewport_get_zoom_level (viewport);
+  double extra_zoom = fmod (zoom_level, 1.0) + 1.0;
+  int width = gtk_widget_get_width (GTK_WIDGET (self));
+  int height = gtk_widget_get_height (GTK_WIDGET (self));
+
+  /* Scale around the center of the view */
+  gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (width / 2.0, height / 2.0));
+  gtk_snapshot_scale (snapshot, extra_zoom, extra_zoom);
+  gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (-width / 2.0, -height / 2.0));
+
+  GTK_WIDGET_CLASS (shumate_map_layer_parent_class)->snapshot (widget, snapshot);
+}
+
 static void
 shumate_map_layer_class_init (ShumateMapLayerClass *klass)
 {
@@ -634,6 +652,7 @@ shumate_map_layer_class_init (ShumateMapLayerClass *klass)
   object_class->constructed = shumate_map_layer_constructed;
 
   widget_class->size_allocate = shumate_map_layer_size_allocate;
+  widget_class->snapshot = shumate_map_layer_snapshot;
   widget_class->measure = shumate_map_layer_measure;
 
   obj_properties[PROP_MAP_SOURCE] =
diff --git a/shumate/shumate-map-source.c b/shumate/shumate-map-source.c
index 66571b1..e04cc67 100644
--- a/shumate/shumate-map-source.c
+++ b/shumate/shumate-map-source.c
@@ -52,6 +52,12 @@ shumate_map_source_class_init (ShumateMapSourceClass *klass)
   klass->fill_tile_async = NULL;
 }
 
+static double
+map_size (ShumateMapSource *self, double zoom_level)
+{
+  return shumate_map_source_get_column_count (self, zoom_level) * shumate_map_source_get_tile_size (self) * 
(fmod (zoom_level, 1.0) + 1.0);
+}
+
 
 static void
 shumate_map_source_init (ShumateMapSource *map_source)
@@ -215,7 +221,7 @@ shumate_map_source_get_x (ShumateMapSource *map_source,
   longitude = CLAMP (longitude, SHUMATE_MIN_LONGITUDE, SHUMATE_MAX_LONGITUDE);
 
   /* FIXME: support other projections */
-  return ((longitude + 180.0) / 360.0) * shumate_map_source_get_tile_size (map_source) * pow (2.0, 
zoom_level);
+  return ((longitude + 180.0) / 360.0) * map_size (map_source, zoom_level);
 }
 
 
@@ -242,7 +248,7 @@ shumate_map_source_get_y (ShumateMapSource *map_source,
   latitude = CLAMP (latitude, SHUMATE_MIN_LATITUDE, SHUMATE_MAX_LATITUDE);
   /* FIXME: support other projections */
   sin_latitude = sin (latitude * G_PI / 180.0);
-  return (0.5 - log ((1.0 + sin_latitude) / (1.0 - sin_latitude)) / (4.0 * G_PI)) * 
shumate_map_source_get_tile_size (map_source) * pow (2.0, zoom_level);
+  return (0.5 - log ((1.0 + sin_latitude) / (1.0 - sin_latitude)) / (4.0 * G_PI)) * map_size (map_source, 
zoom_level);
 }
 
 
@@ -265,9 +271,9 @@ shumate_map_source_get_longitude (ShumateMapSource *map_source,
   double longitude;
 
   g_return_val_if_fail (SHUMATE_IS_MAP_SOURCE (map_source), 0.0);
+
   /* FIXME: support other projections */
-  double dx = x / (double) shumate_map_source_get_tile_size (map_source);
-  longitude = dx / pow (2.0, zoom_level) * 360.0 - 180.0;
+  longitude = x / map_size (map_source, zoom_level) * 360.0 - 180.0;
 
   return CLAMP (longitude, SHUMATE_MIN_LONGITUDE, SHUMATE_MAX_LONGITUDE);
 }
@@ -289,12 +295,11 @@ shumate_map_source_get_latitude (ShumateMapSource *map_source,
     double zoom_level,
     double y)
 {
-  double latitude, map_size, dy;
+  double latitude, dy;
 
   g_return_val_if_fail (SHUMATE_IS_MAP_SOURCE (map_source), 0.0);
   /* FIXME: support other projections */
-  map_size = shumate_map_source_get_tile_size (map_source) * shumate_map_source_get_row_count (map_source, 
zoom_level);
-  dy = 0.5 - y / map_size;
+  dy = 0.5 - y / map_size (map_source, zoom_level);
   latitude = 90.0 - 360.0 / G_PI * atan (exp (-dy * 2.0 * G_PI));
   
   return CLAMP (latitude, SHUMATE_MIN_LATITUDE, SHUMATE_MAX_LATITUDE);
@@ -368,9 +373,8 @@ shumate_map_source_get_meters_per_pixel (ShumateMapSource *map_source,
    * radius_at_latitude = 2pi * k * sin (pi/2-theta)
    */
 
-  double map_size = shumate_map_source_get_tile_size (map_source) * shumate_map_source_get_row_count 
(map_source, zoom_level);
   /* FIXME: support other projections */
-  return 2.0 * G_PI * EARTH_RADIUS * sin (G_PI / 2.0 - G_PI / 180.0 * latitude) / map_size;
+  return 2.0 * G_PI * EARTH_RADIUS * sin (G_PI / 2.0 - G_PI / 180.0 * latitude) / map_size (map_source, 
zoom_level);
 }
 
 
diff --git a/shumate/shumate-scale.c b/shumate/shumate-scale.c
index ff91d0f..9cef5a1 100644
--- a/shumate/shumate-scale.c
+++ b/shumate/shumate-scale.c
@@ -91,7 +91,7 @@ shumate_scale_compute_length (ShumateScale *self,
                               gboolean     *out_is_small_unit)
 {
   ShumateMapSource *map_source;
-  int zoom_level;
+  double zoom_level;
   double lat, lon;
   float scale_width;
   float base;
diff --git a/shumate/shumate-view.c b/shumate/shumate-view.c
index fff54ba..cb04afd 100644
--- a/shumate/shumate-view.c
+++ b/shumate/shumate-view.c
@@ -248,8 +248,8 @@ move_viewport_from_pixel_offset (ShumateView *self,
   ShumateMapSource *map_source;
   double x, y;
   double lat, lon;
-  guint zoom_level;
-  guint tile_size, max_x, max_y;
+  double zoom_level;
+  double tile_size, max_x, max_y;
 
   g_assert (SHUMATE_IS_VIEW (self));
 
@@ -261,7 +261,7 @@ move_viewport_from_pixel_offset (ShumateView *self,
   x = shumate_map_source_get_x (map_source, zoom_level, longitude) - offset_x;
   y = shumate_map_source_get_y (map_source, zoom_level, latitude) - offset_y;
 
-  tile_size = shumate_map_source_get_tile_size (map_source);
+  tile_size = shumate_map_source_get_tile_size (map_source) * (fmod (zoom_level, 1.0) + 1.0);
   max_x = shumate_map_source_get_column_count (map_source, zoom_level) * tile_size;
   max_y = shumate_map_source_get_row_count (map_source, zoom_level) * tile_size;
 
@@ -544,7 +544,7 @@ on_scroll_controller_scroll (ShumateView              *self,
       double scroll_map_x, scroll_map_y;
       double view_center_x, view_center_y;
       double x_offset, y_offset;
-      guint zoom_level;
+      double zoom_level;
 
       scroll_map_x = shumate_viewport_longitude_to_widget_x (priv->viewport, GTK_WIDGET (self), 
scroll_longitude);
       scroll_map_y = shumate_viewport_latitude_to_widget_y (priv->viewport, GTK_WIDGET (self), 
scroll_latitude);
diff --git a/shumate/shumate-viewport.c b/shumate/shumate-viewport.c
index 8b18004..bbfea60 100644
--- a/shumate/shumate-viewport.c
+++ b/shumate/shumate-viewport.c
@@ -411,29 +411,35 @@ shumate_viewport_get_min_zoom_level (ShumateViewport *self)
  * shumate_viewport_zoom_in:
  * @self: a #ShumateViewport
  *
- * Increments the zoom level
+ * Increases the zoom level
  */
 void shumate_viewport_zoom_in (ShumateViewport *self)
 {
+  double zoom_level;
+
   g_return_if_fail (SHUMATE_IS_VIEWPORT (self));
 
-  shumate_viewport_set_zoom_level (self, self->zoom_level + 1);
+  /* Round to the nearest 1/5 of a zoom level to prevent floating point
+   * error accumulation. */
+
+  zoom_level = roundf ((self->zoom_level + 0.2) * 5) / 5;
+  shumate_viewport_set_zoom_level (self, zoom_level);
 }
 
 /**
  * shumate_viewport_zoom_out:
  * @self: a #ShumateViewport
  *
- * Decrements the zoom level
+ * Decreases the zoom level
  */
 void shumate_viewport_zoom_out (ShumateViewport *self)
 {
-  g_return_if_fail (SHUMATE_IS_VIEWPORT (self));
+  double zoom_level;
 
-  if (self->zoom_level == 0)
-    return;
+  g_return_if_fail (SHUMATE_IS_VIEWPORT (self));
 
-  shumate_viewport_set_zoom_level (self, self->zoom_level - 1);
+  zoom_level = roundf ((self->zoom_level - 0.2) * 5) / 5;
+  shumate_viewport_set_zoom_level (self, zoom_level);
 }
 
 /**


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]