web-dev-qa-db-fra.com

Comment limiter le panoramique dans Google Maps API V3?

Dans la V2, il existait un moyen de limiter le panoramique/déplacement afin que la carte reste dans certaines limites. Comment cela se fait-il dans la V3? 

Disons que je veux que les utilisateurs ne regardent que l'Europe. J'ai déjà limité le zoom, mais si j'autorise le glissement (ce que je dois faire dans ce cas, pour d'autres raisons), l'utilisateur peut alors effectuer un panoramique au-delà de la zone à afficher.

Veuillez donner un exemple de travail ou un extrait de code - je ne suis pas un codeur expert ... 

Merci!

73
E. Tal

Je suppose que je suis un peu en retard pour la fête, mais comme c'était exactement ce dont j'avais besoin tout à l'heure ET que je l'avais améliorée, je pensais poster une réponse de toute façon.

Avec les réponses de Daniel Vassallo et brendo , l'utilisateur peut toujours utiliser le contrôle panoramique (s'il est activé) pour s'éloigner de la zone souhaitée. La chose @ Yauhen.F mentionnée dans un commentaire.

Donc, au lieu d'utiliser l'événement dragend, j'utilise l'événement center_changed. Ceci est déclenché en continu pendant le glissement et chaque fois que quelqu'un utilise le contrôle de panoramique.

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

En sauvegardant continuellement la dernière position valide pendant le glissement, le mouvement s’arrêtera juste une fois qu’il sera hors limites, au lieu de revenir en arrière une fois le glissement terminé ..

91
HenningJ

L'astuce consiste à écouter l'événement dragend et, si la carte est déplacée en dehors des limites autorisées, déplacez-la à l'intérieur. Si vous définissez vos limites autorisées en tant qu'objet LatLngBounds , vous pouvez utiliser la méthode contains(), car elle renvoie true si l'argument lat/lng donné est dans les limites.

Il est également important de limiter le niveau de zoom, mais il semble que vous le fassiez déjà.

Par conséquent, vous voudrez peut-être essayer l'exemple suivant:

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

Capture d'écran de l'exemple ci-dessus. L'utilisateur ne pourra pas traîner plus au sud ou à l'extrême est dans ce cas:

Google Maps JavaScript API v3 Example: Limit Panning

21
Daniel Vassallo

Ma version, basée sur celle de @HenningJ, mais avec quelques modifications de lastValidCenter pour permettre à smooth de faire glisser le long des limites.

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

9
Koen.

La meilleure méthode pour limiter consiste à définir le niveau de zoom et le point central et à désactiver les commandes telles que le zoom, le défilement, etc., comme ci-dessous.

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

Voici une extension de Nice de la précédente qui réinitialisera le centre de la carte à la dernière position valide en écoutant l'événement 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
brendo

Voici une solution qui est une fusion de la réponse de Tom Andersen et de la réponse actuellement acceptée de HenningJ. Les avantages de ceci sont les suivants 1) permet un défilement plus fluide le long des bords (la solution de HenningJ semblait maladroite), et 2) ne pose aucun problème lors du zoom avant dans une zone (encore une fois, la réponse de HenningJ semble briser et près des limites). 

La réponse de Tom était sur le point de fonctionner pour moi, sauf qu'elle plaçait la zone verrouillée au centre de l'écran, ce qui n'était pas acceptable pour l'application sur laquelle je travaillais.

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

});
2
George Huber

Voici une solution simple qui fonctionnera sur les appareils mobiles et les ordinateurs de bureau. Cela arrêtera le panoramique de la carte au-delà de la latitude maximale ou minimale du monde et permettra de définir un niveau de zoom minimum, ce qui empêchera les zones grises de devenir visibles lorsque vous effectuez un zoom arrière trop grand (en fonction de la taille que vous avez définie pour votre carte):

(Je recommande d'utiliser avec prudence l'événement center_changed comme suggéré dans la réponse acceptée par HenningJ. Dans mon cas, le nombre d'événements que cela crée a provoqué des erreurs de débordement de la pile dans Google Chrome. Au lieu de cela, l'événement "dragend" peut être utilisé - bien que cela permette l'utilisateur à faire glisser en dehors des zones, puis se "ramène" immédiatement dans une zone valide de la carte).

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();
    }
}
1
Chris Halcrow

J’ai essayé la réponse de HenningJ et la carte n’a pas arrêté de se déplacer tant que le centre n’était pas dans un coin, ce qui n’était pas idéal. Voici ma 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));
1
Brandon Peters

Il y a un autre fil sur le sujet qui est également très bon. Le problème que je devais résoudre était qu'au lieu de définir les limites manuellement et de vérifier le confinement du centre, je souhaitais définir une limite lors du chargement de la page, puis permettre le glissement vers le bord si le zoom était effectué.

Donc, je règle les limites de panoramique sur le chargement de la carte, une fois . Ensuite, je vérifie si la carte est toujours au zoom maximal et, dans l'affirmative, renvoie le centre initial . , pas seulement vérifier si CENTER est contenu, car cela prolongerait le panoramique autorisé de la moitié de la fenêtre.

Malheureusement, bien que le travail soit fait et fonctionne correctement lorsque vous effectuez un panoramique lent, c’est un peu saccadé si vous effectuez un panoramique rapide.

Si vous avez des suggestions sur la manière d'éviter cela, je vous en serais reconnaissant.

    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
            )
        );
    });
1
tim

Je posterai ma réponse au cas où quelqu'un serait intéressé, car je ne pouvais pas obtenir ce dont j'avais besoin avec aucune des solutions proposées ici.

J'avais besoin de limiter les limites verticales (latitude) de la carte afin que l'utilisateur ne puisse pas effectuer un panoramique au-delà des limites de latitude de la Terre (~ +/- 85 degrés), mais toute autre limite fonctionnerait également.

Cette approche utilise le même événement center_changed que décrit ailleurs et fixe simplement le centre au cas où des parties des limites interdites sont affichées.

Ce mécanisme ne fonctionne que si le zoom minimum de la carte est défini de manière à ce que le zoom arrière ne puisse jamais afficher plus de zones que celles comprises dans les limites autorisées.

Exemple de travail: 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>
0
Simone

Lorsque j'utilise glisser ou glisser, ou autre, la carte revient dans les limites autorisées au lieu de simplement limiter les mouvements en débordement. Changez simplement l'événement en 'center_changed' pour l'empêcher de sauter comme ça.

Jsfiddle modifié: http://jsfiddle.net/Vjdde/1/

Edit: Vous ne savez pas pourquoi le violon ne provoque pas de débordement de pile, mais cela devrait être le cas, car setCenter appellera de nouveau center_changed. 

0
johanj

Je sais que je suis un peu en retard pour la fête, mais il semble que, à compter du {milieu 2016} _, il n'y ait pas de moyen officiel de restreindre la zone visible. 

Il existe quelques solutions pour limiter les limites (certaines d’entre elles dans cette question), mais pour moi elles ont un défaut, car elles ne restreignent pas les limites pour s’adapter à la vue cartographique, elles limitent uniquement le limites spécifiées. Si vous souhaitez limiter les limites à superposer une image comme moi, cela peut entraîner un comportement similaire à celui illustré ci-dessous, dans lequel la carte sous-jacente est visible sous notre incrustation d'image:

 enter image description here

Pour résoudre ce problème, j'ai créé une bibliothèque library , qui limite avec succès les limites, de sorte que vous ne pouvez pas vous déplacer de la superposition.

Cependant, à l'instar des autres solutions existantes, le problème est "vibrant". Lorsque l'utilisateur effectue un panoramique suffisamment agressif sur la carte, après avoir relâché le bouton gauche de la souris, la carte continue de se déplacer d'elle-même, en ralentissant progressivement. Je ramène toujours la carte dans les limites, mais cela entraîne une sorte de vibration, qui s'installe au bout d'un moment. Cet effet de panoramique ne peut être arrêté avec aucun moyen fourni par l’API Js pour le moment. Il semble que tant que Google n'aura pas pris en charge quelque chose comme map.stopPanningAnimation (), nous ne pourrons pas créer une expérience fluide.

Exemple utilisant la bibliothèque mentionnée, l'expérience la plus douce et stricte que j'ai pu obtenir:

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>

0
Matej P.

Les solutions ici m'ont laissé avec 2 problèmes. Tout d’abord, si vous maintenez la touche fléchée enfoncée pour qu’elle commence à effectuer un panoramique rapide, elle n’irait pas jusqu'au bord, car en raison de l’accélération panoramique prenant de grandes "étapes", la suivante " step "serait sorti des limites, donc il ne faut pas que la dernière" étape ". Donc, une fois que cela s'est arrêté, vous pouvez relâcher la touche fléchée, puis appuyer à nouveau dessus, et le panoramique se poursuivra un peu plus loin. Deuxièmement, ces solutions ne contenaient pas correctement le panoramique après un changement de zoom. J'ai réussi à résoudre les deux et j'ai posté ma solution dans un autre fil , mais je pensais la relier ici, car c'est le fil qui m'a permis de démarrer dans la bonne direction.

0
qwertymodo

C'est HenningJ - mais HenningJ sur iOS utilise lastValidCentre - ce qui n'est pas bon, car il est ancien, alors je bloque, ce qui le rend meilleur sur 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));
});
0
Tom Andersen