Leaflet: Berücksichtigen Sie einen Versatz in der Mitte der Karte.

Erstellt am 31. Juli 2012  ·  31Kommentare  ·  Quelle: Leaflet/Leaflet

Hallo. Es wäre super toll, die Mitte der Karte relativ zum Ansichtsfenster versetzen zu können. Mein Hauptanwendungsfall ist, dass ich eine große Box habe, die die Karte auf der linken Seite überlagert, und möchte, dass die Mitte der Karte weiter rechts liegt. Das bedeutet, dass beim Schwenken oder Zentrieren tatsächlich eine bestimmte Anzahl von Pixeln oder ein bestimmter Prozentsatz des Ansichtsfensters versetzt wird. Dies wäre eine Einstellung auf Kartenebene.

Ich wollte diese Funktion in vielen JS-Mapping-Bibliotheken haben, habe sie aber noch nicht gefunden. Leider muss es in der API ziemlich niedrig sein.

Tut mir leid, ich habe keinen Code, habe nur darüber nachgedacht und wollte ihn runterholen.

Danke für das tolle Projekt!

feature

Hilfreichster Kommentar

So habe ich derzeit ein PanTo mit Offset in unserem Projekt unter https://github.com/codeforamerica/lv-trucks-map (Live-Site unter http://clvfoodtrucks.com/) implementiert. Es ändert das L.map-Prototypobjekt so, dass diese Methode "nativ" für Kartenobjekte verfügbar ist, und sie sollte Ihrem JavaScript-Code hinzugefügt werden, bevor Sie eine Karte erstellen:

L.Map.prototype.panToOffset = function (latlng, offset, options) {
    var x = this.latLngToContainerPoint(latlng).x - offset[0]
    var y = this.latLngToContainerPoint(latlng).y - offset[1]
    var point = this.containerPointToLatLng([x, y])
    return this.setView(point, this._zoom, { pan: options })
}

Es funktioniert genauso wie panTo(), aber mit einem zusätzlichen Parameter, offset , der ein Array in Form von [xOffset, yOffset] ist. Sie könnten dies ändern, um auch eine setViewOffset() -Methode zu erstellen.

Alle 31 Kommentare

Es ist eigentlich ziemlich einfach in Leaflet. Was Sie brauchen, ist die Methode map.containerPointToLatLng . Es wandelt einige Pixelkoordinaten relativ zur oberen linken Ecke der Karte in eine geografische Position um. Also machst du so etwas:

var centerPoint = map.getSize().divideBy(2),
    targetPoint = centerPoint.subtract([overlayWidth, 0]),
    targetLatLng = map.containerPointToLatLng(centerPoint);

map.panTo(targetLatLng);

Löst dies Ihren Anwendungsfall?

Hey. Das tut es irgendwie und ist wahrscheinlich gut genug. Das Problem hier ist jedoch, dass davon ausgegangen wird, dass sich das Ansichtsfenster dort befindet, wo Sie es zentrieren möchten, und es dann versetzt, sodass es sich um eine Bewegung in zwei Schritten handelt. Im Gegensatz zum Aufnehmen eines Lat-Lng, Versetzens und dann Schwenkens; eine einzige Bewegung.

Oh, meine Berechnungen sind nicht korrekt. Sie müssen den tatsächlichen Kartenmittelpunkt so einstellen, dass der Versatzmittelpunkt im Zielpunkt liegt und der Versatz gleich overlayWidth / 2 ist. Hier ist also der Arbeitscode für eine einzelne Bewegung:

var targetPoint = map.project(targetLatLng, targetZoom).subtract([overlayWidth / 2, 0]),
    targetLatLng = map.unproject(targetPoint, targetZoom);

map.setView(targetLatLng, targetZoom);

Dies ist derselbe Fall, den wir auch hatten – eine leicht undurchsichtige Seitenleiste, die über der Karte liegt. Wann immer wir fitBounds() aufgerufen haben, war es ein wenig daneben und wir mussten eine weitere Bewegung ausführen, um es richtig zu machen. Wir haben über eine Pull-Anfrage nachgedacht, aber am Ende haben wir die Seitenleiste einfach so verschoben, dass sie neben der Karte ist, da wir dachten, dass dies ein bisschen Arbeit sein könnte, und nicht der Meinung waren, dass dies ein ausreichend häufiger Anwendungsfall ist.

@mourner Das macht sehr viel Sinn. Ich glaube, ich habe die project () -Methoden verpasst. Dies erledigt die meisten meiner Anwendungsfälle. Danke vielmals.

Wie bei @ajbeaven wäre ein Offset fitBounds für eine (GeoJSON)-Ebene super genial, aber das scheint viel komplizierter zu sein. Ich denke nicht, dass nur das Versetzen gut genug ist, da sich die Grenzen des Ansichtsfensters effektiv ändern, und ich würde auch nicht wissen, wie es in einem Aufruf geht.

In jedem Fall können Sie dies gerne schließen, wenn es keinen Sinn macht, die Bibliothek selbst hinzuzufügen. Ich denke, ich kann mein spezifisches Projekt mit dem von Ihnen bereitgestellten Code verwalten. Danke.

fitBounds ist etwas ausführlicher, aber auch einfach. Kopieren Sie einfach diese https://github.com/CloudMade/Leaflet/blob/master/src/map/Map.js#L275 Methode, ändern Sie die verwendete Größe und verwenden Sie sie dann

var targetZoom = altGetBoundsZoom(map, bounds);
// ... calculate targetLatLng as above
map.setView(targetLatLng, targetZoom);

Hallo. Ich verwende fitBounds mit LatLng[] als Parameter und habe versucht, Code-Snippets zu verwenden, die Sie oben bereitgestellt haben, aber ohne Erfolg.
Mein Web hat eine Seitenleistenüberlagerung, unter der die Karte sichtbar sein soll, aber ich möchte verhindern, dass Markierungen unter dieser Überlagerung erscheinen. (Es wäre großartig, auch die Kartenmitte zu versetzen, aber das kann manuell erfolgen.)

Könnten Sie bitte ein vollständiges Code-Snippet für fitBounds auf _offset map_ posten?
Ich bin ziemlich verwirrt von dem, was du oben geschrieben hast. Das Problem ist, dass ich weder targetLatLng noch targetPoint habe, da ich ein Array von Punkten verwende.

Übrigens. Es wäre wirklich großartig, eine Option zu haben, um die Menge an halb überlagertem Raum auf jeder Seite des Containers zu definieren, wo Sie immer noch die Karte zeichnen würden, aber dort keine Steuerelemente auf Markierungen platzieren würden.

Sie können den Code sehen, den ich hier implementiert habe. (Es ist keineswegs eine großartige Codeinstanz, aber die Idee ist da.) Es ist auch in ein Objekt eingebaut, das eine Karteneigenschaft enthält, die die Leaflet-Karte ist. Es hat die folgenden Methoden:

appSetView
appSetBounds
appGetBoundsZoom

https://github.com/MinnPost/minnpost-my-boundaries/blob/master/visualizations/index.html#L556

Wiedereröffnung, da es nützlich wäre, diese integrierte Funktionalität hinzuzufügen.

Bin auf diese Diskussion gestoßen, da ich auf der linken Seite des Bildschirms eine Liste mit 550 Pixeln und der Karte dahinter haben musste. Ich entschied, dass es wahrscheinlich einfacher wäre, einige Basisfunktionen von L.Map zu überschreiben. Danke für all deine Ideen.

Ich hoffe, ich habe sie alle abgedeckt, aber ich bin mir nicht 100% sicher.

MapCenterOffsetMixin = {
    UIOffset: [550, 0], // x, y
    getBounds: function(){
        var a=this.getPixelBounds(),
            b=this.unproject(new L.Point(a.min.x+this.UIOffset[0],a.max.y+this.UIOffset[1]), this._zoom,!0),
            c=this.unproject(new L.Point(a.max.x,a.min.y),this._zoom,!0);
            return new L.LatLngBounds(b,c)
    },
    _latLngToNewLayerPoint: function (latlng, newZoom, newCenter) {
        var targetPoint = this.project(newCenter, newCenter).subtract([this.UIOffset[0]/2, this.UIOffset[1]/2]),
            newCenter = this.unproject(targetPoint, newZoom);
        var topLeft = this._getNewTopLeftPoint(newCenter, newZoom).add(this._getMapPanePos());
        return this.project(latlng, newZoom)._subtract(topLeft);
    },
    _getCenterLayerPoint: function () {
        return this.containerPointToLayerPoint(this.getSize().divideBy(2).add([this.UIOffset[0]/2, this.UIOffset[1]/2]));
    },
    _resetView: function (a, b, c, d) {
        var e = this._zoom !== b;
        // Change the center
        var targetPoint = this.project(a, b).subtract([this.UIOffset[0] / 2, this.UIOffset[1]/2]),
            a = this.unproject(targetPoint, b);
        d || (this.fire("movestart"), e && this.fire("zoomstart")), this._zoom = b, this._initialTopLeftPoint = this._getNewTopLeftPoint(a);
        if (!c) L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
        else {
            var f = L.DomUtil.getPosition(this._mapPane);
            this._initialTopLeftPoint._add(f)
        }
        this._tileLayersToLoad = this._tileLayersNum, this.fire("viewreset", {
            hard: !c
        }), this.fire("move"), (e || d) && this.fire("zoomend"), this.fire("moveend"), this._loaded || (this._loaded = !0, this.fire("load"))
    }
}

L.Map.include(MapCenterOffsetMixin);

@averrips Danke, dass du das gepostet hast. Ihre Lösung funktioniert derzeit gut für mich ... außer dass die Zoom-Steuerelemente in Firefox jetzt defekt sind :( Ich erhalte eine Art "Catchall" -Fehler: TypeError: t is undefined

Hier ist eine Implementierung von fitBoundsPadded, die in der Karte leben sollte:
Verwenden Sie es wie fitBounds, aber übergeben Sie die gewünschte Auffüllung, also verwenden Sie es wie folgt:

map.fitBoundsPadded(myBounds, new L.Point(0, 0), new L.Point(0, 150));

Und der Code:

    fitBoundsPadded: function (bounds, paddingTopLeft, paddingBottomRight) { //(LatLngBounds, Point, Point)

        var zoom = this.getBoundsZoom(bounds);
        var zoomToTry = zoom;

        while (true) {
            var newTopLeft = this.unproject(this.project(bounds.getNorthEast(), zoomToTry).add(paddingTopLeft), zoomToTry);
            var newBottomRight = this.unproject(this.project(bounds.getSouthWest(), zoomToTry).add(paddingBottomRight), zoomToTry);

            var paddedBounds = new L.LatLngBounds([bounds.getSouthWest(), bounds.getNorthEast(), newTopLeft, newBottomRight]);

            var zoom2 = this.getBoundsZoom(paddedBounds);

            if (zoom2 == zoomToTry) {
                return this.fitBounds(paddedBounds);
            } else {
                zoomToTry--;
            }
        }
    },

Im Master implementiert! Verwenden Sie es wie folgt:

map.fitBounds(bounds, [20, 30], [40, 50]); // bounds, topLeftPadding, bottomRightPadding

Oder übergeben Sie nur das zweite Argument und die Auffüllung unten rechts wird standardmäßig gleich oben links.

Die Signatur hat sich etwas geändert. Jetzt verwenden Sie es wie folgt:

map.fitBounds(bounds, {
    padding: [20, 30]
});

map.fitBounds(bounds, {
    paddingTopLeft: [20, 30],
    paddingbottomRight: [40, 50]
});

Gibt es geplante Arbeiten zum Hinzufügen von Padding zu anderen Funktionen?

Möglicherweise, ja. Für mindestens setView und panTo .

Es sollte eine Abkürzung geben, um auf allen Seiten die gleiche Polsterung anzubringen. Etwas wie das.

map.fitBounds(bounds, { padding: 20 });

Statt der jetzigen...

map.fitBounds(bounds, { padding: [20, 20] });

@averrips Das Mixin war sehr hilfreich, danke.

Ich konnte die Lösung noch nicht finden, also los geht's (ganz einfach):

 Funktion Rezentrierer (Karte, Latlng, Offsetx, Offsety) {
 var center = map.project(latlng);
 center = new L.point(center.x+offsetx,center.y+offsety);
 var target = map.unproject (Mitte);
 map.panTo(Ziel);
 }

Bitte fügen Sie die gleiche Funktionalität auch für "setView" und "panTo" hinzu.
Danke!

Gibt es auch eine Möglichkeit, auch die linken|rechten Steuerelemente aufzufüllen?

So habe ich derzeit ein PanTo mit Offset in unserem Projekt unter https://github.com/codeforamerica/lv-trucks-map (Live-Site unter http://clvfoodtrucks.com/) implementiert. Es ändert das L.map-Prototypobjekt so, dass diese Methode "nativ" für Kartenobjekte verfügbar ist, und sie sollte Ihrem JavaScript-Code hinzugefügt werden, bevor Sie eine Karte erstellen:

L.Map.prototype.panToOffset = function (latlng, offset, options) {
    var x = this.latLngToContainerPoint(latlng).x - offset[0]
    var y = this.latLngToContainerPoint(latlng).y - offset[1]
    var point = this.containerPointToLatLng([x, y])
    return this.setView(point, this._zoom, { pan: options })
}

Es funktioniert genauso wie panTo(), aber mit einem zusätzlichen Parameter, offset , der ein Array in Form von [xOffset, yOffset] ist. Sie könnten dies ändern, um auch eine setViewOffset() -Methode zu erstellen.

Danke, lau!

Als Referenz funktioniert diese Konfiguration für mich, um den horizontalen Versatz zu emulieren, wie er auf der Foursquare-Homepage zu sehen ist.
https://gist.github.com/missinglink/7620340

Danke @louh!

Danke @missinglink , dein Code funktioniert!

Gibt es eine Lösung auch für die Zoom-In/Out-Aktionen?

Ich verwende so etwas:

        var MapCenterOffsetMixin = {
            getMapOffset: function() {
                console.log('getMapOffset');
                return [$('#left-panel').offset().left, 0];
            },
            getBounds: function(){
                console.log('getBounds');
                var offset = this.getMapOffset(),
                    bounds = this.getPixelBounds(),
                    sw = this.unproject(new L.Point(bounds.min.x + offset[0], bounds.max.y + offset[1]), this._zoom, !0),
                    ne = this.unproject(new L.Point(bounds.max.x, bounds.min.y), this._zoom, !0);

                return new L.LatLngBounds(sw, ne)
            },
            _oldLatLngToNewLayerPoint: L.Map.prototype._latLngToNewLayerPoint,
            _latLngToNewLayerPoint: function (latlng, newZoom, newCenter) {
                console.log('_latLngToNewLayerPoint');
                var offset = this.getMapOffset(),
                    targetPoint = this.project(newCenter, newCenter).subtract([offset[0] / 2, offset[1] / 2]);
                newCenter = this.unproject(targetPoint, newZoom);

                return this._oldLatLngToNewLayerPoint(latlng, newZoom, newCenter);
            },
            _getCenterLayerPoint: function () {
                console.log('_getCenterLayerPoint');
                var offset = this.getMapOffset();
                return this.containerPointToLayerPoint(this.getSize().divideBy(2).add([offset[0] / 2, offset[1] / 2]));
            },
            _oldResetView: L.Map.prototype._resetView,
            _resetView: function (center, zoom, preserveMapOffset, afterZoomAnim) {
                console.log('_resetView');
                var offset = this.getMapOffset(),
                    targetPoint = this.project(center, zoom).subtract([offset[0] / 2, offset[1] / 2]);
                center = this.unproject(targetPoint, zoom);

                this._oldResetView(center, zoom, preserveMapOffset, afterZoomAnim);
            }
        };
        L.Map.include(MapCenterOffsetMixin);

aber es schlägt beim Zoomen fehl.

Hier ist die Lösung: https://github.com/Mappy/Leaflet-active-area. Brillant! :)

Die Funktion ‚map.fitBounds(bounds, topLeftPadding, bottomRightPadding)‘ scheint Werte zu verwenden, um Lat Long und nicht Pixel anzupassen. Unterschiedliche Zoomstufen haben also unterschiedliche Auswirkungen darauf, wie weit die Karte angepasst wird. Ich bin mir sicher, dass es dafür einen Anwendungsfall gibt, aber es scheint, als ob die Anpassung für das Problem, das alle ansprechen, eine Seitenleiste mit festgelegter Größe oder ein oberes Menü, nicht von der Zoomstufe abhängen sollte. Gibt es eine integrierte Merkblattfunktion, die eine Auffüllung basierend auf Pixeln anstelle von Koordinaten hinzufügt?

topLeftPadding und bottomRightPadding erwarten Werte in Pixel: http://leafletjs.com/reference.html#map -fitboundsoptions

Irgendwie scheint das bei mir nicht der Fall zu sein. Um dies zu testen, zentriere ich einen Marker auf eine Zoomstufe von 15 und führe dann diesen Befehl aus ...
map.fitBounds(pointsArray, {paddingTopLeft: [0,0], paddingBottomRight: [800,0], maxZoom: map.getZoom()});
Dann zentriere ich den Marker auf eine Zoomstufe von 19 und führe ihn erneut aus. Auf Stufe 15 ist es fast unbemerkt, um wie viel es sich verschiebt. Auf Stufe 19 verschiebt es sich jedoch über etwa die Hälfte der Seite. Mache ich etwas falsch in meinem Funktionsaufruf?

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen