Search code examples
javascriptgoogle-mapsgoogle-maps-api-3fitbounds

google maps fitBounds callback


I know there is a bounds_changed event that is triggered if the bounds have changed, however I need to know when fitBounds has completed (when bounds have changed or bounds have stayed the same).

Google has some sort of tolerance where if the bounds have changed very little it will not actually change the bounds / call bounds_changed. I need to know when this happens.

I have done this successfully with timeouts but it seems hacky and possibly prone to some errors when bounds_changed take more than 100ms to trigger.

See example: http://jsbin.com/sosurexuke/1/edit?js,console,output

Any better solutions?

Edit: If you think just checking if the map.getBounds() is the same as what you are sending fitBounds() then you'll be disappointed to know that the result of fitting to bounds is typically not even close to the bounds you send. I wrote this function thinking this might be possible

function boundsAreIdentical(bounds1, bounds2) {
        if (!bounds1 || !bounds2) return false;

        var tolerance = 0.00001;
        console.log(bounds1.getNorthEast().lat(), bounds2.getNorthEast().lat());
        console.log(bounds1.getNorthEast().lng(), bounds2.getNorthEast().lng());
        console.log(bounds1.getSouthWest().lat(), bounds2.getSouthWest().lat());
        console.log(bounds1.getSouthWest().lng(), bounds2.getSouthWest().lng());
        if (
            (Math.abs(bounds1.getNorthEast().lat() - bounds2.getNorthEast().lat()) <= tolerance) &&
            (Math.abs(bounds1.getNorthEast().lng() - bounds2.getNorthEast().lng()) <= tolerance) &&
            (Math.abs(bounds1.getSouthWest().lat() - bounds2.getSouthWest().lat()) <= tolerance) &&
            (Math.abs(bounds1.getSouthWest().lng() - bounds2.getSouthWest().lng()) <= tolerance)) {
            return true;
        }
        return false;
    }

Edit 2 years later:

It seems that in the latest experimental version of google maps (3.32) bounds_changed is fired even when the bounds are identical to current bounds.

I updated the original example to be version 3.31 which still shows the original issue.


Solution

  • As denoted in the edit to the question. If you can use 3.32, then just use that. Otherwise if you need a solution that works pre-3.32 then you can simply pan the map by 1 pixel right before fitting bounds. The visual change doesnt trigger a refresh and forces the bounds to always change.

    Demo: http://jsbin.com/bipekaxosa/1/edit?js,console,output

    function pan(map, newCenterLatLng, offsetx, offsety, callback) {
        if (!map.getProjection()) {
            if (callback) {
                return setTimeout(pan, 1, map, newCenterLatLng, offsetx, offsety, callback);
            }
            throw new Error("You must wait until map.getProjection() is ready. Try using the callback instead");
        }
    
        var newCenterLatLngPixels = map.getProjection().fromLatLngToPoint(newCenterLatLng);
    
        var offset = new google.maps.Point(
            ((typeof (offsetx) == "number" ? offsetx : 0) / Math.pow(2, map.getZoom())) || 0,
            ((typeof (offsety) == "number" ? offsety : 0) / Math.pow(2, map.getZoom())) || 0
        );
    
        map.setCenter(map.getProjection().fromPointToLatLng(new google.maps.Point(
            newCenterLatLngPixels.x - offset.x,
            newCenterLatLngPixels.y + offset.y
        )));
    
        if (callback) {
            return callback();
        }
    }
    
    function fitBoundsAndCallback(map, bounds, callback) {
        google.maps.event.addListenerOnce(map, "bounds_changed", function() {
            if (callback) {
                callback();
            }
        });
    
        // Pan the map 1 pixel up so that bounds will always change
        // even in the event you are fitting to the same bounds as before
        // Remove this once the move to gmaps 3.32 as bounds_changed works always
        this.pan(map, map.getCenter(), 0, 1, function(){
          map.fitBounds(bounds);
        });
    }