wake-up-neo.net

Google Maps API 3 fitBounds-Auffüllung - Stellen Sie sicher, dass Markierungen nicht durch überlagerte Steuerelemente verdeckt werden

Ich möchte einer Kartenansicht nach dem Aufrufen von map.fitBounds() einen Abstand hinzufügen können, damit alle Markierungen sichtbar sind, unabhängig von Kartensteuerelementen oder Elementen wie Schiebereglern, die Markierungen beim Öffnen verdecken. Leaftlet verfügt über eine Option, mit der fitBounds aufgefüllt werden kann, Google Maps jedoch nicht.

Manchmal verstecken sich die nördlichsten Markierungen teilweise über dem Ansichtsfenster. Die westlichsten Markierungen liegen häufig auch unter dem Zoomregler. Mit API 2 war es möglich, ein virtuelles Ansichtsfenster zu erstellen, indem bestimmte Abstände aus dem Kartenansichtsfenster reduziert und dann die Methode showBounds() aufgerufen wurden, um Zoomen und Zentrieren basierend auf diesem virtuellen Ansichtsfenster zu berechnen und durchzuführen:

map.showBounds(bounds, {top:30,right:10,left:50});

Ein funktionierendes Beispiel für API 2 finden Sie hier unter dem Beispiellink showBounds ().

Ich kann ähnliche Funktionen in API V3 nicht finden, aber es gibt hoffentlich einen anderen Weg, wie dies erreicht werden kann. Vielleicht könnte ich die Nordost- und Südwestpunkte erfassen und dann falsche Koordinaten hinzufügen, um die Grenzen weiter zu erweitern, nachdem ich sie eingeschlossen habe?

AKTUALISIEREN

( Codepen falls der Code unten nicht funktioniert)

function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    draggable: true,
    streetViewControl: false,
    zoomControl: false
  });

  var marker1 = new google.maps.Marker({
    position: {lat: 37, lng: -121},
    map: map,
  });

  var marker2 = new google.maps.Marker({
    position: {lat: 39.3, lng: -122},
    map: map,
  });
 
  var bounds = new google.maps.LatLngBounds();
  bounds.extend(marker1.position);
  bounds.extend(marker2.position);
  map.fitBounds(bounds);
}
#map {
  height: 640px;
  width: 360px;
}
#overlays {
  position: absolute;
  height: 50px;
  width: 340px;
  background: white;
  margin: -80px 10px;
  text-align: center;
  line-height: 50px;
}
/* Optional: Makes the sample page fill the window. */
html, body {
  height: 100%;
  margin: 0;
  padding: 0;
}
<html>
  <head>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
    <meta charset="utf-8">
    <title>Simple markers</title>

  </head>
  <body>
    <div id="map"></div>
    <div id="overlays">Controls / Order pizza / ETA / etc.</div>
  
    <script async defer
    src="https://maps.googleapis.com/maps/api/js?&callback=initMap">
    </script>
  </body>
</html>

Das Problem ist folgendes:

 enter image description here

Ich habe versucht, ein Steuerelement hinzuzufügen, wie unter Benutzerdefinierte Steuerelemente dokumentiert, aber die Karte weiß es nicht genau - siehe diese Geige aus dem Beispiel für benutzerdefinierte Steuerelemente für Maps . Einer der Marker wird von der Steuerung immer noch verdeckt.

25
jsurf

Dies ist eine Art Hack-ish-Lösung, aber nach der fitBounds können Sie eine Stufe herauszoomen, damit Sie genügend Abstand für Ihre Marker haben.

Angenommen, map variable ist Ihr Verweis auf das Kartenobjekt.

map.setZoom(map.getZoom() - 1);
23
keune

Ab Juni 2017 unterstützt die Maps JavaScript-API den Auffüllparameter in der fitBounds () -Methode.

fitBounds(bounds:LatLngBounds|LatLngBoundsLiteral, padding?:number)

Weitere Einzelheiten entnehmen Sie bitte der Dokumentation

https://developers.google.com/maps/documentation/javascript/reference#Map

11
xomena

Ich habe dieses Problem gelöst, indem ich die Kartengrenzen um eine Latlng erweitert habe, die die Markierungen ausreichend in Sichtweite gebracht hat.

Zunächst müssen Sie eine Überlagerungsansicht erstellen

var overlayHelper = new google.maps.OverlayView();
overlayHelper.onAdd = function() {};
overlayHelper.onRemove = function() {};
overlayHelper.draw = function() {};
overlayHelper.setMap(map);

Sobald Sie einen Overlay-Helfer haben, müssen Sie die Kartenprojektion abrufen und darauf aufbauend Berechnungen durchführen.

Beachten Sie, dass das Steuerelement, über das ich auf meiner Karte verfüge, ein 420 Pixel breiter, 100% -iger Höhenunterschied ganz rechts auf der Karte ist. Sie müssen den Code natürlich ändern, um Ihre Steuerelemente unterzubringen.

var mapCanvas = $("#map_canvas"),
    controlLeft = mapCanvas.width() - 420, // canvas width minus width of the overlayed control
    projection = overlayHelper.getProjection(),
    widestPoint = 0, 
    latlng, 
    point;

// the markers were created elsewhere and already extended the bounds on creation
map.fitBounds(mapBounds);

// check if any markers are hidden behind the overlayed control
for (var m in markers) {
    point = projection.fromLatLngToContainerPixel(markers[m].getPosition());
    if (point.x > controlLeft && point.x > widestPoint) {
        widestPoint = point.x;
    }
}

if (widestPoint > 0) {
    point = new google.maps.Point(
                mapCanvas.width() + (widestPoint - controlLeft), 
                mapCanvas.height() / 2); // middle of map height, since we only want to reposition bounds to the left and not up and down

    latlng = projection.fromContainerPixelToLatLng(point);
    mapBounds.extend(latlng);
    map.fitBounds(mapBounds);
}

Wenn Sie dies tun, wenn die Karte zum ersten Mal geladen wird, müssen Sie dies in ein Kartenereignis einschließen, um auf Leerlauf zu warten. Dadurch kann die Überlagerungsansicht initialisiert werden. Fügen Sie die Overlay-Helper-Erstellung nicht in den Ereignis-Callback ein.

google.maps.event.addListenerOnce(map, 'idle', function() { <above code> });
10
Seain Malkin

Aktualisierte

Die Google Maps API unterstützt jetzt einen nativen "Auffüll" -Parameter in der fitBounds -Methode (ab Version 3.32, korrigieren Sie mich, falls dies früher der Fall war).

Ich hatte noch keine Chance, es zu testen, aber wenn Sie ein Upgrade durchführen können, würde ich empfehlen, eine native Methode zu verwenden. Wenn Sie Version <3.32 verwenden und kein Upgrade durchführen können, ist meine Lösung für Sie.


Ich habe working solution von erzzo genommen und ein bisschen verbessert.
Beispiel

fitBoundsWithPadding(googleMapInstance, PolygonLatLngBounds, {left:250, bottom:10});

Argumente Beschreibung:

  1. gMap - Google Map-Instanz
  2. grenzen - Google Maps LatLngBounds Objekt zu passen
  3. paddingXY - Objektliteral: 2 mögliche Formate:
    • {x, y} - für horizontale und vertikale Auffüllungen (x = links = rechts, y = oben = unten)
    • {links, rechts, oben, unten}

Funktionsliste zum Kopieren

function fitBoundsWithPadding(gMap, bounds, paddingXY) {
        var projection = gMap.getProjection();
        if (projection) {
            if (!$.isPlainObject(paddingXY))
                paddingXY = {x: 0, y: 0};

            var paddings = {
                top: 0,
                right: 0,
                bottom: 0,
                left: 0
            };

            if (paddingXY.left){
                paddings.left = paddingXY.left;
            } else if (paddingXY.x) {
                paddings.left = paddingXY.x;
                paddings.right = paddingXY.x;
            }

            if (paddingXY.right){
                paddings.right = paddingXY.right;
            }

            if (paddingXY.top){
                paddings.top = paddingXY.top;
            } else if (paddingXY.y) {
                paddings.top = paddingXY.y;
                paddings.bottom = paddingXY.y;
            }

            if (paddingXY.bottom){
                paddings.bottom = paddingXY.bottom;
            }

            // copying the bounds object, since we will extend it
            bounds = new google.maps.LatLngBounds(bounds.getSouthWest(), bounds.getNorthEast());

            // SW
            var point1 = projection.fromLatLngToPoint(bounds.getSouthWest());


            // we must call fitBounds 2 times - first is necessary to set up a projection with initial (actual) bounds
            // and then calculate new bounds by adding our pixel-sized paddings to the resulting viewport
            gMap.fitBounds(bounds);

            var point2 = new google.maps.Point(
                ( (typeof(paddings.left) == 'number' ? paddings.left : 0) / Math.pow(2, gMap.getZoom()) ) || 0,
                ( (typeof(paddings.bottom) == 'number' ? paddings.bottom : 0) / Math.pow(2, gMap.getZoom()) ) || 0
            );

            var newPoint = projection.fromPointToLatLng(new google.maps.Point(
                point1.x - point2.x,
                point1.y + point2.y
            ));

            bounds.extend(newPoint);

            // NE
            point1 = projection.fromLatLngToPoint(bounds.getNorthEast());
            point2 = new google.maps.Point(
                ( (typeof(paddings.right) == 'number' ? paddings.right : 0) / Math.pow(2, gMap.getZoom()) ) || 0,
                ( (typeof(paddings.top) == 'number' ? paddings.top : 0) / Math.pow(2, gMap.getZoom()) ) || 0
            );
            newPoint = projection.fromPointToLatLng(new google.maps.Point(
                point1.x + point2.x,
                point1.y - point2.y
            ));

            bounds.extend(newPoint);

            gMap.fitBounds(bounds);
        }
    }
8
Roman86

Sie können map.fitBounds() mit API V3 mit der gleichen Füllsyntax verwenden, die Sie mit map.showBounds() erwähnt haben.

Einfach map.fitBounds(bounds, {top:30,right:10,left:50}); zu benutzen hat bei mir funktioniert.

(Dies könnte ein Kommentar zu xomenas oder Roman86 'Post sein. Ich habe nicht genug Reputation, um einen Kommentar abzugeben.)

4
Milan Švehla

Ich werde eine allgemeinere Lösung für dieses Problem bereitstellen. Wenn wir eine Position haben, z. marker.getPosition(), mit dieser Funktion können wir eine andere Position (x, y) Pixel davon finden.

function extendedLocation(position, x, y) {
    var projection = map.getProjection();
    var newMarkerX = projection.fromLatLngToPoint(position).x + x/(Math.pow(2, map.getZoom()))
    var newMarkerY = projection.fromLatLngToPoint(position).y + y/(Math.pow(2, map.getZoom()))
    var newMarkerPoint = new google.maps.Point(newMarkerX, newMarkerY);
    return projection.fromPointToLatLng(newMarkerPoint)
}

Hinweis: Positives x ist in der richtigen Richtung und positives y ist in der Abwärtsrichtung. Im Allgemeinen müssen wir also einen negativen Wert von y, z. extendedLocation(marker.getPosition(), 4, -18)

Wenn Sie einen beständigen Schieberegler oder ein solches Element über einer Höhe von 30 px haben, verwenden Sie einfach den Parameter y in der Funktion als -30.

Es kann eine allgemeinere Funktion erstellt werden, die ein Array von 4 Punkten zurückgibt, wobei jeder (x, y) Pixel von der angegebenen Richtung nach oben, unten, rechts und links entfernt ist.

function getAllExtendedPosition(position, x, y){
   var positionArray = [];
   postitionArray.Push(extendedLocation(position, x, y);
   postitionArray.Push(extendedLocation(position, -x, -y);
   postitionArray.Push(extendedLocation(position, -x, y);
   postitionArray.Push(extendedLocation(position, x, -y);
   return positionArray
}
1