google maps fitBounds callback
Asked Answered
A

2

0

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.

Aalst answered 3/3, 2016 at 1:23 Comment(0)
A
0

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);
    });
}
Aalst answered 18/3, 2018 at 15:42 Comment(0)
S
1

The easiest way to solve the first part of this question (knowing when the bounds have finished changing) is to use the idle event listener.

How ever you are changing the bounds, set this event just before you are about to change them. That way, when the bounds are all settled, the map will register as idle and the idle event fires, inside which you can do your shiz. To prevent events piling on top of each other, clear it at the same time too.

You may have to fiddle a little with it. Speaking of fiddling, here's a fiddle. In this fiddle I just set the alert to happen every time:

https://jsbin.com/kapimov/edit?js,output Click the "Change Bounds" and you'll see it happen over and over:)

EDIT: To check for if fitBounds() fired when it fails, compare center before and after firing fitBounds().

Even when trying to set the bounds to the same coordinates twice, the centre appears to change and can be compared. The centre check seems to happen before the actual movement of the map for the bounds, but always changes on calling that function.

Hit the "Fit Current Bounds" button twice to see it in action; the first time 'bounds_changed' fires, the second it doesn't but centre still checks correctly. Comment out the fitBounds() function to see how centre doesn't change without it.

Sergo answered 3/3, 2016 at 7:52 Comment(4)
a call of fitBounds must not modify the bounds of a map, that's what's this question is aboutGrimmett
Hmm, I see, for the case when fitBounds() doesn't succeed in moving the map. I updated the Fiddle with a sure failure case, making bounds the SAME as current bounds. Then I used another property - the center - to check if bounds always changes it. Turns out it does! Edited my answer to reflect new solution.Sergo
Fit current bounds button does not work properly, goes to ocean. When I make it work then it never triggers bounds_changed. It's worth noting that getCenter will fire immediately and not after the bounds may have potentially changed.That is getCenter fires say 0.1 ms after fitBounds but the changing of the bounds typically happens in 10 ms. In order for the center trick to work properly you need to know when the fitBounds has finished (you need a callback) which is back to the original problem.Aalst
It doesn't matter where the bounds are set to; the functions are the same. The 10ms you refer to is an animation effect. Are you trying to know when the animation ends? Regardless of if the fitBounds() function actually moved the bounds, 'center_changed' fires a moment later. When fitBounds() is not called, 'center_changed' does not fire. This is a good test for the case you were looking for - when fitBounds() is called but 'bounds_changed' doesn't fire.Sergo
A
0

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);
    });
}
Aalst answered 18/3, 2018 at 15:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.