Leaflet: マップの中心でのオフセットを考慮してください。

作成日 2012年07月31日  ·  31コメント  ·  ソース: Leaflet/Leaflet

やあ。 ビューポートに対してマップの中心をオフセットできるのは非常に素晴らしいことです。 私の主な使用例は、左側の地図に大きなボックスを重ねて、地図の中央をもっと右側に配置したいというものです。 これは、パンまたはセンターを実行すると、実際には特定のピクセル数またはビューポートのパーセンテージがオフセットされることを意味します。 これはマップレベルの設定になります。

多くのJSマッピングライブラリでこの機能が必要でしたが、まだ見つかりませんでした。 残念ながら、APIではかなり低レベルである必要があります。

申し訳ありませんが、私はコードを持っていません。ただ考えていて、それを取り下げたいと思っていました。

素晴らしいプロジェクトをありがとう!

feature

最も参考になるコメント

これが、 https://github.com/codeforamerica/lv-trucks-map (ライブサイトhttp://clvfoodtrucks.com/)のプロジェクトでオフセット付きのpanToを現在実装している方法です。 L.mapプロトタイプオブジェクトを変更して、このメソッドがマップオブジェクトで「ネイティブに」使用できるようにします。マップを作成する前に、JavaScriptコードに追加する必要があります。

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

これはpanTo()と同じように機能しますが、追加のパラメーターoffsetがあります。これは、 [xOffset, yOffset]の形式の配列です。 これを変更して、setViewOffset()メソッドを作成することもできます。

全てのコメント31件

Leafletでは実際にはかなり簡単です。 必要なのはmap.containerPointToLatLngメソッドです。 マップの左上隅を基準にしたピクセル座標を地理的な場所に変換します。 だからあなたはこのようなことをします:

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

map.panTo(targetLatLng);

これでユースケースは解決しますか?

おい。 それは一種のことであり、おそらく十分です。 ただし、ここでの問題は、ビューポートが中央に配置し、オフセットする場所であると想定しているため、2段階の移動になるということです。 lat-lngを取り込んで、オフセットしてからパンするのとは対照的です。 単一の動き。

ああ、私の計算は正しくありません。 オフセットの中心がターゲットポイントにあり、オフセットがoverlayWidth / 2になるように、実際のマップの中心を設定する必要があります。 したがって、単一の動きの作業コードは次のとおりです。

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

map.setView(targetLatLng, targetZoom);

これも私たちが持っていたのと同じケースです-マップの上に少し不透明なサイドバーがオーバーレイされています。 fitBounds()を呼び出すときはいつでも、それは少しずれており、それを正しくするために別の動きをする必要があります。 これについてプルリクエストを検討していましたが、少し手間がかかる可能性があり、十分に一般的なユースケースではないと考えたため、サイドバーをマップの横に移動することになりました。

@mournerそれは非常に理にかなっています。 project()メソッドを見逃したと思います。 これは私のユースケースのほとんどを処理します。 どうもありがとう。

@ajbeavenのように、(GeoJSON)レイヤーのオフセットfitBoundsは非常に優れていますが、これははるかに複雑になるようです。 ビューポートの境界が効果的に変化しているため、オフセットだけでは十分ではないと思います。また、1回の呼び出しでそれを行う方法もわかりません。

いずれにせよ、ライブラリ自体に追加しても意味がない場合は、これを閉じてください。 提供されたコードで特定のプロジェクトを管理できると思います。 ありがとう。

fitBoundsはもう少し冗長ですが、簡単でもあります。 このhttps://github.com/CloudMade/Leaflet/blob/master/src/map/Map.js#L275メソッドをコピーして貼り付け、使用するサイズを変更してから、

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

やあ。 LatLng[]をパラメーターとして使用してfitBoundsを使用しており、上記で提供したコードスニペットを使用しようとしましたが、うまくいきませんでした。
私のウェブには、地図を下に表示したいサイドバーオーバーレイがありますが、このオーバーレイの下にマーカーが表示されないようにしたいと思います。 (マップの中心をオフセットすることもできますが、手動で行うことができます。)

_offset map_にfitBoundsの完全なコードスニペットを投稿していただけますか?
私はあなたが上に書いたことにかなり混乱しています。 問題は、ポイントの配列を使用するtargetLatLngtargetPoint $もありません。

ところで。 コンテナの両側にセミオーバーレイスペースの量を定義するオプションがあると本当に素晴らしいでしょう。ここでは、マップを描画しますが、そこのマーカーにはコントロールを配置しません。

ここで私が実装したコードを見ることができます。 (これは決して優れたコードのインスタンスではありませんが、アイデアはそこにあります)また、Leafletマップであるmapプロパティを保持するオブジェクトに組み込まれています。 次の方法があります。

appSetView
appSetBounds
appGetBoundsZoom

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

組み込みのこの機能を追加すると便利なため、再度開きます。

画面の左側に550pxのリストがあり、その後ろに地図があるので、この議論に出くわしました。 L.Mapのいくつかの基本関数をオーバーライドする方がおそらく簡単だと思いました。 すべてのアイデアをありがとう。

それらがすべてカバーされていることを願っていますが、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投稿していただきありがとうございます。 あなたの解決策は現在私にとってうまく機能しています... Firefoxでズームコントロールが壊れていることを除いて:(一種の「キャッチオール」エラーが発生しています:TypeError:t is undefined

これがfitBoundsPaddedの実装であり、マップに存在する必要があります。
fitBoundsのように使用しますが、必要なパディングを渡すので、次のように使用します。

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

そしてコード:

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

マスターに実装! 次のように使用します。

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

または、2番目の引数だけを渡すと、右下のパディングはデフォルトで左上に等しくなります。

署名が少し変更されました。 今、あなたはそれをこのように使用します:

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

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

他の機能にパディングを追加する予定の作業はありますか?

おそらく、ええ。 少なくともsetViewpanToの場合。

すべての面に同じパディングを適用するためのショートカットがあるはずです。 このようなもの。

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

現在の代わりに...

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

@averrips Mixinはとても役に立ちました、ありがとう。

私はまだ解決策を見つけることができなかったので、ここに行きます(本当に簡単です):

関数recenter(map、latlng、offsetx、offsety){
 var center = map.project(latlng);
 center = new L.point(center.x + offsetx、center.y + offsety);
 var target = map.unproject(center);
 map.panTo(target);
 }

「setView」と「panTo」にも同じ機能を追加してください。
ありがとう!

また、左右のコントロールもパディングする方法はありますか?

これが、 https://github.com/codeforamerica/lv-trucks-map (ライブサイトhttp://clvfoodtrucks.com/)のプロジェクトでオフセット付きのpanToを現在実装している方法です。 L.mapプロトタイプオブジェクトを変更して、このメソッドがマップオブジェクトで「ネイティブに」使用できるようにします。マップを作成する前に、JavaScriptコードに追加する必要があります。

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

これはpanTo()と同じように機能しますが、追加のパラメーターoffsetがあります。これは、 [xOffset, yOffset]の形式の配列です。 これを変更して、setViewOffset()メソッドを作成することもできます。

ありがとう、ルー!

参考までに、この構成は、foursquareホームページに表示される水平オフセットをエミュレートするために機能します。
https://gist.github.com/missinglink/7620340

ありがとう@louh!

@missinglinkに感謝します、あなたのコードは機能します!

ズームイン/ズームアウトアクションの解決策もありますか?

私は次のようなものを使用しています:

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

しかし、ズームに失敗します。

解決策は次のとおりです:https://github.com/Mappy/Leaflet-active-area。 鮮やかさ! :)

'map.fitBounds(bounds、topLeftPadding、bottomRightPadding)'関数は、ピクセルではなく、緯度の長さを調整するために値を使用しているようです。 したがって、ズームのレベルが異なると、マップが調整される距離について異なる結果が得られます。 これには使用例があると思いますが、設定されたサイズのサイドバーまたはトップメニューなど、誰もが提起している問題については、調整がズームレベルに依存しないようにする必要があるようです。 座標ではなくピクセルに基づいてパディングを追加する組み込みのリーフレット関数はありますか?

topLeftPaddingおよびbottomRightPaddingはピクセル単位の値を期待します: http ://leafletjs.com/reference.html#map -fitboundsoptions

どういうわけか、それは私には当てはまらないようです。 これをテストするには、マーカーをズームレベル15の中央に配置してから、このコマンドを実行します...
map.fitBounds(pointsArray, {paddingTopLeft: [0,0], paddingBottomRight: [800,0], maxZoom: map.getZoom()});
次に、マーカーをズームレベル19で再配置し、再度実行します。 レベル15では、シフト量はほとんど目立たない。 ただし、レベル19では、ページの約半分をシフトします。 関数呼び出しで何か問題がありますか?

このページは役に立ちましたか?
0 / 5 - 0 評価