7

I am trying to get the bounds of a map after the zoom has changed, but the zoom_changed event fires before the bounds have been recalculated. So in the zoom_changed handler you get the previous bounds, not the new bounds.

Is there any way how to get the proper bounds on zoom change?

Tomas
  • 57,621
  • 49
  • 238
  • 373

4 Answers4

17

This is a bug please star this issue if you are interested.

There is an ugly workaround for it:

google.maps.event.addListener(map, 'zoom_changed', function () {
    google.maps.event.addListenerOnce(map, 'bounds_changed', function (e) {
            my_zoom_handler(); // do your job here
    });
});
Fluffeh
  • 33,228
  • 16
  • 67
  • 80
Tomas
  • 57,621
  • 49
  • 238
  • 373
  • +1 I agree this is a bug and the API is not intuitive for this case. Would be nice if the zoom_change was sent once the new bounds have been calculated. – RedBlueThing Nov 30 '11 at 22:57
  • This code will not work if `bounds_changed` event arrives before the zoom event. `Maps API fires these latter events independently`. – mgPePe Oct 18 '13 at 11:30
  • Great workaround. I changed the inner event-listener to `'idle'` instead of `'bounds_changed'` as the `'bounds_changed'` event is triggered during the animation. I think the OP wanted it triggered after. – Todd Oct 14 '14 at 17:56
  • great, thanks; saved me a lot of google and searching for a solution, with timeout, and other things, this is the best; – Thanatos11th Jul 18 '17 at 16:09
  • one issue, the drag event will also be affected by the bound changes; – Thanatos11th Jul 18 '17 at 16:13
3

From the API documentation:

If you are trying to detect a change in the viewport, be sure to use the specific bounds_changed event rather than constituent zoom_changed and center_changed events. Because the Maps API fires these latter events independently, get_bounds() may not report useful results until after the viewport has authoritatively changed. If you wish to get_bounds() after such an event, be sure to listen to the bounds_changed event instead.

RedBlueThing
  • 42,006
  • 17
  • 96
  • 122
  • using bounds_changed event instead of zoom_changed in this case is nonsense. It will fire at any occasion when bounds are changed, but he's only interested in cases when the zoom is changed. – Tomas Nov 23 '11 at 09:40
  • 1
    @tomas-t I don't agree that handling the bounds_changed event is 'nonsense'. If you are after the bounds when the zoom changed, couldn't you check getZoom in your bounds_change handler? The documentation I posted is justifying the behavior (edge case bug), but the workaround seems quite simple to me. – RedBlueThing Nov 30 '11 at 22:56
  • of course you can, but for the thing OP wants handling bounds_changed event instead of zoom_changed is unnecesary overkill, because the event would fire even during panning etc. Look at the workaround in my response. – Tomas Dec 01 '11 at 10:06
2

To bind bounds_changed and work with markers/map stuff after zoom use this:

google.maps.event.addListener(map, 'zoom_changed', function() {
    this.zoomChanged = true;
});

google.maps.event.addListener(map,"bounds_changed",function() {
    if (this.zoomChanged) {
        this.zoomChanged = false;
        // DO YOUR STUFF
    }
});
jeff
  • 8,300
  • 2
  • 31
  • 43
Pion
  • 708
  • 9
  • 21
0

I had this same issue. Here's what I finally got working to solve several of the issues I'd had with other solutions.

*Properly enforces bounds regardless of whether you use the mouse or arrow keys

*Doesn't stop short of the edge if hold down the arrow keys, due to the pan acceleration causing it to "overshoot" the edge in a single step, so it stops short instead (try holding the arrow key in one direction until you hit the edge, then release and press it again, and with some solutions, it will scroll just a little more)

*Doesn't "bounce back" when it hits the edge

*Properly enforces bounds on zoom change

EDIT: Ok, so it works when you change zoom with the scroll wheel, but not with the zoom control. Let me play around with it a bit, and I'll see if I can get that working too...

EDIT 2: Turns out, the issue was because I removed the pan control. As long as the pan control is present, this works fine, both with the scroll wheel and the zoom control.

EDIT 3: Nope... that wasn't it. I've updated the code to handle the zoom control.

// bounds of the desired area
var allowedBounds = new google.maps.LatLngBounds(
                    new google.maps.LatLng(-64, -64), 
                    new google.maps.LatLng(64, 64)
                    );

var zoomChanged = false;

google.maps.event.addListener(map, 'center_changed', function() {
  var mapBounds = map.getBounds();

  if(mapBounds.getNorthEast().lat() > allowedBounds.getNorthEast().lat()) {
    var newCenter = new google.maps.LatLng(map.getCenter().lat() -
                                           (mapBounds.getNorthEast().lat() -
                                           allowedBounds.getNorthEast().lat()),
                                           map.getCenter().lng(), true);
    map.panTo(newCenter);
    return;
  }

  if(mapBounds.getNorthEast().lng() > allowedBounds.getNorthEast().lng()) {
    var newCenter = new google.maps.LatLng(map.getCenter().lat(),
                                           map.getCenter().lng() -
                                           (mapBounds.getNorthEast().lng() -
                                           allowedBounds.getNorthEast().lng()), true);
    map.panTo(newCenter);
    return;
  }

  if(mapBounds.getSouthWest().lat() < allowedBounds.getSouthWest().lat()) {
    var newCenter = new google.maps.LatLng(map.getCenter().lat() +
                                           (allowedBounds.getSouthWest().lat() -
                                           mapBounds.getSouthWest().lat()),
                                           map.getCenter().lng(), true);
    map.panTo(newCenter);
    return;
  }

  if(mapBounds.getSouthWest().lng() < allowedBounds.getSouthWest().lng()) {
    var newCenter = new google.maps.LatLng(map.getCenter().lat(),
                                           map.getCenter().lng() +
                                           (allowedBounds.getSouthWest().lng() -
                                           mapBounds.getSouthWest().lng()), true);
    map.panTo(newCenter);
    return;
  }
}, this);

google.maps.event.addListener(map, 'zoom_changed', function() {
  zoomChanged = true;
}, this);

google.maps.event.addListener(map, 'bounds_changed', function() {
  if(zoomChanged) {   
    var mapBounds = map.getBounds();

    if(mapBounds.getNorthEast().lat() > allowedBounds.getNorthEast().lat()) {
      var newCenter = new google.maps.LatLng(map.getCenter().lat() -
                                             (mapBounds.getNorthEast().lat() -
                                             allowedBounds.getNorthEast().lat()),
                                             map.getCenter().lng(), true);
      map.panTo(newCenter);
      return;
    }

    if(mapBounds.getNorthEast().lng() > allowedBounds.getNorthEast().lng()) {
      var newCenter = new google.maps.LatLng(map.getCenter().lat(),
                                             map.getCenter().lng() -
                                             (mapBounds.getNorthEast().lng() -
                                             allowedBounds.getNorthEast().lng()), true);
      map.panTo(newCenter);
      return;
    }

    if(mapBounds.getSouthWest().lat() < allowedBounds.getSouthWest().lat()) {
      var newCenter = new google.maps.LatLng(map.getCenter().lat() +
                                             (allowedBounds.getSouthWest().lat() -
                                             mapBounds.getSouthWest().lat()),
                                             map.getCenter().lng(), true);
      map.panTo(newCenter);
      return;
    }

    if(mapBounds.getSouthWest().lng() < allowedBounds.getSouthWest().lng()) {
      var newCenter = new google.maps.LatLng(map.getCenter().lat(),
                                             map.getCenter().lng() +
                                             (allowedBounds.getSouthWest().lng() -
                                             mapBounds.getSouthWest().lng()), true);
      map.panTo(newCenter);
      return;
    }

    zoomChanged = false;
  }
}, this);
qwertymodo
  • 435
  • 7
  • 16
  • With this approach I get "too much recursion" errors. If I instead just listen the bounds_changed event (ignoring center_changed and zoom_changed), this error disappears. On the other hand, movements are not 100% perfect, as there is still some bounce. – Jose Gómez May 11 '15 at 23:47
  • @JoseGómez there seems to be an issue specific to Google Chrome, try it in Firefox. I'm looking into it further to see if I can correct the issue. See this question: http://stackoverflow.com/questions/15671480/uncaught-rangeerror-maximum-call-stack-size-exceeded-google-maps-when-i-try-to – qwertymodo May 27 '15 at 17:55
  • @JoseGómez I solved the issue by setting v=3 in the query string for the API script include line. i.e. `` – qwertymodo Jun 04 '15 at 18:33