Leaflet: Autoriser le décalage au centre de la carte.

Créé le 31 juil. 2012  ·  31Commentaires  ·  Source: Leaflet/Leaflet

Salut. Ce serait super génial de pouvoir décaler le centre de la carte par rapport à la fenêtre. Mon cas d'utilisation principal est que j'ai une grande boîte recouvrant la carte à gauche et que j'aimerais que le centre de la carte soit plus à droite. Cela signifie que lorsque je fais un panoramique ou un centre, il serait en fait décalé d'un certain nombre de pixels ou d'un pourcentage de la fenêtre d'affichage. Ce serait un paramètre au niveau de la carte.

J'ai voulu cette fonctionnalité dans de nombreuses bibliothèques de mappage JS mais je ne l'ai pas encore trouvée. Malheureusement, il doit être de niveau assez bas dans l'API.

Désolé, je n'ai pas de code, j'y pensais juste et je voulais le faire tomber.

Merci pour le projet génial!

feature

Commentaire le plus utile

Voici comment j'ai actuellement implémenté un panTo avec décalage dans notre projet sur https://github.com/codeforamerica/lv-trucks-map (site en direct sur http://clvfoodtrucks.com/). Il modifie l'objet prototype L.map afin que cette méthode soit "nativement" disponible sur les objets cartographiques, et elle doit être ajoutée à votre code JavaScript avant de créer une carte :

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

Cela fonctionne de la même manière que panTo() mais avec un paramètre supplémentaire, offset , qui est un tableau sous la forme de [xOffset, yOffset] . Vous pouvez modifier cela pour créer également une méthode setViewOffset().

Tous les 31 commentaires

C'est en fait assez facile dans Leaflet. Ce dont vous avez besoin est la méthode map.containerPointToLatLng . Il convertit certaines coordonnées de pixels par rapport au coin supérieur gauche de la carte en emplacement géographique. Donc tu fais quelque chose comme ça :

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

map.panTo(targetLatLng);

Cela résout-il votre cas d'utilisation ?

Hé. C'est en quelque sorte le cas, et c'est probablement assez bon. Mais, le problème ici est qu'il suppose que la fenêtre est là où vous voulez centrer, puis la décaler, donc ce serait un mouvement en deux étapes. Au lieu de prendre un lat-lng, un décalage, puis un panoramique ; un seul mouvement.

Oh, mes calculs ne sont pas bons. Vous devez définir le centre réel de la carte de manière à ce que le centre de décalage se trouve dans le point cible et que le décalage soit égal overlayWidth / 2 . Voici donc le code de travail pour un seul mouvement :

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

map.setView(targetLatLng, targetZoom);

C'est le même cas que nous avions aussi - une barre latérale légèrement opaque superposée au-dessus de la carte. Chaque fois que nous appelions fitBounds (), ce serait un peu décalé et nous aurions besoin de faire un autre mouvement pour bien faire les choses. Nous envisagions une demande d'extraction pour cela, mais nous avons simplement déplacé la barre latérale pour qu'elle soit à côté de la carte car nous avons pensé que cela pourrait être un peu de travail et que nous ne pensions pas que c'était un cas d'utilisation assez courant.

@mourner Cela a beaucoup de sens. Je suppose que j'ai raté les méthodes project(). Cela gère la plupart de mon cas d'utilisation. Merci beaucoup.

Comme @ajbeaven , un fitBounds décalé pour une couche (GeoJSON) serait super génial, mais cela semble être beaucoup plus compliqué. Je ne pense pas que le simple décalage soit suffisant, car les limites de la fenêtre d'affichage changent effectivement, et je ne saurais pas non plus comment le faire en un seul appel.

Quoi qu'il en soit, n'hésitez pas à fermer ceci s'il n'est pas logique d'ajouter la bibliothèque elle-même. Je pense que je peux gérer mon projet spécifique avec le code que vous avez fourni. Merci.

fitBounds est un peu plus verbeux mais facile aussi. Copiez-collez simplement cette méthode https://github.com/CloudMade/Leaflet/blob/master/src/map/Map.js#L275 , en modifiant la taille qu'elle utilise, puis utilisez

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

Salut. J'utilise fitBounds avec LatLng[] comme paramètre et j'ai essayé d'utiliser les extraits de code que vous avez fournis ci-dessus, mais sans succès.
Mon site Web a une superposition de barre latérale où je veux que la carte soit visible en dessous, mais j'aimerais empêcher les marqueurs d'apparaître sous cette superposition. (Ce serait bien de décaler également le centre de la carte, mais cela peut être fait manuellement.)

Pourriez-vous, s'il vous plaît, publier un extrait de code complet pour fitBounds sur _offset map_ ?
Je suis assez confus par ce que vous avez écrit ci-dessus. Le problème est que je n'ai ni targetLatLng ni targetPoint car j'utilise un tableau de points.

D'ailleurs. ce serait vraiment génial d'avoir une option pour définir la quantité d'espace semi-superposé de chaque côté du conteneur, où vous dessineriez toujours la carte, mais ne placeriez aucun contrôle sur les marqueurs là-bas.

Vous pouvez voir le code que j'ai implémenté ici. (ce n'est en aucun cas une excellente instance de code, mais l'idée est là) Il est également intégré dans un objet qui contient une propriété de carte qui est la carte Leaflet. Il a les méthodes suivantes :

appSetView
appSetBounds
appGetBoundsZoom

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

Réouverture, car il serait utile d'ajouter cette fonctionnalité intégrée.

Je suis tombé sur cette discussion car je devais avoir une liste sur le côté gauche de l'écran de 550 pixels avec la carte derrière. J'ai décidé qu'il serait probablement plus facile de remplacer certaines fonctions de base de L.Map. Merci pour toutes vos idées.

J'espère que je les ai tous couverts, mais je ne suis pas sûr à 100%.

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 Merci d'avoir posté ça. Votre solution fonctionne bien pour moi actuellement... sauf que les commandes de zoom sont cassées dans Firefox maintenant :( Je reçois le genre d'erreur "catchall": TypeError: t is undefined

Voici une implémentation de fitBoundsPadded, qui devrait vivre dans la carte :
Utilisez-le comme fitBounds, mais passez le rembourrage que vous voulez, alors utilisez-le comme :

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

Et le 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--;
            }
        }
    },

Implémenté en master ! Utilisez-le comme ceci :

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

Ou passez juste le deuxième argument et le rembourrage en bas à droite sera par défaut égal en haut à gauche.

La signature a un peu changé. Maintenant, vous l'utilisez comme ceci :

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

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

Y a-t-il des travaux prévus pour ajouter du rembourrage à d'autres fonctions ?

Peut-être, ouais. Pour setView et panTo au moins.

Il devrait y avoir un raccourci pour appliquer le même rembourrage de tous les côtés. Quelque chose comme ça.

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

Au lieu du courant...

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

@averrips Ce Mixin a été très utile, merci.

Je n'ai pas encore trouvé la solution, alors voilà (vraiment facile):

 fonction recentrer(map,latlng,offsetx,offsety) {
 var center = map.project(latlng);
 center = new L.point(center.x+offsetx,center.y+offsety);
 var cible = map.unproject(center);
 map.panTo(cible);
 }

Veuillez ajouter la même fonctionnalité également pour "setView" et "panTo".
Merci!

Existe-t-il également un moyen de remplir également les contrôles gauche | droite?

Voici comment j'ai actuellement implémenté un panTo avec décalage dans notre projet sur https://github.com/codeforamerica/lv-trucks-map (site en direct sur http://clvfoodtrucks.com/). Il modifie l'objet prototype L.map afin que cette méthode soit "nativement" disponible sur les objets cartographiques, et elle doit être ajoutée à votre code JavaScript avant de créer une carte :

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

Cela fonctionne de la même manière que panTo() mais avec un paramètre supplémentaire, offset , qui est un tableau sous la forme de [xOffset, yOffset] . Vous pouvez modifier cela pour créer également une méthode setViewOffset().

Merci, louh !

Pour référence, cette configuration fonctionne pour moi pour émuler le décalage horizontal comme on le voit sur la page d'accueil de foursquare.
https://gist.github.com/missinglink/7620340

Merci @louh!

Merci @missinglink , votre code fonctionne !

Existe-t-il également une solution pour les actions de zoom avant/arrière ?

J'utilise quelque chose comme ça :

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

mais il échoue sur le zoom.

Voici la solution : https://github.com/Mappy/Leaflet-active-area. Brillant! :)

La fonction 'map.fitBounds(bounds, topLeftPadding, bottomRightPadding)' semble utiliser des valeurs pour ajuster la longueur, pas les pixels. Ainsi, différents niveaux de zoom auront des résultats différents sur le degré d'ajustement de la carte. Je suis sûr qu'il y a un cas d'utilisation pour cela, mais il semble que pour le problème que tout le monde soulève, une barre latérale de taille définie ou un menu supérieur, que l'ajustement ne devrait pas dépendre du niveau de zoom. Existe-t-il une fonction de dépliant intégrée qui ajoute un rembourrage basé sur des pixels plutôt que sur des coordonnées ?

topLeftPadding et bottomRightPadding attendent des valeurs en pixels : http://leafletjs.com/reference.html#map -fitboundsoptions

Pour une raison quelconque, cela ne semble pas être le cas pour moi. Pour tester cela, je centre un marqueur à un niveau de zoom de 15, puis j'exécute cette commande...
map.fitBounds(pointsArray, {paddingTopLeft: [0,0], paddingBottomRight: [800,0], maxZoom: map.getZoom()});
Ensuite, je recentre le marqueur à un niveau de zoom de 19 et je le relance. Au niveau 15, la quantité de décalage est presque imperceptible. Cependant, au niveau 19, il se déplace sur environ la moitié de la page. Est-ce que je fais quelque chose de mal dans mon appel de fonction ?

Cette page vous a été utile?
0 / 5 - 0 notes