Dynamically Adjust Google Maps Zoom with Xamarin.iOS

One of the challenges when displaying information within a map is determining the right zoom level. The map needs to be zoomed out far enough to allow the user to see a broad set of information but zoomed in close enough to provide enough detail to give the user context.

In our app at Spectafy, getting this right is very important to us. Since the focus of our app is providing user’s with information about things that are going on nearby them, we want to be sure to get that map zoom level just right.

For a user in a large city like San Francisco or New York, a zoom level that shows just a few blocks may show them everything they need. For someone like me who lives in Central Florida, we may need to zoom out a bit further.

To resolve this challenge, we have our app start initially at a reasonably zoomed-in level and then adjust the zoom level dynamically to to include a minimum number of markers.

Determining that appropriate zoom level is pretty straightforward because Google Maps’ zooming changes at a very predictable rate … each reduction in the zoom level (a lower zoom level value is further away) doubles the amount of map that’s visible.

Knowing that, we’re able to determine our perfect zoom level with a method like the following.

float CalculateMarkerInclusiveZoomLevel(
  MapView mapView, List<Marker> markers, 
  int minVisible)
{
 var bounds = 
   new CoordinateBounds(mapView.Projection.VisibleRegion);
 var initialZoomLevel = mapView.Camera.Zoom;
 var markerInclusiveZoomLevel = initialZoomLevel;

 var count = markers.Count(
   m => bounds.ContainsCoordinate(m.Position));

 while (count < markers.Count && count < minVisible)
 {
   // Each zoom level doubles the viewable area
   var latGrowth = 
     (bounds.NorthEast.Latitude - bounds.SouthWest.Latitude) / 2;
   var lngGrowth = 
     (bounds.NorthEast.Longitude - bounds.SouthWest.Longitude) / 2;
   markerInclusiveZoomLevel--;

   bounds = new CoordinateBounds(
      new CLLocationCoordinate2D(
          bounds.NorthEast.Latitude + latGrowth, 
          bounds.NorthEast.Longitude + lngGrowth),
      new CLLocationCoordinate2D(
          bounds.SouthWest.Latitude - latGrowth, 
          bounds.SouthWest.Longitude - lngGrowth));

   count = 
     markers.Count(m => bounds.ContainsCoordinate(m.Position));
 }

return markerInclusiveZoomLevel;
}

The above method accepts in a reference to the MapView being displayed, the list of markers on the map, and the minimum number of markers that should be visible to the user.

The method then gets the bounds of the map that’s visible at the current zoom level and what that zoom level is. From there it’s just simple math.

The app counts how many markers are visible within the current bounds. It then keeps doubling the size of the bounds and decrementing the zoom level (lower zoom level is further away) until the desired number of markers is visible within that bounds(either the value of minVisible or all of the markers if the list contains less then minVisible markers).

We can then use the above method similar to the following

// Assumes _mapView is already visible and contains the list of markers in _markers
var newZoomLevel = 
  CalculateMarkerInclusiveZoomLevel(_mapView, _markers, 10);
_mapView.Animate(
  CameraUpdate.SetTarget(_mapView.Camera.Target, newZoomLevel));

With this code, the map will now animate to the appropriate zoom level to make at least 10 markers visible.

That easily, we’re now able to give the user just what the want … the right amount of information with the right amount of context.

Advertisements