Google Maps API V3 fitbounds() zooms out but never in
I've created a quite complex store locator of sorts. The user enters their zip and a table returns results with corresponding markers on a map. They can page through results and the markers move further and further out and the fitbounds()
function works开发者_如何学编程 great for zoom out to the appropriate zoom level to fit the markers. The problem is that if you page back to closer locations the fitbounds()
doesn't zoom in. Even if you enter a new search it doesn't zoom in around those new markers -- it centers over them but stays at whatever zoom level it was at previously. I hope this makes sense. If anyone knows what I need to add to get it to zoom back in on closer results please help. These are the google functions that I'm call at the end of my marker pushing function:
map.setCenter(bounds.getCenter());
map.panToBounds(bounds);
map.fitBounds(bounds);
Thanks!
Nothing fancy needed here. First fit bounds then pan to it. This will give you the proper zoom and contain the entire bounds.
map.fitBounds(bounds);
map.panToBounds(bounds);
The problem is this: we set
var bounds = new google.maps.LatLngBounds();
so that we can later fit our markers to a bounded area on the map. GMaps will always zoom out asynchronously to fitBounds() accordingly, but will not zoom in to achieve the same (as previously noted by @broady). This is not ideal for many applications as once you have gone and displayed a series of markers on the map that caused the map to zoom out (maybe <10), it will not zoom back in without the user manually doing so.
GMaps will continue to use the bounds of the (lack of better words) most zoomed out marker collection status (sorry). Setting to 'null' before each call for new markers gives you a fresh map to work with.
To auto-zoom in, simply set the LatLngBounds(); to 'null' like so (see pseudo example below to see its placement):
bounds = new google.maps.LatLngBounds(null);
Pseudo example:
// map stuff/initiation
...
var bounds = new google.maps.LatLngBounds();
var gmarkers = [];
function CreateMarker (obj) {
myLatLng = new google.maps.LatLng(obj['latitude'], obj['longitude']);
marker = new google.maps.Marker({
position: myLatLng,
map: map
});
google.maps.event.addListener(marker, 'click', (function(marker, i) {
return function() {
infowindow.setContent(obj['job']);
infowindow.open(map, marker);
}
})(marker, i));
bounds.extend(myLatLng);
gmarkers.push(marker);
}
....
// here's an AJAX method I use to grab marker coords from a database:
$.ajax({
beforeSend: function() {
clear_markers(gmarkers); // see below for clear_markers() function declaration
},
cache: false,
data: params,
dataType: 'json',
timeout: 0,
type: 'POST',
url: '/map/get_markers.php?_=<?php echo md5(session_id() . time() . mt_rand(1,9999)); ?>',
success: function(data) {
if (data) {
if (data['count'] > 0) {
var obj;
var results = data['results'];
// Plot the markers
for (r in results) {
if (r < (data['count'])) {
CreateMarker(results[r]);
}
}
}
}
},
complete: function() {
map.fitBounds(bounds);
}
});
// clear_markers()
function clear_markers(a) {
if (a) {
for (i in a) {
a[i].setMap(null);
}
a.length = 0;
}
bounds = new google.maps.LatLngBounds(null); // this is where the magic happens; setting LatLngBounds to null resets the current bounds and allows the new call for zoom in/out to be made directly against the latest markers to be plotted on the map
}
That's right, fitBounds
only ensures that the provided bounds are visible. It doesn't zoom in to an appropriate level.
You could first call setZoom(20)
, then fitBounds.
Hmm, interesting.. I use PHP to loop through all (to be) marker coordinates and calculate the values of southWest and northEast; the coords of origin are halfway between the two. If all marker coordinates are very close to each other, the zoom factor set by fitBounds is much higher (zoomed in) than the 15 used at map creation. That's why I have added that last row..
var map;
function mapInit() {
var origin = new google.maps.LatLng(59.33344615, 18.0678188);
var options = {
zoom: 15,
center: origin,
mapTypeControlOptions: {
mapTypeIds: [google.maps.MapTypeId.ROADMAP, google.maps.MapTypeId.HYBRID]
},
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("googlemap"), options);
var southWest = new google.maps.LatLng(59.3308415, 18.0643054);
var northEast = new google.maps.LatLng(59.3360508, 18.0713322);
var bounds = new google.maps.LatLngBounds(southWest, northEast);
map.fitBounds(bounds);
google.maps.event.addListenerOnce(map, "idle", function() {
if (map.getZoom() > 16) map.setZoom(16);
});
So, either Google has reprogrammed the function since you posted the question or.. I need more information about your code.
What helped me was using 0 padding as the second parameter to fitBounds(bounds, padding), here is the full code sample:
function initMap() {
var mapOptions = {
center: new google.maps.LatLng(0, 0),
zoom: 1,
minZoom: 1
};
map = new google.maps.Map(document.getElementById('officeMap'), mapOptions);
google.maps.event.addListenerOnce(map, 'idle', function() {
//Map is ready
worldViewFit(map);
});
}
function worldViewFit(mapObj) {
var worldBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(70.4043,-143.5291), //Top-left
new google.maps.LatLng(-46.11251, 163.4288) //Bottom-right
);
mapObj.fitBounds(worldBounds, 0);
var actualBounds = mapObj.getBounds();
if(actualBounds.getSouthWest().lng() == -180 && actualBounds.getNorthEast().lng() == 180) {
mapObj.setZoom(mapObj.getZoom()+1);
}
}
I also had an issue with the map zooming way out when calling fitBounds a second time on the same map with new markers. This is the frankensolution that works for me:
// This is a stationary point tho dynamic will work too
var myLat = someLat,
myLng = someLong;
var map = false,
markersArray = [];
function makeOrderMap(lat, lng) { // pass in dynamic point when updating map
var mapOptions = {
center: new google.maps.LatLng(myLat, myLng),
zoom: 16,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
deleteOverlays(); // remove any existing markers..
if(!map) {
map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
}
// Find the mid-point to center the map
midLat = (parseFloat(lat) + parseFloat(myLat)) / 2;
midLng = (parseFloat(lng) + parseFloat(myLng)) / 2;
map.setCenter(new google.maps.LatLng(midLat, midLng));
var newSpot = new google.maps.LatLng(lat, lng);
placeMarker(mapOptions.center);
placeMarker(newSpot);
// determine the distance between points for deciding the zoom level
var dist = distHaversine(mapOptions.center, newSpot);
var zoom = 10;
if(dist < 1.25) {
zoom = 15;
} else if(dist < 2.5) {
zoom = 14;
} else if(dist < 5) {
zoom = 13;
} else if(dist < 10) {
zoom = 12;
} else if(dist < 20) {
zoom = 11;
}
map.setZoom(zoom);
}
rad = function(x) {return x*Math.PI/180;}
distHaversine = function(p1, p2) {
var R = 6371; // earth's mean radius in km
var dLat = rad(p2.lat() - p1.lat());
var dLong = rad(p2.lng() - p1.lng());
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(rad(p1.lat())) * Math.cos(rad(p2.lat())) * Math.sin(dLong/2) * Math.sin(dLong/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
return d.toFixed(3);
}
function deleteOverlays() {
if (markersArray) {
for (i in markersArray) {
markersArray[i].setMap(null);
}
markersArray = [];
markersArray.length = 0;
}
}
function placeMarker(location, alt_icon) {
var marker = new google.maps.Marker({
position: location,
map: map
});
// add marker in markers array
markersArray.push(marker);
}
It turns out that when you call map.getBounds()
it returns the viewport with a little margin around the edges. Since this new bounds is larger than the current bounds, the map will always zoom out. I was able to solve it by avoiding using the current map's bounds altogether and maintaining a separate LatLngBounds variable. Every time I added a point I called:
markersBounds.extend(newMarkersLatLng);
map.fitBounds(markersBounds);
map.panToBounds(markersBounds);
To remove points (I was always adding them), you could make a new LatLngBounds object with the first point, then extend each remaining point to get your new bounds:
var markersBounds = new google.maps.LatLngBounds(markers[0].getPosition(),
markers[0].getPosition());
for(var i=1; i<markers.length; i++){
markersBounds.extend(markers[i].getPosition());
}
map.fitBounds(markersBounds);
map.panToBounds(markersBounds);
I encountered this general problem today, thought I'd share a solution. I realise this is slightly different to your 'store locator' problem, but it does apply in many ways. In my case I have a collection of markers on the map and am adding one, and want to ensure all markers are now in view. This code checks each existing marker to see if its in the view, and along the way, builds a new bounding box that would contain them all, if required. At the end, if any have been found to not be in view, the view is reset so they all are.
(this uses Dojo's array module, simply a convenience wrapper around basic array iteration)
var origBounds = map.getBounds(),
newBounds = new google.maps.LatLngBounds(null),
anyFailed = false;
array.forEach(markers, function (m) {
newBounds.extend(m.position);
if (!origBounds.contains(m.position)) {
anyFailed = true;
}
});
if (anyFailed) {
map.setCenter(newBounds.getCenter());
map.fitBounds(newBounds);
}
One could easily modify this to only ensure the new marker is in view, by not looping and just doing a contains() check on the new marker.
Check this quote from the GMap docs on marker.setMap(null);
(which is used for removing markers from a map):
Note that the above method does not delete the marker. It removes the marker from the map. If instead you wish to delete the marker, you should remove it from the map, and then set the marker itself to null.
My problem was that when I was removing markers from the map they were still stored in map.markers
. So when setting bounds
it is grabbing all previous markers also:
map.markers.forEach( (marker) => {
var latlng = new google.maps.LatLng(marker.position.lat(), marker.position.lng())
bounds.extend(latlng)
})
Console.logging bounds
showed that the bounds would always accommodate all earlier bounds because of this.
My solution was to keep track of the markers in a seperate markers
array/object that stores a unique key for each marker (in my case linked to a places autocomplete input field) and remove a marker from it when removing the marker from a map. This way I can run a check when generating bounds
to see if the current marker in map.markers
is in the (up-to-date) markers
array.
map.markers.forEach( (marker) => {
// check if map marker is in this.markers (to make sure only current)
for (const [key, value] of Object.entries(markers)) {
if (marker == value) {
var latlng = new google.maps.LatLng(marker.position.lat(), marker.position.lng())
bounds.extend(latlng)
}
}
})
Now my map zooms in correctly after changing the markers. If anyone can come up with any improvements to above method I would love to hear it!
Rens
map.setZoom(10);
I believe the acceptable range is 1-20. Whole numbers only, decimals broke the map in my experience.
fitBounds: function(bounds, mapId)
{
//bounds: bounds, id: map id
if (bounds==null) return false;
maps[mapId].fitBounds(bounds);
},
This should help, i use this method and works fine, on map v2 a little bit different way.
Make sure there are no CSS animations on the width or height of your map. The animations interfere with the zoom behaviour of fitBound()
I spent most of the day on this problem yesterday, and I encountered this StackOverflow post at least a dozen times. But I eventually had to find the problem the hard way on my own.
Deleting the transition: all 0.5s ease;
line below fixed it for me.
.google-map {
width: 100%;
height: 0px;
transition: all 0.5s ease;
}
.google-map.loaded {
height: 400px;
}
精彩评论