How do I limit panning in Google maps API V3?

JavascriptGoogle MapsGoogle Maps-Api-3

Javascript Problem Overview


In V2 there was a way to limit panning/dragging so the map stays within certain bounds. How is that done in V3?

Let's say I want the users to only look at Europe. I've already limited the zoom, but if I allow dragging (which I have to in this case, for other reasons) then the user can pan beyond the area I want to show.

Please give working example or code snippet - I'm not an expert coder...

Javascript Solutions


Solution 1 - Javascript

I guess I'm a little bit late to the party, but since this was exactly what I needed just now AND I improved on it, I thought I'd post an answer anyway.

With both the answers of Daniel Vassallo and brendo, the user can still use the pan-control (if it's activated) to move away from the wanted area. The thing @Yauhen.F mentioned in a comment.

So instead of using the dragend event, I use the center_changed event. This is continuously fired during dragging and every time someone uses the pan control.

// bounds of the desired area
var allowedBounds = new google.maps.LatLngBounds(
     new google.maps.LatLng(70.33956792419954, 178.01171875), 
     new google.maps.LatLng(83.86483689701898, -88.033203125)
);
var lastValidCenter = map.getCenter();

google.maps.event.addListener(map, 'center_changed', function() {
	if (allowedBounds.contains(map.getCenter())) {
		// still within valid bounds, so save the last valid position
		lastValidCenter = map.getCenter();
		return; 
	}

	// not valid anymore => return to last valid position
	map.panTo(lastValidCenter);
});

By saving the last valid position continuously during the dragging, the movement will just stop once it's out of bounds, instead of yerking back once the dragging ended. ......

Solution 2 - Javascript

The trick is to listen to the dragend event, and if the map is dragged outside the allowed bounds, move it back inside. If you define your allowed bounds as a LatLngBounds object, you can use the contains() method, since it returns true if the given lat/lng argument is within the bounds.

It is also important to limit the zoom level, but it seems you are already doing this.

Therefore, you may want to try the following example:

<!DOCTYPE html>
<html> 
<head> 
   <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> 
   <title>Google Maps JavaScript API v3 Example: Limit Panning</title> 
   <script type="text/javascript" 
           src="http://maps.google.com/maps/api/js?sensor=false"></script>
</head> 
<body> 
   <div id="map" style="width: 400px; height: 300px;"></div> 

   <script type="text/javascript"> 
   
   var minZoomLevel = 5;

   var map = new google.maps.Map(document.getElementById('map'), {
      zoom: minZoomLevel,
      center: new google.maps.LatLng(38.50, -90.50),
      mapTypeId: google.maps.MapTypeId.ROADMAP
   });
   
   // Bounds for North America
   var allowedBounds = new google.maps.LatLngBounds(
     new google.maps.LatLng(28.70, -127.50), 
     new google.maps.LatLng(48.85, -55.90));
   
   // Listen for the dragend event
   google.maps.event.addListener(map, 'dragend', function() {
     if (allowedBounds.contains(map.getCenter())) return;

     // Out of bounds - Move the map back within the bounds

     var c = map.getCenter(),
         x = c.lng(),
         y = c.lat(),
         maxX = allowedBounds.getNorthEast().lng(),
         maxY = allowedBounds.getNorthEast().lat(),
         minX = allowedBounds.getSouthWest().lng(),
         minY = allowedBounds.getSouthWest().lat();

     if (x < minX) x = minX;
     if (x > maxX) x = maxX;
     if (y < minY) y = minY;
     if (y > maxY) y = maxY;

     map.setCenter(new google.maps.LatLng(y, x));
   });
    
   // Limit the zoom level
   google.maps.event.addListener(map, 'zoom_changed', function() {
     if (map.getZoom() < minZoomLevel) map.setZoom(minZoomLevel);
   });
   
   </script> 
</body> 
</html>

Screenshot from the above example. The user will not be able to drag further south or far east in this case:

Google Maps JavaScript API v3 Example: Limit Panning

Solution 3 - Javascript

My version, based on the one from @HenningJ, but with some modification of the lastValidCenter to allow smooth dragging along the edges of the bounds.

<!DOCTYPE html>
<html>
	<head>
		<style type="text/css">
			html { height: 100% }
			body { height: 100%; margin: 0; padding: 0 }
			#map-canvas { height: 100% }
		</style>
		<script type="text/javascript"
			src="http://maps.google.com/maps/api/js?sensor=false"></script>
		</script>
		<script type="text/javascript">
			function initialize() {
				var mapOptions = {
					center: new google.maps.LatLng(28.70, -127.50),
					zoom: 4,
					mapTypeId: google.maps.MapTypeId.ROADMAP
				};
				var map = new google.maps.Map(document.getElementById("map-canvas"),
						mapOptions);
					
				// bounds of the desired area
				var allowedBounds = new google.maps.LatLngBounds(
				  new google.maps.LatLng(28.70, -127.50),
				  new google.maps.LatLng(48.85, -55.90)
				);
				var boundLimits = {
					maxLat : allowedBounds.getNorthEast().lat(),
					maxLng : allowedBounds.getNorthEast().lng(),
					minLat : allowedBounds.getSouthWest().lat(),
					minLng : allowedBounds.getSouthWest().lng()
				};

				var lastValidCenter = map.getCenter();
				var newLat, newLng;
				google.maps.event.addListener(map, 'center_changed', function() {
					center = map.getCenter();
					if (allowedBounds.contains(center)) {
						// still within valid bounds, so save the last valid position
						lastValidCenter = map.getCenter();
						return;
					}
					newLat = lastValidCenter.lat();
					newLng = lastValidCenter.lng();
					if(center.lng() > boundLimits.minLng && center.lng() < boundLimits.maxLng){
						newLng = center.lng();
					}
					if(center.lat() > boundLimits.minLat && center.lat() < boundLimits.maxLat){
						newLat = center.lat();
					}
					map.panTo(new google.maps.LatLng(newLat, newLng));
				});
			}
			google.maps.event.addDomListener(window, 'load', initialize);
		</script>
	</head>
	<body>
		<div id="map-canvas"/>
	</body>
</html>

Fiddle here: http://jsfiddle.net/koenpunt/n7h6t/

Solution 4 - Javascript

The new version of google map API now provides a way to restrict boundary. Just specify your restriction boundaries when you initialize the map. For example if you want to restrict boundary to Newzeland only, this is how you do it:

  <body>
    <div id="map"></div>
    <script>
      var map;
      var NEW_ZEALAND_BOUNDS = {
        north: -34.36,
        south: -47.35,
        west: 166.28,
        east: -175.81,
      };
      var AUCKLAND = {lat: -37.06, lng: 174.58};

      function initMap() {
        map = new google.maps.Map(document.getElementById('map'), {
          center: AUCKLAND,
          restriction: {
            latLngBounds: NEW_ZEALAND_BOUNDS,
            strictBounds: false,
          },
          zoom: 7,
        });
      }
    </script>
    <script async defer
    src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
    </script>
  </body>

FYI: To restrict the map of the whole world you can use the following bounds. The tricky part here is to use strictBounds:true. This will make sure that the user can not zoom out of the world view:

map = new google.maps.Map(document.getElementById('map'), {
    restriction: {
        latLngBounds: {
            north: 85,
            south: -85,
            west: -180,
            east: 180
        },
        strictBounds: true,
    },
   zoom: 7,
   });

Solution 5 - Javascript

Here is a nice extension to the above that will reset the Map's center to the last valid position by listening to the dragstart event.

// Limit panning
var lastCenter = map.getCenter();
			
google.maps.event.addListener(map, 'dragstart', function() {
    lastCenter = map.getCenter();
});

google.maps.event.addListener(map, 'dragend', function() {
    if(allowedBounds.contains(map.getCenter())) return;
	
    map.setCenter(lastCenter);
});

Solution 6 - Javascript

The best method to restrict is, set the zoom level and center point and disable the controls like zoom, scroll etc like below.

 var latlng = new google.maps.LatLng(18.283078,84.047556);
     var myOptions = {
          zoom: 12,
    	  
          center: latlng,
    	  zoomControl: false,
          mapTypeId: google.maps.MapTypeId.ROADMAP,
    	  scrollwheel: false,
        navigationControl: false,
        mapTypeControl: false,
        scaleControl: false,
        draggable: false,
    	disableDoubleClickZoom: true,
        };
        map = new google.maps.Map(document.getElementById("map_canvas"),   myOptions);

Solution 7 - Javascript

Here's a solution which is a merge together of Tom Andersen's answer and the currently accepted HenningJ answer. The benefits of this is it 1) allows for smoother scrolling along edges (which HenningJ's solution seemed clunky with), and 2) doesn't have any issues when zooming in an out of an area (again HenningJ's answer seemed to break when zooming in and out near the boundaries).

Tom's answer was close to working for me, except it positioned the locked off area into the center of the screen, which wasn't acceptable for the application I was working on.

// bounds of the desired area
var allowedBounds = new google.maps.LatLngBounds(
     new google.maps.LatLng(70.33956792419954, 178.01171875), 
     new google.maps.LatLng(83.86483689701898, -88.033203125)
);
var lastValidCenter = map.getCenter();

google.maps.event.addListener(map, 'center_changed', function() {

    var mapBounds = map.getBounds();
	var mapNe = mapBounds.getNorthEast();
	var mapSw = mapBounds.getSouthWest();
	var center = map.getCenter();

	if( allowedBounds.contains(mapNe) && allowedBounds.contains(mapSw) ) {
		//the user is scrolling within the bounds.
		lastValidCenter = center;
		return;
	}

	//the user has scrolled beyond the edge.

	var mapWidth = mapNe.lng() - mapSw.lng();
	var mapHeight = mapNe.lat() - mapSw.lat();

	var x = center.lng();
	var y = center.lat();

	var maxX = allowedBounds.getNorthEast().lng();
	var maxY = allowedBounds.getNorthEast().lat();
	var minX = allowedBounds.getSouthWest().lng();
	var minY = allowedBounds.getSouthWest().lat();

	//shift the min and max dimensions by 1/2 of the screen size, so the bounds remain at the edge of the screen

	maxX -= (mapWidth / 2);
	minX += (mapWidth / 2);

	maxY -= (mapHeight / 2);
	minY += (mapHeight / 2);


	if (x < minX) {
		x = minX;
	}
	if (x > maxX) {
		x = maxX;
	}
	if (y < minY){
		y = minY;
	}
	if (y > maxY){
		y = maxY;
	}

	map.panTo(new google.maps.LatLng(y, x));

});

Solution 8 - Javascript

There is another thread on the subject that is also very good. The problem I had to solve was that instead of setting boundaries manually and checking center containment, I wanted a boundary set on page load, then allow dragging to the edge if zoomed in.

So I set panning boundaries on map load, once. Then I check if map is still at max zoom and if so, return the initial center. If zoomed in, I want to pan to the EDGE of initial boundaries, not just check if CENTER contained, because that would extend the allowed panning by half the viewport.

Unfortunately, although this gets the job done and works fine when paning slowly, it's a bit jerky if you pan quickly.

If you have suggestions on how this can be avoided, I'd be grateful.

	map = new google.maps.Map( // defaults
		document.getElementById("map22"),
		{
			disableDefaultUI		: true,
			zoomControl				: true,
			zoom					: 7,
			minZoom					: 7,
			maxZoom					: 10,
			center					: new google.maps.LatLng(
				64.99473104134819,
				-19.22332763671875
			),
			mapTypeId				: google.maps.MapTypeId.ROADMAP
		}
	);



	function borders(){
		return {
			maxLat : map.getBounds().getNorthEast().lat(),
			maxLng : map.getBounds().getNorthEast().lng(),
			minLat : map.getBounds().getSouthWest().lat(),
			minLng : map.getBounds().getSouthWest().lng(),
			center : map.getCenter()
		}
	}

	google.maps.event.addListenerOnce(map,'idle',function() {
		limit = borders();
	});

	google.maps.event.addListener(map,'drag',function() {
		if(map.getZoom() == 7) return map.setCenter(limit.center);
		current = borders();
		if( current.maxLng < limit.maxLng && current.minLng > limit.minLng ) activeCenterLng = current.center.lng();
		if( current.maxLat < limit.maxLat && current.minLat > limit.minLat ) activeCenterLat = current.center.lat();
		map.setCenter(
			new google.maps.LatLng(
				activeCenterLat,
				activeCenterLng
			)
		);
	});

Solution 9 - Javascript

I tried the answer from HenningJ and the map wouldn't stop panning until the center was in a corner of the bounds which was not ideal. Here is my solution:

google.maps.event.addListener(map, 'center_changed', function() {
    var mapBounds = map.getBounds();
    if(allowedBounds.contains(mapBounds.getNorthEast()) && allowedBounds.contains(mapBounds.getSouthWest())) {
        lastCenter = map.getCenter();
        return;
    }

    map.panTo(lastCenter);
}, this));

Solution 10 - Javascript

Here's a simple solution that will work across mobile and desktop. It will stop the map panning beyond the world's maximum or minimum latitude, and allows a minimum zoom level to be set, which can help to prevent grey areas becoming visible through zooming too far out (depending on the size you set for your map):

(I recommend caution in using the center_changed event as suggested in the accepted answer by HenningJ. In my case the number of events this creates caused stack overflow errors in Google Chrome. Instead, the 'dragend' event can be used - although this will allow the user to drag outside of the areas, and will then immediately 'snap back' to a valid area of the map).

var lastValidCenter;
var minZoomLevel = 2;

setOutOfBoundsListener();

function setOutOfBoundsListener() {
        google.maps.event.addListener(map, 'dragend', function () {
            checkLatitude(map);
        });
        google.maps.event.addListener(map, 'idle', function () {
            checkLatitude(map);
        });
        google.maps.event.addListener(map, 'zoom_changed', function () {
            checkLatitude(map);
        });
};

function checkLatitude(map) {
    if (this.minZoomLevel) {
        if (map.getZoom() < minZoomLevel) {
            map.setZoom(parseInt(minZoomLevel));
        }
    }

    var bounds = map.getBounds();
    var sLat = map.getBounds().getSouthWest().lat();
    var nLat = map.getBounds().getNorthEast().lat();
    if (sLat < -85 || nLat > 85) {
        //the map has gone beyone the world's max or min latitude - gray areas are visible
        //return to a valid position
        if (this.lastValidCenter) {
            map.setCenter(this.lastValidCenter);
        }
    }
    else {
        this.lastValidCenter = map.getCenter();
    }
}

Solution 11 - Javascript

When I'm using drag or dragend or whatever, the map jumps back into allowed bounds instead of simply restricting overflowing movement. Just change the event to 'center_changed' to stop it from jumping around like that.

Modified jsfiddle: http://jsfiddle.net/Vjdde/1/

Edit: Not sure why the fiddle doesn't produce a stack overflow but it should, since setCenter will call center_changed again.. Just watch out

Solution 12 - Javascript

The solutions here left me with 2 issues. First of all, if you held down the arrow key so that it started panning rapidly, when it hit the edge, it wouldn't go all the way to the edge because due to the panning acceleration taking large "steps", the next "step" would have gone outside the bounds, so it doesn't take that last "step". So once it stopped, you could release the arrow key and then press it again, and it would pan just a little bit further. Second, these solutions didn't properly contain the panning after a zoom change. I managed to solve both, and posted my solution in another thread, but I thought I'd link it here, since this was the thread that first got me started in the right direction.

Solution 13 - Javascript

This is HenningJ - but HenningJ on ios uses lastValidCentre - which is not good, as its old, so I clamp, which makes it better on iOS.

var mapObject = plugin_map.gmap3('get');
var allowedBounds = new google.maps.LatLngBounds(
     new google.maps.LatLng(41.3957556, -88.4345472), 
     new google.maps.LatLng(49.4010417, -78.4286389)
);
google.maps.event.addListener(mapObject, 'center_changed', function() {
    if (allowedBounds.contains(mapObject.getCenter())) {
        // still within valid bounds, so save the last valid position
        return; 
    }

    // not valid anymore => return to last valid position
     var c = mapObject.getCenter(),
         x = c.lng(),
         y = c.lat(),
         maxX = allowedBounds.getNorthEast().lng(),
         maxY = allowedBounds.getNorthEast().lat(),
         minX = allowedBounds.getSouthWest().lng(),
         minY = allowedBounds.getSouthWest().lat();

     if (x < minX) x = minX;
     if (x > maxX) x = maxX;
     if (y < minY) y = minY;
     if (y > maxY) y = maxY;

     //mapObject.setCenter(new google.maps.LatLng(y, x));
     mapObject.panTo(new google.maps.LatLng(y, x));
});

Solution 14 - Javascript

I'll post my answer in case anyone's interested because I couldn't achieve what I needed with any of the other solutions posted here.

What I needed was to restrict the vertical bounds (latitude) of the map so that the user would not be able to pan beyond the latitude bounds of the earth (~ +/-85 degrees), but any other bounds would work too.

This approach uses the same center_changed event as described elsewhere and simply fixes the center in case parts of the prohibited bounds are shown.

This mechanism only works if the minimum zoom of the map is set so that zooming out can never show more area than that within the allowed bounds.

Working example: http://jsbin.com/vizihe

function initMap() {
  // sample bounds, can be anything and goes hand in hand with minZoom
  var northBoundary = 40
  var southBoundary = -40
  
  var map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: 0, lng: 0},
    zoom: 4,
    // it's important to set this to a large enough value
    // so that zooming out does not show an area larger than allowed
    minZoom: 4 
  })
  
  map.addListener('center_changed', function () {
    var bounds = map.getBounds();
    var ne = bounds.getNorthEast()
    var sw = bounds.getSouthWest()
    var center = map.getCenter()
    
    if(ne.lat() > northBoundary) {
      map.setCenter({lat: center.lat() - (ne.lat() - northBoundary), lng: center.lng()})
    }
    
    if(sw.lat() < southBoundary) {
      map.setCenter({lat: center.lat() - (sw.lat() - southBoundary), lng: center.lng()})
    }   
  })
}

html, body, #map {
  height: 100%;
  margin: 0;
  padding: 0;
}

<!DOCTYPE html>
<html>
  <head>
<meta name="description" content="limit google map panning">
    <title>Simple Map</title>
    <meta name="viewport" content="initial-scale=1.0">
    <meta charset="utf-8">
  </head>
  <body>
    <div id="map"></div>
    <script src="https://maps.googleapis.com/maps/api/js?callback=initMap"
    async defer></script>
  </body>
</html>

Solution 15 - Javascript

I know I am little late to the party, but it seems that as of middle 2016, there is no official way to restrict viewable area.

There are some solutions to restrict the bounds (some of them in this question) but for me they have a flaw though, because they don't restrict the bounds exactly to fit the map view, they only restrict the map center be contained within the specified bounds. If you want to restrict the bounds to overlaying image like me, this can result in a behavior like illustrated below, where the underlaying map is visible under our image overlay:

enter image description here

To tackle this issue, I have created a library, which successfully restrict the bounds so you cannot pan out of the overlay.

However, as other existing solutions, it has a "vibrating" issue. When the user pans the map aggressively enough, after they release the left mouse button, the map still continues panning by itself, gradually slowing. I always return the map back to the bounds, but that results in kind of vibrating, which settles after a moment. This panning effect cannot be stopped with any means provided by the Js API at the moment. It seems that until google adds support for something like map.stopPanningAnimation() we won't be able to create a smooth experience.

Example using the mentioned library, the smoothest strict bounds experience I was able to get:

function initialise(){
  
  var myOptions = {
     zoom: 5,
     center: new google.maps.LatLng(0,0),
     mapTypeId: google.maps.MapTypeId.ROADMAP,
  };
  var map = new google.maps.Map(document.getElementById('map'), myOptions);
  
  addStrictBoundsImage(map);
}

function addStrictBoundsImage(map){
	var bounds = new google.maps.LatLngBounds(
		new google.maps.LatLng(62.281819, -150.287132),
		new google.maps.LatLng(62.400471, -150.005608));

	var image_src = 'https://developers.google.com/maps/documentation/' +
		'javascript/examples/full/images/talkeetna.png';

	var strict_bounds_image = new StrictBoundsImage(bounds, image_src, map);
}

<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
    google.load("maps", "3",{other_params:"sensor=false"});
</script>
<body style="margin:0px; padding:0px;" onload="initialise()">
	 <div id="map" style="height:500px; width:1000px;"></div>
     <script  type="text/javascript"src="https://raw.githubusercontent.com/matej-pavla/StrictBoundsImage/master/StrictBoundsImage.js"></script>
</body>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionE. TalView Question on Stackoverflow
Solution 1 - JavascriptHenningJView Answer on Stackoverflow
Solution 2 - JavascriptDaniel VassalloView Answer on Stackoverflow
Solution 3 - JavascriptKoen.View Answer on Stackoverflow
Solution 4 - Javascriptconnect2CoderView Answer on Stackoverflow
Solution 5 - JavascriptbrendoView Answer on Stackoverflow
Solution 6 - JavascriptAjayRView Answer on Stackoverflow
Solution 7 - JavascriptGeorge HuberView Answer on Stackoverflow
Solution 8 - JavascripttimView Answer on Stackoverflow
Solution 9 - JavascriptBrandon PetersView Answer on Stackoverflow
Solution 10 - JavascriptChris HalcrowView Answer on Stackoverflow
Solution 11 - JavascriptjohanjView Answer on Stackoverflow
Solution 12 - JavascriptqwertymodoView Answer on Stackoverflow
Solution 13 - JavascriptTom AndersenView Answer on Stackoverflow
Solution 14 - JavascriptSimoneView Answer on Stackoverflow
Solution 15 - JavascriptMatej P.View Answer on Stackoverflow