google maps API 3 zooms out on fitBounds

This problem of Google Map API was discussed few times. See:

  • gmaps-api-issues
  • google-maps-js-api-v3 group

Function map.fitBounds() sets the viewport to contain the bounds. But map.getBounds() function returns the viewport bounds with some extra margin (note that this is normal behaviour and no bug). The enlarged bounds no longer fits in the previous viewport, so the map zooms out.

A simple solution to your problem is to use the map center and the zoom level instead. See functions map.getCenter(), map.setCenter(), map.getZoom() and map.setZoom().


For people seeing this question in 2018, Google Maps API v3 now supports a second padding argument that specifies the amount of padding to include around the bounds you specify.

To avoid zooming out, simply just call fitBounds with a padding of 0. For this example it would be map.fitBounds(map.getBounds(), 0)


Fixing this problem in 2017

Shrink the bounding box slightly before sending it to map.fitBounds().

Here's a function to shrink a bounding box:

// Values between 0.08 and 0.28 seem to work
// Any lower and the map will zoom out too much
// Any higher and it will zoom in too much
var BOUNDS_SHRINK_PERCENTAGE = 0.08; // 8%

/**
 * @param  {google.maps.LatLngLiteral} southwest
 * @param  {google.maps.LatLngLiteral} northeast
 * @return {Array[google.maps.LatLngLiteral]}
 */
function slightlySmallerBounds(southwest, northeast) {
  function adjustmentAmount(value1, value2) {
    return Math.abs(value1 - value2) * BOUNDS_SHRINK_PERCENTAGE;
  }

  var latAdjustment = adjustmentAmount(northeast.lat, southwest.lat);
  var lngAdjustment = adjustmentAmount(northeast.lng, southwest.lng);

  return [
    {lat: southwest.lat + latAdjustment, lng: southwest.lng + lngAdjustment},
    {lat: northeast.lat - latAdjustment, lng: northeast.lng - lngAdjustment}
  ]
}

Use it like this:

// Ensure `southwest` and `northwest` are objects in google.maps.LatLngLiteral form:
// southwest == {lat: 32.79712, lng: -117.13931}
// northwest == {lat: 32.85020, lng: -117.09356}

var shrunkBounds = slightlySmallerBounds(southwest, northeast);
var newSouthwest = shrunkBounds[0];
var newNortheast = shrunkBounds[1];

// `map` is a `google.maps.Map`
map.fitBounds(
  new google.maps.LatLngBounds(newSouthwest, newNortheast)
);

I have an app doing the same thing as codr: saving the current map bounds in the URL, and initializing the map from the URL bounds upon refresh. This solution works wonderfully for that.

Why does this work?

Google Maps essentially does this when fitBounds() is called:

  1. Center the map on the centerpoint of the given bounding box.
  2. Zoom in to the highest zoom level where the viewport would contain the given bounds inside of the viewport box.

If the bounds given would exactly match the viewport, Google Maps doesn't consider that as "containing" the bounds, so it zooms out one more level.