如何限制 Google 地图 API V3 中的平移?

Posted

技术标签:

【中文标题】如何限制 Google 地图 API V3 中的平移?【英文标题】:How do I limit panning in Google maps API V3? 【发布时间】:2011-03-08 16:37:19 【问题描述】:

在 V2 中,有一种方法可以限制平移/拖动,以便地图保持在一定范围内。在 V3 中是如何做到的?

假设我希望用户只关注欧洲。我已经限制了缩放,但是如果我允许拖动(在这种情况下我必须这样做,出于其他原因),那么用户可以平移到我想要显示的区域之外。

请给出工作示例或代码 sn-p - 我不是专业的编码员...

【问题讨论】:

从 v 3.35 开始,您可以使用 restriction 选项来限制地图的视口。查看***.com/a/54717220/5140781中的示例和解释。 【参考方案1】:

诀窍是监听dragend 事件,如果地图被拖到允许范围之外,则将其移回内部。如果您将允许的边界定义为 LatLngBounds 对象,则可以使用 contains() 方法,因为如果给定的 lat/lng 参数在边界内,它将返回 true。

限制缩放级别也很重要,但您似乎已经这样做了。

因此,您可能想尝试以下示例:

<!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>

以上示例的屏幕截图。在这种情况下,用户将无法进一步向南或向东拖动:

【讨论】:

在大地图上(当容器不是太小)默认情况下还有允许用户导航的控件(导航面板)。使用可以按箭头左/右。因此,您的事件不足以解决所有问题。您将需要捕获空闲事件。很好的例子在这里 code.google.com/p/gmaps-api-issues/issues/detail?id=1371 某个人 isaac.z.foster 发布了它。但无论如何,采取行动然后重新设置的逻辑是非常糟糕的。即使是默认情况下谷歌地图在最大缩放级别上显示几个世界的最丑陋的东西。并且标记是重复的。谷歌代码真的很糟糕。 还有关于 minZoomLevel。有 minzoom 地图选项。无需处理事件。【参考方案2】:

这是对the above 的一个很好的扩展,它将通过监听 dragstart 事件将地图的中心重置到最后一个有效位置。

// 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);
);

【讨论】:

【参考方案3】:

我想我参加聚会有点晚了,但由于这正是我刚才所需要的并且我对此进行了改进,所以我想我还是会发布一个答案。

有了Daniel Vassallo 和brendo 的答案,用户仍然可以使用平移控件(如果已激活)离开想要的区域。 @Yauhen.F 在评论中提到的事情。

所以我没有使用 dragend 事件,而是使用 center_changed 事件。在拖动过程中以及每次有人使用平移控件时都会连续触发。

// 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);
);

通过在拖动过程中连续保存最后一个有效位置,移动将在超出范围时停止,而不是在拖动结束后猛拉回来。 ......

【讨论】:

这是一个极好的方法。我喜欢 brendo 的想法,但这会将橡皮筋移回原位,只是将地图停在边界处。我用这个把地图变成静态图像,根本没有平移,但有动态标记。非常便利。谢谢! 太好了,center_changed 事件是关键。但在移动 (ios) 设备上仍然存在一些问题。 有没有人想出如何在移动设备上做到这一点?在移动设备上拖动时似乎没有调度 center_changed 或拖动 我知道这里的代码可以工作,但是在最后一行,map.setCenter(...); 会比map.panTo(...); 更合适吗?后者似乎是潜在的奢侈(CPU),因为它的目的就是阻止平移死在其轨道上。 边界/约束框的边缘似乎随缩放级别而变化...有没有办法针对所有缩放级别修复它?【参考方案4】:

主题上有another thread也很好。我必须解决的问题是,我不想手动设置边界并检查中心包含,而是希望在页面加载时设置边界,然后在放大时允许拖动到边缘。

所以我在地图加载时设置了平移边界,一次。 然后我检查地图是否仍处于最大缩放状态,如果是,则返回初始中心。 如果放大,我想平移到初始边界的边缘,而不仅仅是检查 CENTER 是否包含,因为这会将允许的平移扩展到视口的一半。

不幸的是,虽然这样可以完成工作并且在缓慢平移时工作正常,但如果快速平移会有点生涩。

如果您对如何避免这种情况有任何建议,我将不胜感激。

    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
            )
        );
    );

【讨论】:

【参考方案5】:

当我使用拖动或拖动或其他方式时,地图会跳回到允许的范围内,而不是简单地限制溢出的移动。只需将事件更改为 'center_changed' 即可阻止它像那样跳来跳去。

修改后的jsfiddle:http://jsfiddle.net/Vjdde/1/

编辑:不知道为什么小提琴不会产生堆栈溢出,但它应该,因为 setCenter 将再次调用 center_changed.. 小心

【讨论】:

【参考方案6】:

我尝试了 HenningJ 的答案,地图不会停止平移,直到中心位于不理想的边界角落。这是我的解决方案:

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));

【讨论】:

【参考方案7】:

我的版本基于@HenningJ 的版本,但对lastValidCenter 进行了一些修改,以允许沿边界平滑拖动

<!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>

在这里提琴:http://jsfiddle.net/koenpunt/n7h6t/

【讨论】:

它遇到了与@HenningJ 的解决方案相同的问题。这只是检查地图的中心是否在边界内。如果地图的中心紧邻边界,则地图的一半位于边界之外。因此,需要进行更花哨的计算(对于低缩放级别,中心可能是一个很好的近似值,但不适用于世界观)- 这一定是答案 不太好,好像你仍然希望人们放大/缩小【参考方案8】:

这里的解决方案给我留下了 2 个问题。首先,如果你按住箭头键让它开始快速平移,当它碰到边缘时,它不会一直走到边缘,因为平移加速度采取了很大的“步幅”,下一个“步骤”会超出范围,因此它不会采取最后一个“步骤”。因此,一旦它停止,您可以释放箭头键,然后再次按下它,它会平移一点点。其次,这些解决方案没有正确包含缩放更改后的平移。我设法解决了这两个问题,并发布了我的解决方案in another thread,但我想我会在这里链接它,因为这是第一次让我朝着正确方向开始的线程。

【讨论】:

链接的解决方案对我来说效果不太好。地图移动不顺畅。 @JoseGómez 对我来说工作顺利:zelda.qwertymodo.com/overworld/lightworld【参考方案9】:

最好的限制方法是,设置缩放级别和中心点,并禁用缩放、滚动等控件,如下所示。

 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);

【讨论】:

如果您想完全限制平移和缩放,而不是按照要求“限制在某些范围内”,这是一个很好的解决方案。如果地图已经存在并且您想在创建地图后锁定它,您也可以使用 map.setOptions()。【参考方案10】:

这是 HenningJ - 但 ios 上的 HenningJ 使用 lastValidCentre - 这并不好,因为它是旧的,所以我钳制它,这使它在 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));
);

【讨论】:

【参考方案11】:

这是一个将 Tom Andersen 的答案和当前接受的 HenningJ 答案合并在一起的解决方案。这样做的好处是 1)允许沿边缘更平滑地滚动(HenningJ 的解决方案似乎很笨拙),以及 2)在放大一个区域时没有任何问题(再次,HenningJ 的答案在放大时似乎中断了并在边界附近)。

Tom 的回答对我来说几乎是可行的,除了它将锁定区域定位到屏幕的中心,这对于我正在处理的应用程序来说是不可接受的。

// 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));

);

【讨论】:

【参考方案12】:

如果有人感兴趣,我会发布我的答案,因为我无法通过此处发布的任何其他解决方案实现我所需要的。

我需要的是限制地图的垂直边界(纬度),这样用户就无法平移到地球的纬度边界之外(~ +/- 85 度),但任何其他边界都可以也是。

此方法使用与其他地方描述的相同的 center_changed 事件,并简单地固定中心,以防显示部分禁止边界。

此机制仅在设置了地图的最小缩放比例时才有效,这样缩小显示的区域永远不会超过允许范围内的区域。

工作示例: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>

【讨论】:

【参考方案13】:

我知道我参加聚会有点晚了,但似乎截至 2016 年年中,没有官方方法来限制可视区域。

有一些解决方案可以限制边界(其中一些在这个问题中),但对我来说,它们有一个缺陷,因为它们不会完全限制边界以适应地图视图,它们只会限制地图中心包含在指定的范围内。如果您想像我一样将边界限制为叠加图像,这可能会导致如下所示的行为,其中底层地图在我们的图像叠加层下可见:

为了解决这个问题,我创建了一个library,它成功地限制了边界,所以你不能平移出覆盖层。

但是,与其他现有解决方案一样,它存在“振动”问题。当用户足够积极地平移地图时,释放鼠标左键后,地图仍会继续自行平移,逐渐变慢。我总是将地图返回到边界,但这会导致某种振动,并在片刻后稳定下来。目前无法通过 Js API 提供的任何方式停止这种平移效果。似乎在谷歌添加对诸如 map.stopPanningAnimation() 之类的支持之前,我们将无法创建流畅的体验。

使用上述库的示例,我能获得的最流畅的严格边界体验:

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>

【讨论】:

【参考方案14】:

这是一个适用于移动设备和桌面设备的简单解决方案。它将停止地图平移超出世界的最大或最小纬度,并允许设置最小缩放级别,这有助于防止灰色区域因放大得太远而变得可见(取决于您为地图设置的大小):

(我建议按照 HenningJ 接受的答案中的建议谨慎使用 center_changed 事件。在我的情况下,这会导致 Google Chrome 中的堆栈溢出错误。相反,可以使用 'dragend' 事件 - 尽管这将允许用户拖动到区域之外,然后将立即“弹回”到地图的有效区域)。

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();
    

【讨论】:

【参考方案15】:

新版 google map API 现在提供了一种限制边界的方法。 只需在初始化地图时指定您的限制边界。 例如,如果您只想将边界限制在新西兰,您可以这样做:

  <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>

仅供参考:要限制整个世界的地图,您可以使用以下界限。这里棘手的部分是使用strictBounds:true。这将确保用户无法缩小世界视图:

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

【讨论】:

以上是关于如何限制 Google 地图 API V3 中的平移?的主要内容,如果未能解决你的问题,请参考以下文章

Google 地图 api V3 中的 fitbounds() 不适合边界

如何使用 API V3 在每页显示多个 Google 地图

如何使用 Google Maps JavaScript API v3 在谷歌地图中获取查看区域中心的坐标

Google Maps API v3:如何将缩放级别和地图中心设置为用户提交的位置?

Google Maps API v3 将地图重新​​定位到标记

Google Maps API V3 使用限制是按网站访问者还是按 Web 服务器?