Как ограничить панорамирование в Google Maps API V3?

В V2 был способ ограничения панорамирования/перетаскивания, чтобы карта оставалась в определенных пределах. Как это делается в V3?

Допустим, я хочу, чтобы пользователи смотрели только на Европу. Я уже ограничил масштаб, но если я разрешу перетаскивать (что в данном случае необходимо по другим причинам), тогда пользователь сможет перемещаться за пределы области, которую я хочу показать.

Пожалуйста, приведите рабочий пример или фрагмент кода - я не эксперт по кодированию...

Ответ 1

Я думаю, я немного опоздал на вечеринку, но так как это было именно то, что мне нужно было сейчас, и я улучшил его, я думал, что все равно отправлю ответ.

В обоих ответах Daniel Vassallo и brendo пользователь все равно может использовать pan-control (если он активирован), чтобы отойти от желаемой области. Вещь @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);
});

Сохраняя последнюю действительную позицию непрерывно во время перетаскивания, движение будет просто останавливаться после того, как оно выходит за пределы, вместо того, чтобы возвращаться назад после окончания перетаскивания. ......

Ответ 2

Трюк заключается в прослушивании события dragend, и если карта перетаскивается за пределы разрешенных границ, переместите ее обратно внутрь. Если вы определяете свои разрешенные границы как объект LatLngBounds, вы можете использовать метод contains(), так как он возвращает true, если данный параметр lat/аргумент lng находится в пределах.

Также важно ограничить уровень масштабирования, но, похоже, вы уже это делаете.

Поэтому вы можете попробовать следующий пример:

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

Снимок экрана из приведенного выше примера. В этом случае пользователь не сможет перетащить дальше на юг или на восток:

Google Maps JavaScript API v3 Example: Limit Panning

Ответ 3

Моя версия, основанная на одном из @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/

Ответ 4

Вот хорошее расширение выше, которое будет reset центром карты до последней действительной позиции, прослушивая событие 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);
});

Ответ 5

Лучший способ ограничения - установить уровень масштабирования и центральную точку и отключить элементы управления, такие как масштабирование, прокрутка и т.д., как показано ниже.

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

Ответ 6

Здесь решение, которое объединяет ответ Тома Андерсена и принятый в настоящее время 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));

});

Ответ 7

Существует другой поток по этому вопросу, который тоже очень хорош. Проблема, которую я должен был решить, заключалась в том, что вместо того, чтобы вручную устанавливать границы и проверять локализацию центра, мне нужно установить границу на загрузку страницы, а затем разрешить перетаскивание к краю, если оно увеличено.

Таким образом, я устанавливаю границы панорамирования на загрузку карты один раз. Затем я проверяю, находится ли карта с максимальным увеличением, и если да, верните начальный центр. Если вы увеличили масштаб, я хочу панорамировать EDGE исходных границ, а не просто проверить, содержит ли 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
            )
        );
    });

Ответ 8

Я попробовал ответить от 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));

Ответ 9

Вот простое решение, которое будет работать на мобильных и настольных компьютерах. Он остановит панорамирование карты за пределами максимальной или минимальной широты мира и позволит установить минимальный уровень масштабирования, что может помочь предотвратить появление серых областей с помощью слишком большого масштабирования (в зависимости от размера, установленного для вашей карты):

(Я рекомендую соблюдать осторожность при использовании события center_changed, как это было предложено в принятом ответе HenningJ. В моем случае количество событий, которые это создает, вызвало ошибки в 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 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();
    }
}

Ответ 10

Когда я использую drag или dragend или что-то еще, карта возвращается обратно в разрешенные границы вместо простого ограничения переполнения движения. Просто измените событие на "center_changed", чтобы он не прыгнул так.

Изменено jsfiddle: http://jsfiddle.net/Vjdde/1/

Изменить: не знаю, почему скрипка не создает переполнение стека, но это должно быть, поскольку setCenter снова вызовет center_changed.. Просто смотрите

Ответ 11

Решения здесь оставили мне две проблемы. Прежде всего, если вы нажали клавишу со стрелкой, чтобы она быстро начала панорамирование, когда она попала в край, она не дошла бы до края, потому что из-за ускорения панорамирования, выполняющего большие "шаги", шаг "вышел бы за пределы, поэтому он не принимает последний" шаг ". Поэтому, как только он остановится, вы можете освободить клавишу со стрелкой, а затем снова нажать ее, и она будет кастрюлю чуть дальше. Во-вторых, эти решения не содержали панорамирование после изменения масштаба. Мне удалось решить оба вопроса, и разместил мое решение в другом потоке, но я подумал, что свяжу его здесь, так как это был поток, который сначала меня начал в вправо.

Ответ 12

Это HenningJ, но HenningJ на ios использует 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));
});

Ответ 13

Я отправлю свой ответ в случае, если кто-то заинтересован, потому что я не мог достичь того, что мне нужно, с любым из других решений, размещенных здесь.

Мне нужно было ограничить вертикальные границы (широту) карты, чтобы пользователь не мог передвигаться за пределы широты земли (~ +/- 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 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>

Ответ 14

Я знаю, что я немного опаздываю на вечеринку, но кажется, что с середины 2016 года нет официального способа ограничить видимую область.

Существуют некоторые решения для ограничения границ (некоторые из них в этом вопросе), но для меня у них есть недостаток, поскольку, поскольку они не ограничивают границы точно, чтобы соответствовать виду карты, они ограничивают только центр карты содержащихся в указанных границах. Если вы хотите ограничить границы наложением изображения, подобного мне, это может привести к поведению, показанному ниже, где подледная карта видна под нашим наложением изображения:

введите описание изображения здесь

Чтобы решить эту проблему, я создал библиотеку которая успешно ограничивает границы, поэтому вы не можете выходить из наложения.

Однако, как и другие существующие решения, он имеет "вибрирующую" проблему. Когда пользователь набирает карту достаточно агрессивно, после отпускания левой кнопки мыши карта по-прежнему продолжает панорамирование сама по себе, постепенно замедляя ее. Я всегда возвращаю карту обратно в границы, но это приводит к вибрации, которая оседает через мгновение. Этот эффект панорамирования не может быть остановлен никакими средствами, предоставляемыми Js API на данный момент. Похоже, что до тех пор, пока Google не добавит поддержку чего-то вроде 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>

Ответ 15

Новая версия API Google Map теперь позволяет ограничить границы. Просто укажите границы ограничения при инициализации карты. Например, если вы хотите ограничить границу только Новой Зеландией, это то, как вы это делаете:

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