API Карт Google V3: ограничить границы карт

Я пытаюсь установить границы, где вы можете перетащить карту с помощью API Карт Google V3 Вот решение для V2 http://econym.org.uk/gmap/example_range.htm, которое работает очень хорошо.

Однако с API V3 это не так хорошо: когда вы используете ту же функцию checkbounds(), карта дергается, когда вы достигаете привязки, в то время как map.setCenter() изменяет центр карты.

Как это исправить? Каково решение для API V3?

Ответ 1

У меня была такая же проблема, но это должно было разобраться в ней (это одна и та же функция, событие для прослушивания меняется с "move" или "drag" на "center_changed", работает как шарм!:

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

function checkBounds() {    
    if(! allowedBounds.contains(map.getCenter())) {
      var C = map.getCenter();
      var X = C.lng();
      var Y = C.lat();

      var AmaxX = allowedBounds.getNorthEast().lng();
      var AmaxY = allowedBounds.getNorthEast().lat();
      var AminX = allowedBounds.getSouthWest().lng();
      var AminY = allowedBounds.getSouthWest().lat();

      if (X < AminX) {X = AminX;}
      if (X > AmaxX) {X = AmaxX;}
      if (Y < AminY) {Y = AminY;}
      if (Y > AmaxY) {Y = AmaxY;}

      map.setCenter(new google.maps.LatLng(Y,X));
    }
}

Ответ 2

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

Одна из проблем с checkBounds() заключается в том, что она не учитывает значения широты, близкие к полюсам север/юг, которые имеют нелинейное искажение, которые ограничивают границы точностью (я использую приблизительные множители магического числа, которые не будут работать во всех ситуациях). По праву, вы должны сначала преобразовать границы в линейные 2d-координаты мира, чтобы увидеть, насколько далеко от границ он находится в плане мировых координат, чем сопоставить фактическую целевую центральную точку в мировой координате с целевым фактическим местоположением широты. Для значений долготы это не похоже на большую проблему, и линейный подход отсечения кажется достаточно точным, основная проблема связана с обертыванием координат долготы, которое учитывается (несколько) в приведенном ниже коде.

// Persitant variables
var allowedBounds;  // assign something here
var lastValidCenter;  // initialize this using map.getCenter()   

function checkBounds() {  // when bounds changes due to resizing or zooming in/out

    var currentBounds = map.getBounds();
    if (currentBounds == null) return;

      var allowed_ne_lng = allowedBounds.getNorthEast().lng();
      var allowed_ne_lat = allowedBounds.getNorthEast().lat();
      var allowed_sw_lng = allowedBounds.getSouthWest().lng();
      var allowed_sw_lat = allowedBounds.getSouthWest().lat();

    var wrap;
    var cc = map.getCenter();
    var centerH = false;
    var centerV = false;

    // Check horizontal wraps and offsets
    if ( currentBounds.toSpan().lng() > allowedBounds.toSpan().lng() ) {
        centerH = true;
    }
    else {  // test positive and negative wrap respectively
        wrap = currentBounds.getNorthEast().lng() < cc.lng();
        var current_ne_lng = !wrap ?   currentBounds.getNorthEast().lng()  : allowed_ne_lng +(currentBounds.getNorthEast().lng() + 180 )  + (180 - allowed_ne_lng);
        wrap = currentBounds.getSouthWest().lng() > cc.lng();
        var current_sw_lng = !wrap ?  currentBounds.getSouthWest().lng() : allowed_sw_lng - (180-currentBounds.getSouthWest().lng()) - (allowed_sw_lng+180);
    }


    // Check vertical wraps and offsets
    if ( currentBounds.toSpan().lat() > allowedBounds.toSpan().lat() ) {
        centerV = true;
    }
    else { // test positive and negative wrap respectively
    wrap = currentBounds.getNorthEast().lat()   < cc.lat();    if (wrap) { alert("WRAp detected top") } // else alert("no wrap:"+currentBounds); wrap = false;
      var current_ne_lat =  !wrap ? currentBounds.getNorthEast().lat()  : allowed_ne_lat + (currentBounds.getNorthEast().lat() +90) + (90 - allowed_ne_lat);
      wrap = currentBounds.getSouthWest().lat() > cc.lat();  if (wrap) { alert("WRAp detected btm") } //alert("no wrap:"+currentBounds);
      var current_sw_lat = !wrap ?  currentBounds.getSouthWest().lat() : allowed_sw_lat - (90-currentBounds.getSouthWest().lat()) - (allowed_sw_lat+90);
    }


      // Finalise positions
      var centerX = cc.lng();
      var centerY = cc.lat();
     if (!centerH) {
        if (current_ne_lng > allowed_ne_lng) centerX -= current_ne_lng-allowed_ne_lng;
        if (current_sw_lng < allowed_sw_lng) centerX += allowed_sw_lng-current_sw_lng;
     }
     else {
         centerX = allowedBounds.getCenter().lng();
     }

     if (!centerV) {
       if (current_ne_lat > allowed_ne_lat) {
           centerY -= (current_ne_lat-allowed_ne_lat) * 3;  // approximation magic numbeer. Adjust as u see fit, or use a more accruate pixel measurement.
       }
       if (current_sw_lat < allowed_sw_lat) {
           centerY += (allowed_sw_lat-current_sw_lat)*2.8;  // approximation magic number
       }
     }
     else {
        centerY = allowedBounds.getCenter().lat();
     }
     map.setCenter(lastValidCenter = new google.maps.LatLng(centerY,centerX));
}



function limitBound(bound) // Occurs during dragging, pass allowedBounds to this function in most cases. Requires persistant 'lastValidCenter=map.getCenter()' var reference.
     {
        var mapBounds = map.getBounds();

         if (   mapBounds.getNorthEast().lng() >=  mapBounds.getSouthWest().lng() && mapBounds.getNorthEast().lat()  >= mapBounds.getSouthWest().lat()  // ensure no left/right, top/bottom wrapping
            && bound.getNorthEast().lat() > mapBounds.getNorthEast().lat()  // top
            && bound.getNorthEast().lng() > mapBounds.getNorthEast().lng() // right
            && bound.getSouthWest().lat() < mapBounds.getSouthWest().lat() // bottom
            && bound.getSouthWest().lng() < mapBounds.getSouthWest().lng()) // left
            {
                lastValidCenter=map.getCenter();  // valid case, set up new valid center location
            }

        //   if (bound.contains(map.getCenter()))
        // {
                map.panTo(lastValidCenter);
             //  }

         }



// Google map listeners

google.maps.event.addListener(map, 'zoom_changed', function() {
    //var zoom = map.getZoom();
    checkBounds();
});

google.maps.event.addListener(map, "bounds_changed", function() {

    checkBounds();
});

google.maps.event.addListener(map, 'center_changed', function() {
      limitBound(allowedBounds);
}); 

p.s Для checkBounds(), чтобы получить правильную координату мира 2d из центра карты, учитывая значения 2 lat/lng, используйте map.getProjection(). fromLatLngToPoint(). Сравните 2 точки, найдите линейную разницу между ними и сопоставьте разницу в мировых координатах назад с lat/lng, используя map.getProjection(). FromPointToLatLng(). Это даст вам точные смещения клипа в единицах lat/lng.

Ответ 3

Этот script получает начальные оценки (allowedBounds) и ограничивает границы на drag и zoom_changed. Также зум ограничен на < 7.

var allowedBounds = false;

google.maps.event.addListener(map, 'idle', function() {
 if (!allowedBounds) {
  allowedBounds = map.getBounds();
 }
});

google.maps.event.addListener(map, 'drag', checkBounds);
google.maps.event.addListener(map, 'zoom_changed', checkBounds); 

function checkBounds() {

 if (map.getZoom() < 7) map.setZoom(7); 

 if (allowedBounds) {

  var allowed_ne_lng = allowedBounds.getNorthEast().lng();
  var allowed_ne_lat = allowedBounds.getNorthEast().lat();
  var allowed_sw_lng = allowedBounds.getSouthWest().lng();
  var allowed_sw_lat = allowedBounds.getSouthWest().lat();

  var currentBounds = map.getBounds();
  var current_ne_lng = currentBounds.getNorthEast().lng();
  var current_ne_lat = currentBounds.getNorthEast().lat();
  var current_sw_lng = currentBounds.getSouthWest().lng();
  var current_sw_lat = currentBounds.getSouthWest().lat();

  var currentCenter = map.getCenter();
  var centerX = currentCenter.lng();
  var centerY = currentCenter.lat();

  if (current_ne_lng > allowed_ne_lng) centerX = centerX-(current_ne_lng-allowed_ne_lng);
  if (current_ne_lat > allowed_ne_lat) centerY = centerY-(current_ne_lat-allowed_ne_lat);
  if (current_sw_lng < allowed_sw_lng) centerX = centerX+(allowed_sw_lng-current_sw_lng);
  if (current_sw_lat < allowed_sw_lat) centerY = centerY+(allowed_sw_lat-current_sw_lat);

  map.setCenter(new google.maps.LatLng(centerY,centerX));
 }
}

Ответ 4

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

google.maps.event.addListenerOnce(map, 'tilesloaded', function() { 
    allowedBounds = map.getBounds();
    google.maps.event.addListener(map,'center_changed',function() { checkBounds(allowedBounds); });
});

// Limit map area
function checkBounds(allowedBounds) { 

if(!allowedBounds.contains(map.getCenter())) {
  var C = map.getCenter();
  var X = C.lng();
  var Y = C.lat();

  var AmaxX = allowedBounds.getNorthEast().lng();
  var AmaxY = allowedBounds.getNorthEast().lat();
  var AminX = allowedBounds.getSouthWest().lng();
  var AminY = allowedBounds.getSouthWest().lat();

  if (X < AminX) {X = AminX;}
  if (X > AmaxX) {X = AmaxX;}
  if (Y < AminY) {Y = AminY;}
  if (Y > AmaxY) {Y = AmaxY;}

  map.setCenter(new google.maps.LatLng(Y,X));
}
}

Ответ 5

southWest = new google.maps.LatLng(48.59475380744011,22.247364044189453);
            northEast = new google.maps.LatLng(48.655344320891444,22.352420806884766);
            var limBound = new google.maps.LatLngBounds(southWest,northEast);
            var lastCenter;

            var option = {zoom:15,
            center: limBound.getCenter(),
            mapTypeId: google.maps.MapTypeId.ROADMAP};
            var map = new google.maps.Map(document.getElementById('divMap'),option);
            google.maps.event.addListener(map,'zoom_changed', function() {
                minZoom(15);
                });
            google.maps.event.addListener(map,'drag',function(e){
                limitBound(limBound);
                });

         function minZoom(minZoom){
                if (map.getZoom()<minZoom)
                {map.setZoom(minZoom);}
             };       

         function limitBound(bound)
         {
             if (bound.getNorthEast().lat() > map.getBounds().getNorthEast().lat()
                && bound.getNorthEast().lng() > map.getBounds().getNorthEast().lng()
                && bound.getSouthWest().lat() < map.getBounds().getSouthWest().lat()
                && bound.getSouthWest().lng() < map.getBounds().getSouthWest().lng())
                {
                    lastCenter=map.getCenter();
                    $('#divText').text(lastCenter.toString());
                    }
                if (bound.contains(map.getCenter()))
                {
                    map.setCenter(lastCenter);
                    }
             }

Ответ 7

@sairafi и @devin

спасибо за ваши ответы. Я мог не заставить это работать в Chrome/Windows 7, потому что проверка .comtains() проверена как true, так и false, как только вы нажмете границу.

Итак, я изменил setCenter() внизу на panTo()

Вторая проблема заключалась в том, что если вы устанавливаете границы начальной загрузки, вам нужно использовать событие google.maps.event.addListenerOnce(map, 'idle'...), иначе он будет восстанавливать границы до текущей просматриваемой карты,

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

В результате получится следующий код:

    google.maps.event.addListenerOnce(map,'idle',function() {
        allowedBounds = map.getBounds();
    });
    google.maps.event.addListener(map,'drag',function() {
        checkBounds(); 
    });
    function checkBounds() {    
        if(! allowedBounds.contains(map.getCenter()))
        {
            var C = map.getCenter();
            var X = C.lng();
            var Y = C.lat();
            var AmaxX = allowedBounds.getNorthEast().lng();
            var AmaxY = allowedBounds.getNorthEast().lat();
            var AminX = allowedBounds.getSouthWest().lng();
            var AminY = allowedBounds.getSouthWest().lat();
            if (X < AminX) {X = AminX;}
            if (X > AmaxX) {X = AmaxX;}
            if (Y < AminY) {Y = AminY;}
            if (Y > AmaxY) {Y = AmaxY;}
            map.panTo(new google.maps.LatLng(Y,X));
        } 
    }