Google Maps v3: Enforcing min. zoom level when using fitBounds
Asked Answered
T

9

94

I'm drawing a series of markers on a map (using v3 of the maps api).

In v2, I had the following code:

  bounds = new GLatLngBounds();

  ... loop thru and put markers on map ...
  bounds.extend(point);
  ... end looping

  map.setCenter(bounds.getCenter());
  var level = map.getBoundsZoomLevel(bounds);
  if ( level == 1 ) 
    level = 5;
  map.setZoom(level > 6 ? 6 : level);

And that work fine to ensure that there was always an appropriate level of detail displayed on the map.

I'm trying to duplicate this functionality in v3, but the setZoom and fitBounds don't seem to be cooperating:

... loop thru and put markers on the map
  var ll = new google.maps.LatLng(p.lat,p.lng);
  bounds.extend(ll);
... end loop

var zoom = map.getZoom();
map.setZoom(zoom > 6 ? 6 : zoom);
map.fitBounds(bounds);

I've tried different permutation (moving the fitBounds before the setZoom, for example) but nothing I do with setZoom seems to affect the map. Am I missing something? Is there a way to do this?

Thurstan answered 7/6, 2010 at 13:22 Comment(0)
D
155

At this discussion on Google Groups I discovered that basically when you do a fitBounds, the zoom happens asynchronously so you need to capture the zoom and bounds change event. The code in the final post worked for me with a small modification... as it stands it stops you zooming greater than 15 completely, so used the idea from the fourth post to have a flag set to only do it the first time.

// Do other stuff to set up map
var map = new google.maps.Map(mapElement, myOptions);
// This is needed to set the zoom after fitbounds, 
google.maps.event.addListener(map, 'zoom_changed', function() {
    zoomChangeBoundsListener = 
        google.maps.event.addListener(map, 'bounds_changed', function(event) {
            if (this.getZoom() > 15 && this.initialZoom == true) {
                // Change max/min zoom here
                this.setZoom(15);
                this.initialZoom = false;
            }
        google.maps.event.removeListener(zoomChangeBoundsListener);
    });
});
map.initialZoom = true;
map.fitBounds(bounds);

Anthony.

Update April 2023:

Per the comments below, using modern JS and Google Maps API V3, this block should look something like:

map = new google.maps.Map(mapElement, myOptions);
map.maxDefaultZoom = 15;

google.maps.event.addListenerOnce(map, "bounds_changed", function () {
  this.setZoom(Math.min(this.getZoom(), this.maxDefaultZoom));
});

Disappoint answered 7/6, 2010 at 13:22 Comment(9)
Thank you! I've been fighting with that for a couple of hours! It does seem like a bug, though.Thurstan
This is a good method. Another method is to check the current bounds and extend the bounds, i.e., #3335229 . Though the linked answer here is only expanding the bounds by a fraction, you can use the same method to expand the bounds by a larger number; I've found that adding 7 to all corners does a good job.His
This is a cool solution. I noticed, however, that it works fine for me in chrome, ie, and firefox without using map.initialZoom.Randazzo
I don't think map.initialZoom is needed, since the event removes itself once it fires. A great solution, nevertheless.Schatz
I'd like to correct my previous comment. I think that setting the initialZoom property might be needed. I had some trouble zooming in real close on IE 9 after leaving out that part of the code... Cool solution!Randazzo
Use google.maps.event.addListenerOnce(), so you don't have to manually remove the listener afterwards.Mallis
the variable zoomChangeBoundsListener throws an execption as its undefined and hence the map is stuck. Is this the correct code snippetBevash
Because I've been looking at this the third time, here's the golfed version. Thanks @pstadler. google.maps.event.addListenerOnce(map, 'bounds_changed', function() { this.setZoom(Math.min(15, this.getZoom())); });Terpsichore
When using addLIstenerOnce you can simply listen zoom_changed. I'm using zoom_changed just before map.fitBounds(bounds); and seems to work great on Chrome.Calder
S
69

Without trying it, I'd say you should be able to do it just by having fitBounds() before you get the zoom level, i.e.

map.fitBounds(bounds);
var zoom = map.getZoom();
map.setZoom(zoom > 6 ? 6 : zoom);

If you did try that and it didn't work, you can setup your map with minZoom in the MapOptions (api-reference) like this:

var map = new google.maps.Map(document.getElementById("map"), { minZoom: 6 });

This would keep the map from zooming any further out when using fitBounds().

Saros answered 17/5, 2011 at 23:24 Comment(6)
nice simple and elegant solution!Colmar
Setting the minZoom and maxZoom values restricts the user from zooming beyond those values, so it doesn't help if all you want to do is set the initial zoom to something reasonable (which is what I think the OP was talking about)Luge
So basically pretty much exactly as I wrote in my answer?Saros
@Luge makes a good point, that isn't covered in the answer. This is great at stopping fitBounds() from zooming too far, but it also restricts the user's interaction with the map's zoom buttons. The accepted answer is the better option if you prefer that the user have full control.Schertz
I'm not really sure how it's not covered by my answer, as I mention the second technique stating: "This would keep the map from zooming any further"Saros
Might not work because wit new versions. fitBounds() works asynchronous!Dragonhead
E
23

You can also set the maxZoom option just before calling fitBounds() and reset the value afterwards:

if(!bounds.isEmpty()) {
    var originalMaxZoom = map.maxZoom;
    map.setOptions({maxZoom: 18});
    map.fitBounds(bounds);
    map.setOptions({maxZoom: originalMaxZoom});
}
Entangle answered 1/7, 2014 at 8:58 Comment(2)
I like this option because it does not cause the zoom_changed event to fire twice like many of the other optionsShuffleboard
Awesome solution when max object has maxZoom option defined.Preachment
M
21

Anthony's solution is very nice. I only needed to fix the zoom for the inital page load (ensuring that you weren't too far zoomed in to start with) and this did the trick for me:

var zoomChangeBoundsListener =
    google.maps.event.addListener(map, 'bounds_changed', function(event) {
        google.maps.event.removeListener(zoomChangeBoundsListener);
        map.setZoom( Math.min( 15, map.getZoom() ) );
    });


map.fitBounds( zoomBounds );
Mer answered 30/11, 2011 at 17:43 Comment(1)
Instead of removing the listener you can use addListenerOnce, but I am using this solution.Weaken
G
11

When you call map.fitBounds() on one item - the map may zoom in too closely. To fix this, simply add 'maxZoom' to mapOptions...

var mapOptions = {
  maxZoom: 15
};
Gouache answered 25/4, 2013 at 17:14 Comment(0)
T
8

In my case, I simply wanted to set the zoom level to one less than what google maps chose for me during fitBounds. The purpose was to use fitBounds, but also ensure no markers were under any map tools, etc.

My map is created early and then a number of other dynamic components of the page have an opportunity to add markers, calling fitBounds after each addition.

This is in the initial block where the map object is originally created...

var mapZoom = null;

Then this is added to each block where a marker is added, right before the map.fitBounds is called...

google.maps.event.addListenerOnce(map, 'bounds_changed', function() { 
    if (mapZoom != map.getZoom()) { 
        mapZoom = (map.getZoom() - 1); 
        map.setZoom(mapZoom); 
    } 
});

When using 'bounds_changed' without the check in place, the map zoomed out once for every marker regardless of whether it needed it or not. Conversely, when I used 'zoom_changed', I would sometimes have markers under map tools because the zoom didn't actually change. Now it is always triggered, but the check ensures that it only zooms out once and only when needed.

Hope this helps.

Tisza answered 21/9, 2012 at 13:45 Comment(1)
This seems to be the only answer that should actually use addListener instead of repeating (and relying on remembering) to addListenerOnce before every single map.fitBounds()Saros
M
3

Since Google Maps V3 is event driven, you can tell the API to set back the zoom to a proper amount when the zoom_changed event triggers:

var initial = true
google.maps.event.addListener(map, "zoom_changed", function() {
    if (initial == true){
       if (map.getZoom() > 11) {
         map.setZoom(11);
         initial = false;
       }
    }
}); 

I used initial to make the map not zooming too much when the eventual fitBounds is permorfed, but to let the user zoom as much as he/she wants. Without the condition any zoom event over 11 would be possible for the user.

Minna answered 20/10, 2013 at 17:54 Comment(0)
H
3

I found the following to work quite nicely. It is a variant on Ryan's answer to https://stackoverflow.com/questions/3334729/.... It guarantees to show an area of at least two times the value of offset in degrees.

const center = bounds.getCenter()
const offset = 0.01
const northEast = new google.maps.LatLng(
    center.lat() + offset,
    center.lng() + offset
)
const southWest = new google.maps.LatLng(
    center.lat() - offset,
    center.lng() - offset
)
const minBounds = new google.maps.LatLngBounds(southWest, northEast)
map.fitBounds(bounds.union(minBounds))
Heterocyclic answered 18/7, 2019 at 12:13 Comment(0)
C
0

I just had the same task to solve and used a simple function to solve it.

it doesn't care how many Markers are in the bounds - if there are a lot and the zoom is already far away, this zooms a little bit out, but when there is only one marker (or a lot very close to each other), then the zoomout is significant (customizable with the extendBy variable):

var extendBounds = function() {
  // Extends the Bounds so that the Zoom Level on fitBounds() is a bit farer away
  var extendBy = 0.005;
  var point1 = new google.maps.LatLng(
    bounds.getNorthEast().lat() + extendBy,
    bounds.getNorthEast().lng() + extendBy
  )
  var point2 = new google.maps.LatLng(
    bounds.getSouthWest().lat() - extendBy,
    bounds.getSouthWest().lng() - extendBy
  )
  bounds.extend(point1);
  bounds.extend(point2);
}

To use it, I use an own function to do the fitBounds():
map is my GoogleMap Object

var refreshBounds = function() {
  extendBounds();
  map.fitBounds(bounds);
}
Consubstantiation answered 6/1, 2021 at 7:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.