18

In Google maps API v2, map of our country was nicely fitted to 700x400px map with the following:

map.getBoundsZoomLevel(<bounds of our country>)

But in API v3, the map.fitBounds() method doesn't fit it at that zoom level to 700x400 - it zooms out one level.

This means that map.fitBounds() counts with some "grace margin" or something.

How can I affect the size of this margin?

BillyBigPotatoes
  • 1,330
  • 11
  • 23
Tomas
  • 57,621
  • 49
  • 238
  • 373
  • Could the "grace margin" be a percentage of your desired bounds? If this margin is fixed, you could input an even smaller, measured, bounds to fitBounds(), so that the end of the grace margin matched your originally desired bounds. Hope this made sense. – Heitor Chang Mar 23 '12 at 18:05
  • @HeitorChang, this would be really hacky and clumsy solution, but anyway without knowing how exactly the "grace margin" is computed by fitBounds(), you cannot do that. – Tomas Mar 23 '12 at 18:31
  • 7
    After hacking around, I concluded that v3 fitBounds() will zoom in only if there's at least a 45 pixel padding (to both sides of the rectangle defined by your bounds) on the long dimension, at that zoom level. Supposing it's a close call, the map will stay zoomed out, leaving a big margin. I hope this may be useful to you. – Heitor Chang Mar 23 '12 at 19:47
  • 4
    After reading this: http://stackoverflow.com/questions/6048975/google-maps-v3-how-to-calculate-the-zoom-level-for-a-given-bounds I'm afraid there's no other solution besides writing my own `fitBounds`. Shitty api v3, yet another flaw! – Tomas Mar 24 '12 at 07:54
  • Do you know a workaround to get bounds of that "grace margin"? – Murat Çorlu Oct 03 '13 at 09:03
  • @Tomas did you find an answer to your question? I am also interested in this margin, and especially how it's calculated. Or, in other words, how to transform the result of getBounds() to be used for fitBounds() and have no zoom effect after. Thanks! – Tengiz Oct 26 '13 at 21:55
  • No, I don't know the answer. If I knew it I would post it here. – Tomas Oct 27 '13 at 13:52
  • 2
    In many cases, you could replace your map bounds by a reference to the map center coordinates and the zoom level. – MrUpsidown Dec 23 '13 at 13:46
  • @Tomas, how do you get your country bounds? Did you define them manually or do you get them from the API? – MrUpsidown Jan 31 '14 at 12:53
  • @MrUpsidown: manually. – Tomas Jun 14 '14 at 10:11

2 Answers2

2

Here's a solution that will zoom into the map as far as possible without a custom margin. You should be able to adapt it to account for some margin if you want to.

My solution is based on this comment in a Google Groups thread. Unfortunately, the call to helper.getProjection() always returned undefined, so I adapted the above answer a bit and came up with this working code.

Just replace your existing calls to map.fitBounds(bounds) with myFitBounds(map, bounds):

function myFitBounds(myMap, bounds) {
    myMap.fitBounds(bounds);

    var overlayHelper = new google.maps.OverlayView();
    overlayHelper.draw = function () {
        if (!this.ready) {
            var projection = this.getProjection(),
                zoom = getExtraZoom(projection, bounds, myMap.getBounds());
            if (zoom > 0) {
                myMap.setZoom(myMap.getZoom() + zoom);
            }
            this.ready = true;
            google.maps.event.trigger(this, 'ready');
        }
    };
    overlayHelper.setMap(myMap);
}

// LatLngBounds b1, b2 -> zoom increment
function getExtraZoom(projection, expectedBounds, actualBounds) {
    var expectedSize = getSizeInPixels(projection, expectedBounds),
        actualSize = getSizeInPixels(projection, actualBounds);

    if (Math.floor(expectedSize.x) == 0 || Math.floor(expectedSize.y) == 0) {
        return 0;
    }

    var qx = actualSize.x / expectedSize.x;
    var qy = actualSize.y / expectedSize.y;
    var min = Math.min(qx, qy);

    if (min < 1) {
        return 0;
    }

    return Math.floor(Math.log(min) / Math.log(2) /* = log2(min) */);
}

// LatLngBounds bnds -> height and width as a Point
function getSizeInPixels(projection, bounds) {
    var sw = projection.fromLatLngToContainerPixel(bounds.getSouthWest());
    var ne = projection.fromLatLngToContainerPixel(bounds.getNorthEast());
    return new google.maps.Point(Math.abs(sw.y - ne.y), Math.abs(sw.x - ne.x));
}
Oliver
  • 9,239
  • 9
  • 69
  • 100
  • This worked well for me, but I had to modify one line to deal with a floating point rounding error when `expectedSize` is exactly double `actualSize`. Changed the return statement of `getSizeInPixels` to: `return new google.maps.Point( Math.round(10000 * Math.abs(sw.y - ne.y)) / 10000, Math.round(10000 * Math.abs(sw.x - ne.x)) / 10000 );` – Eric Freese Dec 05 '14 at 21:06
0

Now, the method fitBounds has a second parameter that represents the size of the padding. For those who want to remove it, you just need to pass 0.

Map.map.fitBounds(bounds, 0);


New method signature: fitBounds(bounds:LatLngBounds|LatLngBoundsLiteral, padding?:number)