I've run into a problem where calling map.fitBounds seems to zoom out. I'm trying to use the backbone.js router to save the map bounds in the url. I want to be able to bookmark the url and later have the map look exactly the same.
I noticed calling map.fitBounds(map.getBounds())
in the console will always zoom out. Where as I would like it to not change the map.
Is this normal behaviour? How can I enforce this so the map will look the same from one step to the next?
Thanks
The app does this because you're not in navigation mode, you're browsing the route. It zooms out to fit the entire route on your screen when you reorient the device. If you don't want it to zoom, press the navigation button in the lower right corner and optionally switch off voice navigation.
Users can zoom the map by clicking the zoom controls. They can also zoom and pan by using two-finger movements on the map for touchscreen devices.
Setting up the minimum or maximum Zoom value for Google Maps can be done by adding the shortcode attribute with Store Locator Shortcode. Furthermore, the maximum value of Google Maps Zoom level is 22. Therefore, the defined maximum value must be below or equal to 22, which is the default.
For people seeing this question in 2018, Google Maps API v3 now supports a second padding
argument that specifies the amount of padding to include around the bounds you specify.
To avoid zooming out, simply just call fitBounds
with a padding of 0
. For this example it would be map.fitBounds(map.getBounds(), 0)
This problem of Google Map API was discussed few times. See:
Function map.fitBounds()
sets the viewport to contain the bounds. But map.getBounds()
function returns the viewport bounds with some extra margin (note that this is normal behaviour and no bug). The enlarged bounds no longer fits in the previous viewport, so the map zooms out.
A simple solution to your problem is to use the map center and the zoom level instead. See functions map.getCenter()
, map.setCenter()
, map.getZoom()
and map.setZoom()
.
Shrink the bounding box slightly before sending it to map.fitBounds()
.
Here's a function to shrink a bounding box:
// Values between 0.08 and 0.28 seem to work
// Any lower and the map will zoom out too much
// Any higher and it will zoom in too much
var BOUNDS_SHRINK_PERCENTAGE = 0.08; // 8%
/**
* @param {google.maps.LatLngLiteral} southwest
* @param {google.maps.LatLngLiteral} northeast
* @return {Array[google.maps.LatLngLiteral]}
*/
function slightlySmallerBounds(southwest, northeast) {
function adjustmentAmount(value1, value2) {
return Math.abs(value1 - value2) * BOUNDS_SHRINK_PERCENTAGE;
}
var latAdjustment = adjustmentAmount(northeast.lat, southwest.lat);
var lngAdjustment = adjustmentAmount(northeast.lng, southwest.lng);
return [
{lat: southwest.lat + latAdjustment, lng: southwest.lng + lngAdjustment},
{lat: northeast.lat - latAdjustment, lng: northeast.lng - lngAdjustment}
]
}
Use it like this:
// Ensure `southwest` and `northwest` are objects in google.maps.LatLngLiteral form:
// southwest == {lat: 32.79712, lng: -117.13931}
// northwest == {lat: 32.85020, lng: -117.09356}
var shrunkBounds = slightlySmallerBounds(southwest, northeast);
var newSouthwest = shrunkBounds[0];
var newNortheast = shrunkBounds[1];
// `map` is a `google.maps.Map`
map.fitBounds(
new google.maps.LatLngBounds(newSouthwest, newNortheast)
);
I have an app doing the same thing as codr: saving the current map bounds in the URL, and initializing the map from the URL bounds upon refresh. This solution works wonderfully for that.
Google Maps essentially does this when fitBounds()
is called:
If the bounds given would exactly match the viewport, Google Maps doesn't consider that as "containing" the bounds, so it zooms out one more level.
I got the same problem when after initializing the map called map.fitBounds(...)
. The behaviour I encountered was that if the fitBounds
method was called a few seconds after the map was initialized then no problem (the zoom was appropriate).
So first I started to call the fitBounds
method after the map was loaded, which on google maps translates to when the map is idle
.
google.maps.event.addListener(map, 'idle', function() {
var bounds = //define bounds...
map.fitBounds(bounds);
});
This actually works, however the idle
event is triggered almost constantly. And if there is any change to the map (drag, zoom, etc...) when this change stops, the map will fit the bounds again...
The problem is that calling the method only once does not work (either with a boolean to check if was already called, or with google.maps.event.addListenerOnce
because the first time the map is idle it still "isn't ready" for the bounds to be fitted. (From experience).
So my solution was to trigger the fitBounds
on a different event. Instead of idle
which is called too early for the fitBounds
method, the event tilesloaded
actually gets the job done (only called once)!
Solution:
google.maps.event.addListenerOnce(map, 'tilesloaded', function() {
var bounds = //define bounds...
map.fitBounds(bounds);
});
I noticed this behavior when using fitBounds()
on a hidden map view (using a front-end rendering framework, like Angular). Because this was a mobile view, I was showing a list of locations first, and was populating the markers on a hidden map as the list was loading (a concurrent load). So when the user wants to view the map, and they switch the segment view/tab/whatever, the map would show with the markers already loaded and in view. But this bugged out and zoomed to show the complete world in the map.
<div [hidden]="view !== 'map'>
<google-map></google-map>
</div>
fitBounds()
needs the map to be loaded in the view because it uses the maps dimensions in order to resize the map. If the map is hidden, it zooms the map until the complete map is shown.
The solution was simple: when the view was switched, call fitBounds()
again.
<button (click)="showMap()">Show Map</button>
public showMap() {
this.view = 'map';
setTimeout(() => {
if (typeof this.map !== 'undefined') this.map.fitBounds(this.bounds);
});
}
Note: I wrapped a setTimeout
around the fitBounds()
call so that Angular can finish the lifecycle and render the map before it is called.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With