Google Maps V3 – So berechnen Sie die Zoomstufe für eine bestimmte Grenze

Lesezeit: 12 Minuten

Benutzeravatar von Nick Clark
Nick Clark

Ich suche nach einer Möglichkeit, die Zoomstufe für bestimmte Grenzen mithilfe der Google Maps V3-API zu berechnen, ähnlich wie getBoundsZoomLevel() in der V2-API.

Folgendes möchte ich tun:

// These are exact bounds previously captured from the map object
var sw = new google.maps.LatLng(42.763479, -84.338918);
var ne = new google.maps.LatLng(42.679488, -84.524313);
var bounds = new google.maps.LatLngBounds(sw, ne);
var zoom = // do some magic to calculate the zoom level

// Set the map to these exact bounds
map.setCenter(bounds.getCenter());
map.setZoom(zoom);

// NOTE: fitBounds() will not work

Leider kann ich das nicht nutzen fitBounds() Methode für meinen speziellen Anwendungsfall. Es eignet sich gut zum Einpassen von Markierungen auf der Karte, nicht jedoch zum Festlegen genauer Grenzen. Hier ist ein Beispiel dafür, warum ich das nicht verwenden kann fitBounds() Methode.

map.fitBounds(map.getBounds()); // not what you expect

  • Sorry, falsche Frage verlinkt, das ist der richtige Link.

    – Tomas

    24. März 2012 um 8:09


  • Diese Frage ist kein Duplikat der anderen Frage. Die Antwort auf die andere Frage lautet: verwenden fitBounds(). Bei dieser Frage geht es darum, was wann zu tun ist fitBounds() ist unzureichend – entweder weil es zu stark zoomt oder Sie nicht zoomen möchten (dh Sie möchten nur die Zoomstufe).

    – John S

    9. Mai 2014 um 15:31


  • @Nick Clark: Woher wissen Sie, welche sw, ne-Grenzen festgelegt werden müssen? Wie hast du sie vorher eingefangen?

    – Varaprakash

    30. Mai 2014 um 22:24


Benutzeravatar von John S
John S

Vielen Dank an Giles Gardam für seine Antwort, die sich jedoch nur auf den Längengrad und nicht auf den Breitengrad bezieht. Eine vollständige Lösung sollte die für den Breitengrad und den Längengrad erforderliche Zoomstufe berechnen und dann die kleinere (weiter entfernte) der beiden verwenden.

Hier ist eine Funktion, die sowohl Breiten- als auch Längengrad verwendet:

function getBoundsZoomLevel(bounds, mapDim) {
    var WORLD_DIM = { height: 256, width: 256 };
    var ZOOM_MAX = 21;

    function latRad(lat) {
        var sin = Math.sin(lat * Math.PI / 180);
        var radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
        return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;
    }

    function zoom(mapPx, worldPx, fraction) {
        return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.LN2);
    }

    var ne = bounds.getNorthEast();
    var sw = bounds.getSouthWest();

    var latFraction = (latRad(ne.lat()) - latRad(sw.lat())) / Math.PI;

    var lngDiff = ne.lng() - sw.lng();
    var lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;

    var latZoom = zoom(mapDim.height, WORLD_DIM.height, latFraction);
    var lngZoom = zoom(mapDim.width, WORLD_DIM.width, lngFraction);

    return Math.min(latZoom, lngZoom, ZOOM_MAX);
}

Demo auf jsfiddle

Parameter:

Der Parameterwert „bounds“ sollte a sein google.maps.LatLngBounds Objekt.

Der Parameterwert „mapDim“ sollte ein Objekt mit den Eigenschaften „height“ und „width“ sein, die die Höhe und Breite des DOM-Elements darstellen, das die Karte anzeigt. Möglicherweise möchten Sie diese Werte verringern, wenn Sie die Polsterung sicherstellen möchten. Das heißt, Sie möchten möglicherweise nicht, dass Kartenmarkierungen innerhalb der Grenzen zu nahe am Kartenrand liegen.

Wenn Sie die jQuery-Bibliothek verwenden, ist die mapDim Der Wert kann wie folgt ermittelt werden:

var $mapDiv = $('#mapElementId');
var mapDim = { height: $mapDiv.height(), width: $mapDiv.width() };

Wenn Sie die Prototype-Bibliothek verwenden, kann der MapDim-Wert wie folgt ermittelt werden:

var mapDim = $('mapElementId').getDimensions();

Rückgabewert:

Der Rückgabewert ist die maximale Zoomstufe, bei der noch die gesamten Grenzen angezeigt werden. Dieser Wert liegt zwischen 0 und die maximale Zoomstufe, inklusive.

Die maximale Zoomstufe beträgt 21. (Ich glaube, für Google Maps API v2 waren es nur 19.)


Erläuterung:

Google Maps verwendet eine Mercator-Projektion. In einer Mercator-Projektion haben die Längengrade den gleichen Abstand, die Breitengrade jedoch nicht. Der Abstand zwischen den Breitengraden nimmt zu, wenn sie vom Äquator zu den Polen verlaufen. Tatsächlich tendiert die Entfernung gegen Unendlich, wenn sie die Pole erreicht. Eine Google Maps-Karte zeigt jedoch keine Breitengrade über etwa 85 Grad Nord oder unter etwa -85 Grad Süd an. (Referenz) (Ich berechne den tatsächlichen Grenzwert bei +/-85,05112877980658 Grad.)

Dies macht die Berechnung der Brüche für die Grenzen für den Breitengrad komplizierter als für den Längengrad. Ich habe ein verwendet Formel aus Wikipedia um den Breitengradanteil zu berechnen. Ich gehe davon aus, dass dies mit der von Google Maps verwendeten Projektion übereinstimmt. Schließlich enthält die Google Maps-Dokumentationsseite, auf die ich oben verlinke, einen Link zu derselben Wikipedia-Seite.

Weitere Hinweise:

  1. Die Zoomstufen reichen von 0 bis zur maximalen Zoomstufe. Zoomstufe 0 ist die vollständig verkleinerte Karte. Höhere Ebenen vergrößern die Karte weiter. (Referenz)
  2. Bei Zoomstufe 0 kann die gesamte Welt in einer Fläche von 256 x 256 Pixeln dargestellt werden. (Referenz)
  3. Mit jeder höheren Zoomstufe verdoppelt sich die Anzahl der Pixel, die zur Darstellung desselben Bereichs sowohl in der Breite als auch in der Höhe benötigt werden. (Referenz)
  4. Karten werden in Längsrichtung umbrochen, jedoch nicht in Breitenrichtung.

  • @John S – Das ist eine fantastische Lösung und ich denke darüber nach, sie anstelle der nativen FitBounds-Methode von Google Maps zu verwenden, die mir ebenfalls zur Verfügung steht. Mir ist aufgefallen, dass fitBounds manchmal eine Zoomstufe zurückgeht (herausgezoomt), aber ich gehe davon aus, dass das an der Polsterung liegt, die es hinzufügt. Besteht der einzige Unterschied zwischen dieser und der fitBounds-Methode nur darin, wie viel Auffüllung Sie hinzufügen möchten, um die Änderung der Zoomstufe zwischen den beiden zu erklären?

    – johntrepreneur

    25. Juni 2013 um 19:40 Uhr


  • @johntrepreneur – Vorteil Nr. 1: Sie können diese Methode verwenden, bevor Sie die Karte überhaupt erstellen, sodass Sie deren Ergebnis den anfänglichen Karteneinstellungen zur Verfügung stellen können. Mit fitBoundsmüssen Sie die Karte erstellen und dann auf das Ereignis „bounds_changed“ warten.

    – John S

    25. Juni 2013 um 21:40 Uhr

  • @MarianPaździoch – Für diese Grenzen funktioniert es, siehe hier. Erwarten Sie, dass Sie zoomen können, sodass sich diese Punkte genau an den Ecken der Karte befinden? Dies ist nicht möglich, da es sich bei den Zoomstufen um ganzzahlige Werte handelt. Die Funktion gibt die höchste Zoomstufe zurück, die noch die gesamten Grenzen auf der Karte umfasst.

    – John S

    28. Januar 2015 um 0:16

  • @CarlMeyer – Ich erwähne es in meiner Antwort nicht, aber in einem Kommentar oben erkläre ich, dass ein Vorteil dieser Funktion darin besteht: „Sie können diese Methode verwenden, bevor Sie überhaupt die Karte erstellen.“ Benutzen map.getProjection() würde einen Teil der Mathematik (und der Annahmen über die Projektion) eliminieren, aber es würde bedeuten, dass die Funktion erst aufgerufen werden könnte, nachdem die Karte erstellt und das Ereignis „projection_changed“ ausgelöst wurde.

    – John S

    21. April 2016 um 18:01 Uhr

  • @hugoderhungrige – Siehe Anmerkung Nr. 2 in der Antwort. Wenn eine Google-Karte ganz herausgezoomt wird (auf Stufe 0), wird die ganze Welt in einem Bild mit den Abmessungen 256 x 256 Pixel angezeigt.

    – John S

    27. April 2021 um 22:46 Uhr

Giles Gardams Benutzeravatar
Giles Gardam

Eine ähnliche Frage wurde in der Google-Gruppe gestellt: http://groups.google.com/group/google-maps-js-api-v3/browse_thread/thread/e6448fc197c3c892

Die Zoomstufen sind diskret, wobei sich der Maßstab bei jedem Schritt verdoppelt. Daher können Sie die gewünschten Grenzen im Allgemeinen nicht genau anpassen (es sei denn, Sie haben großes Glück mit der jeweiligen Kartengröße).

Ein weiteres Problem ist das Verhältnis zwischen den Seitenlängen, z. B. können Sie die Grenzen nicht genau an ein dünnes Rechteck innerhalb einer quadratischen Karte anpassen.

Es gibt keine einfache Antwort darauf, wie man genaue Grenzen einpasst, denn selbst wenn Sie bereit sind, die Größe des Kartenteils zu ändern, müssen Sie auswählen, auf welche Größe und entsprechende Zoomstufe Sie wechseln möchten (grob gesagt: Machen Sie es größer oder kleiner?). als es derzeit ist?).

Wenn Sie den Zoom wirklich berechnen müssen, anstatt ihn zu speichern, sollte dies ausreichen:

Die Mercator-Projektion verzerrt den Breitengrad, aber jeder Längengradunterschied stellt immer den gleichen Bruchteil der Breite der Karte dar (Winkelunterschied in Grad / 360). Bei Zoom Null ist die gesamte Weltkarte 256 x 256 Pixel groß und beim Zoomen jeder Stufe werden Breite und Höhe verdoppelt. Nach ein wenig Algebra können wir den Zoom also wie folgt berechnen, vorausgesetzt, wir kennen die Breite der Karte in Pixel. Beachten Sie, dass wir sicherstellen müssen, dass der Winkel positiv ist, da der Längengrad umläuft.

var GLOBE_WIDTH = 256; // a constant in Google's map projection
var west = sw.lng();
var east = ne.lng();
var angle = east - west;
if (angle < 0) {
  angle += 360;
}
var zoom = Math.round(Math.log(pixelWidth * 360 / angle / GLOBE_WIDTH) / Math.LN2);

  • Müsste man das nicht für die Breite wiederholen und dann die Minute des Ergebnisses der 2 wählen? Ich glaube nicht, dass dies für große, schmale Grenzen funktionieren würde …..

    – Weißatom

    25. März 2012 um 3:15


  • Funktioniert bei mir hervorragend mit einem Wechsel von Math.round zu Math.floor. Tausend Dank.

    – Pete

    27. April 2012 um 13:54

  • Wie kann das richtig sein, wenn der Spielraum nicht berücksichtigt wird? In der Nähe des Äquators sollte es in Ordnung sein, aber der Maßstab der Karte bei einer bestimmten Zoomstufe ändert sich je nach Breitengrad!

    – Eyal

    17. Mai 2012 um 8:19 Uhr

  • @Pete, guter Punkt, im Allgemeinen möchten Sie wahrscheinlich die Zoomstufe abrunden, damit etwas mehr als gewünscht in die Karte passt, und nicht etwas weniger. Ich habe Math.round verwendet, weil in der OP-Situation der Wert vor dem Runden ungefähr ganzzahlig sein sollte.

    – Giles Gardam

    15. Juli 2012 um 8:16 Uhr

  • Was ist der Wert für pixelWidth?

    – Albert Jegani

    23. Februar 2015 um 14:27

d.raevs Benutzeravatar
d.raev

Für Version 3 der API ist dies einfach und funktioniert:

var latlngList = [];
latlngList.push(new google.maps.LatLng(lat, lng));

var bounds = new google.maps.LatLngBounds();
latlngList.each(function(n) {
    bounds.extend(n);
});

map.setCenter(bounds.getCenter()); //or use custom center
map.fitBounds(bounds);

und einige optionale Tricks:

//remove one zoom level to ensure no marker is on the edge.
map.setZoom(map.getZoom() - 1); 

// set a minimum zoom 
// if you got only 1 marker or all markers are on the same address map will be zoomed too much.
if(map.getZoom() > 15){
    map.setZoom(15);
}

  • Warum nicht beim Initialisieren der Karte eine Mindestzoomstufe festlegen, etwa so: var mapOptions = { maxZoom: 15, };

    – Kusch

    12. August 2014 um 7:22 Uhr


  • @Kush, guter Punkt. Aber maxZoom wird den Benutzer daran hindern Handbuch Zoomen. Mein Beispiel ändert nur den DefaultZoom und auch nur, wenn es nötig ist.

    – d.raev

    12. August 2014 um 9:03 Uhr

  • Wenn Sie „fitBounds“ ausführen, springt es einfach, um die Grenzen anzupassen, anstatt sie aus der aktuellen Ansicht heraus zu animieren. Die großartige Lösung ist die bereits erwähnte Verwendung getBoundsZoomLevel. Auf diese Weise wird es beim Aufruf von setZoom auf die gewünschte Zoomstufe animiert. Von da an ist es kein Problem, panTo auszuführen, und am Ende erhalten Sie eine wunderschöne Kartenanimation, die den Grenzen entspricht

    – Benutzer151496

    26. Februar 2015 um 17:24


  • Animation wird weder in der Frage noch in meiner Antwort besprochen. Wenn Sie nützliche Beispiele zu dem Thema haben, erstellen Sie einfach eine konstruktive Antwort mit einem Beispiel und wie und wann es verwendet werden kann.

    – d.raev

    26. Februar 2015 um 17:46 Uhr

  • Aus irgendeinem Grund zoomt Google Map nicht, wenn setZoom() unmittelbar nach dem Aufruf von map.fitBounds() aufgerufen wird. (gmaps ist derzeit v3.25)

    – Kashiraja

    7. Okt. 2016 um 23:11

Dart-Version:

  double latRad(double lat) {
    final double sin = math.sin(lat * math.pi / 180);
    final double radX2 = math.log((1 + sin) / (1 - sin)) / 2;
    return math.max(math.min(radX2, math.pi), -math.pi) / 2;
  }

  double getMapBoundZoom(LatLngBounds bounds, double mapWidth, double mapHeight) {
    final LatLng northEast = bounds.northEast;
    final LatLng southWest = bounds.southWest;

    final double latFraction = (latRad(northEast.latitude) - latRad(southWest.latitude)) / math.pi;

    final double lngDiff = northEast.longitude - southWest.longitude;
    final double lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;

    final double latZoom = (math.log(mapHeight / 256 / latFraction) / math.ln2).floorToDouble();
    final double lngZoom = (math.log(mapWidth / 256 / lngFraction) / math.ln2).floorToDouble();

    return math.min(latZoom, lngZoom);
  }

Benutzeravatar von photograve
Fotogravur

Hier eine Kotlin-Version der Funktion:

fun getBoundsZoomLevel(bounds: LatLngBounds, mapDim: Size): Double {
        val WORLD_DIM = Size(256, 256)
        val ZOOM_MAX = 21.toDouble();

        fun latRad(lat: Double): Double {
            val sin = Math.sin(lat * Math.PI / 180);
            val radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
            return max(min(radX2, Math.PI), -Math.PI) /2
        }

        fun zoom(mapPx: Int, worldPx: Int, fraction: Double): Double {
            return floor(Math.log(mapPx / worldPx / fraction) / Math.log(2.0))
        }

        val ne = bounds.northeast;
        val sw = bounds.southwest;

        val latFraction = (latRad(ne.latitude) - latRad(sw.latitude)) / Math.PI;

        val lngDiff = ne.longitude - sw.longitude;
        val lngFraction = if (lngDiff < 0) { (lngDiff + 360) / 360 } else { (lngDiff / 360) }

        val latZoom = zoom(mapDim.height, WORLD_DIM.height, latFraction);
        val lngZoom = zoom(mapDim.width, WORLD_DIM.width, lngFraction);

        return minOf(latZoom, lngZoom, ZOOM_MAX)
    }

  • Bitte fügen Sie die Bedeutung von mapDim und WORLD_DIM hinzu und wie bekomme ich sie?

    – Rowan Gontier

    9. April um 9:05 Uhr

Benutzeravatar von xcodesucks123
xcodesucks123

Keine der hoch bewerteten Antworten hat für mich funktioniert. Sie machten verschiedene undefinierte Fehler und berechneten schließlich Inf/Nan für Winkel. Ich vermute, dass sich das Verhalten von LatLngBounds im Laufe der Zeit geändert hat. Auf jeden Fall habe ich festgestellt, dass dieser Code meinen Anforderungen entspricht. Vielleicht kann er jemandem helfen:

function latRad(lat) {
  var sin = Math.sin(lat * Math.PI / 180);
  var radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
  return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;
}

function getZoom(lat_a, lng_a, lat_b, lng_b) {

      let latDif = Math.abs(latRad(lat_a) - latRad(lat_b))
      let lngDif = Math.abs(lng_a - lng_b)

      let latFrac = latDif / Math.PI 
      let lngFrac = lngDif / 360 

      let lngZoom = Math.log(1/latFrac) / Math.log(2)
      let latZoom = Math.log(1/lngFrac) / Math.log(2)

      return Math.min(lngZoom, latZoom)

}

  • Bitte fügen Sie die Bedeutung von mapDim und WORLD_DIM hinzu und wie bekomme ich sie?

    – Rowan Gontier

    9. April um 9:05 Uhr

Benutzeravatar von Valerio Giacosa
Valerio Giacosa

Vielen Dank, das hat mir sehr dabei geholfen, den am besten geeigneten Zoomfaktor für die korrekte Darstellung einer Polylinie zu finden. Ich finde die maximalen und minimalen Koordinaten unter den Punkten, die ich verfolgen muss, und falls der Pfad sehr „vertikal“ ist, habe ich nur ein paar Codezeilen hinzugefügt:

var GLOBE_WIDTH = 256; // a constant in Google's map projection
var west = <?php echo $minLng; ?>;
var east = <?php echo $maxLng; ?>;
*var north = <?php echo $maxLat; ?>;*
*var south = <?php echo $minLat; ?>;*
var angle = east - west;
if (angle < 0) {
    angle += 360;
}
*var angle2 = north - south;*
*if (angle2 > angle) angle = angle2;*
var zoomfactor = Math.round(Math.log(960 * 360 / angle / GLOBE_WIDTH) / Math.LN2);

Eigentlich ist der ideale Zoomfaktor Zoomfaktor-1.

  • ich mochte var zoomfactor = Math.floor(Math.log(960 * 360 / angle / GLOBE_WIDTH) / Math.LN2)-1;. Dennoch sehr hilfreich.

    – Mojowen

    29. Juni 2012 um 20:30 Uhr


1454470cookie-checkGoogle Maps V3 – So berechnen Sie die Zoomstufe für eine bestimmte Grenze

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy