Leaflet: Memungkinkan untuk offset di tengah peta.

Dibuat pada 31 Jul 2012  ·  31Komentar  ·  Sumber: Leaflet/Leaflet

Hai. Akan sangat luar biasa untuk dapat mengimbangi bagian tengah peta relatif terhadap viewport. Kasus penggunaan utama saya adalah saya memiliki kotak besar yang melapisi peta di sebelah kiri, dan ingin bagian tengah peta lebih ke kanan. Ini berarti bahwa ketika saya melakukan pan atau center, itu sebenarnya akan mengimbangi sejumlah piksel atau persentase viewport tertentu. Ini akan menjadi pengaturan tingkat peta.

Saya menginginkan fitur ini di banyak perpustakaan pemetaan JS tetapi belum menemukannya. Sayangnya, levelnya harus cukup rendah di API.

Maaf, saya tidak punya kode apa pun, hanya memikirkan dan ingin menghapusnya.

Terima kasih untuk proyek yang luar biasa!

feature

Komentar yang paling membantu

Inilah cara saya saat ini menerapkan panTo dengan offset dalam proyek kami di https://github.com/codeforamerica/lv-trucks-map (situs langsung di http://clvfoodtrucks.com/). Ini memodifikasi objek prototipe L.map sehingga metode ini "asli" tersedia pada objek peta, dan itu harus ditambahkan ke kode JavaScript Anda sebelum membuat peta:

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

Ini bekerja sama dengan panTo() tetapi dengan parameter tambahan, offset , yang merupakan array dalam bentuk [xOffset, yOffset] . Anda dapat memodifikasi ini untuk membuat metode setViewOffset() juga.

Semua 31 komentar

Ini sebenarnya cukup mudah di Leaflet. Yang Anda butuhkan adalah metode map.containerPointToLatLng . Ini mengubah beberapa koordinat piksel relatif terhadap sudut kiri atas peta menjadi lokasi geografis. Jadi Anda melakukan sesuatu seperti ini:

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

map.panTo(targetLatLng);

Apakah ini menyelesaikan kasus penggunaan Anda?

Hai. Ini semacam tidak, dan mungkin cukup baik. Tapi, masalahnya di sini adalah bahwa ia mengasumsikan bahwa viewport adalah tempat Anda ingin memusatkan, lalu mengimbanginya, jadi itu akan menjadi gerakan dua langkah. Berbeda dengan mengambil lat-lng, offset, lalu panning; satu gerakan.

Oh, perhitungan saya tidak benar. Anda perlu mengatur pusat peta yang sebenarnya sedemikian rupa sehingga pusat offset berada di titik target, dan offset akan sama dengan overlayWidth / 2 . Jadi, inilah kode kerja untuk satu gerakan:

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

map.setView(targetLatLng, targetZoom);

Ini adalah kasus yang sama yang kami alami - bilah sisi yang sedikit buram dihamparkan di atas peta. Setiap kali kami memanggil fitBounds(), itu akan menjadi sedikit salah dan kami perlu melakukan gerakan lain untuk memperbaikinya. Kami sedang mempertimbangkan permintaan tarik untuk ini tetapi akhirnya hanya memindahkan bilah sisi agar berada di sebelah peta karena kami pikir itu mungkin sedikit berhasil dan tidak berpikir itu adalah kasus penggunaan yang cukup umum.

@mourner Itu sangat masuk akal. Saya kira saya melewatkan metode project(). Ini menangani sebagian besar kasus penggunaan saya. Terimakasih banyak.

Seperti @ajbeaven , offset fitBounds untuk lapisan (GeoJSON) akan sangat luar biasa, tetapi ini sepertinya akan jauh lebih rumit. Saya tidak berpikir hanya mengimbangi saja sudah cukup baik, karena batas viewport berubah secara efektif, saya juga tidak tahu bagaimana melakukannya dalam satu panggilan.

Either way, jangan ragu untuk menutup ini jika tidak masuk akal untuk menambahkan perpustakaan itu sendiri. Saya pikir saya dapat mengelola proyek khusus saya dengan kode yang Anda berikan. Terima kasih.

fitBounds sedikit lebih bertele-tele tetapi juga mudah. Cukup salin-tempel metode https://github.com/CloudMade/Leaflet/blob/master/src/map/Map.js#L275 ini, ubah ukuran apa yang digunakannya, lalu gunakan

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

Hai. Saya menggunakan fitBounds dengan LatLng[] sebagai parameter dan saya mencoba menggunakan cuplikan kode yang Anda berikan di atas, tetapi tidak berhasil.
Web saya memiliki hamparan bilah sisi tempat saya ingin peta terlihat di bawahnya, tetapi saya ingin mencegah penanda muncul di bawah hamparan ini. (Akan sangat bagus untuk mengimbangi pusat peta juga, tetapi itu dapat dilakukan secara manual.)

Bisakah Anda memposting cuplikan kode lengkap untuk fitBounds di _offset map_?
Saya cukup bingung dengan apa yang Anda tulis di atas. Masalahnya adalah saya tidak punya targetLatLng atau targetPoint karena saya menggunakan array poin.

Omong-omong. akan sangat luar biasa memiliki opsi untuk menentukan jumlah ruang semi-overlay di setiap sisi wadah, tempat Anda masih menggambar peta, tetapi tidak akan menempatkan kontrol apa pun pada penanda di sana.

Anda dapat melihat kode yang saya terapkan di sini. (Ini sama sekali bukan contoh kode yang bagus, tetapi idenya ada di sana) Itu juga dibangun ke dalam objek yang menyimpan properti peta yaitu peta Leaflet. Ini memiliki metode berikut:

AppSetView
aplikasiSetBounds
aplikasiGetBoundsZoom

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

Dibuka kembali, karena akan berguna untuk menambahkan fungsi bawaan ini.

Menemukan diskusi ini karena saya harus memiliki daftar di sisi kiri layar 550px dengan peta di belakangnya. Saya memutuskan mungkin akan lebih mudah untuk mengganti beberapa fungsi dasar L.Map. Terima kasih untuk semua ide Anda.

Semoga saya sudah mendapatkan semuanya, tapi saya tidak 100% yakin.

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 Terima kasih telah memposting itu. Solusi Anda bekerja dengan baik untuk saya saat ini ... kecuali bahwa kontrol zoom rusak di Firefox sekarang :( Saya mendapatkan semacam kesalahan "catchall": TypeError: t is undefined

Berikut ini adalah implementasi fitBoundsPadded, yang seharusnya hidup di peta:
Gunakan seperti fitBounds, tetapi berikan padding yang Anda inginkan, jadi gunakan seperti:

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

Dan kodenya:

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

Diimplementasikan di master! Gunakan seperti ini:

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

Atau berikan argumen kedua saja dan padding kanan bawah akan disetel ke kiri atas yang sama.

Tanda tangan berubah sedikit. Sekarang Anda menggunakannya seperti ini:

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

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

Apakah ada pekerjaan yang direncanakan untuk menambahkan padding ke fungsi lain?

Mungkin, ya. Untuk setView dan panTo setidaknya.

Harus ada jalan pintas untuk menerapkan padding yang sama di semua sisi. Sesuatu seperti ini.

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

Alih-alih saat ini...

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

@averrips Mixin itu sangat membantu, terima kasih.

Saya belum dapat menemukan solusinya, jadi ini dia (sangat mudah):

 fungsi terbaru(peta,latlng,offsetx,offsety) {
 var center = map.project(latlng);
 center = new L.point(center.x+offsetx,center.y+offsety);
 var target = map.unproject(center);
 peta.panKe(target);
 }

Harap tambahkan fungsi yang sama juga untuk "setView" dan "panTo".
Terima kasih!

Juga, apakah ada cara untuk padding juga kontrol kiri|kanan?

Inilah cara saya saat ini menerapkan panTo dengan offset dalam proyek kami di https://github.com/codeforamerica/lv-trucks-map (situs langsung di http://clvfoodtrucks.com/). Ini memodifikasi objek prototipe L.map sehingga metode ini "asli" tersedia pada objek peta, dan itu harus ditambahkan ke kode JavaScript Anda sebelum membuat peta:

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

Ini bekerja sama dengan panTo() tetapi dengan parameter tambahan, offset , yang merupakan array dalam bentuk [xOffset, yOffset] . Anda dapat memodifikasi ini untuk membuat metode setViewOffset() juga.

Terima kasih, loh!

Sebagai referensi, konfigurasi ini berfungsi bagi saya untuk meniru offset horizontal seperti yang terlihat di beranda foursquare.
https://Gist.github.com/missinglink/7620340

Terima kasih @louh!

Terima kasih @missinglink , kode Anda berfungsi!

Apakah ada solusi juga untuk tindakan zoom in/out?

Saya menggunakan sesuatu seperti ini:

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

tapi gagal di zoom.

Inilah solusinya: https://github.com/Mappy/Leaflet-active-area. Cemerlang! :)

Fungsi 'map.fitBounds(bounds, topLeftPadding, bottomRightPadding)' tampaknya menggunakan nilai untuk menyesuaikan panjang lat, bukan piksel. Jadi tingkat zoom yang berbeda akan memiliki hasil yang berbeda pada seberapa jauh peta disesuaikan. Saya yakin ada kasus penggunaan untuk ini, tetapi sepertinya untuk masalah yang diangkat semua orang, bilah samping ukuran yang ditetapkan atau menu atas, bahwa penyesuaian tidak boleh bergantung pada tingkat zoom. Apakah ada fungsi selebaran bawaan yang menambahkan bantalan berdasarkan piksel daripada koordinat?

topLeftPadding dan bottomRightPadding mengharapkan nilai dalam piksel: http://leafletjs.com/reference.html#map -fitboundsoptions

Untuk beberapa alasan sepertinya itu tidak berlaku untukku. Untuk mengujinya, saya memusatkan penanda pada tingkat zoom 15 dan kemudian menjalankan perintah ini...
map.fitBounds(pointsArray, {paddingTopLeft: [0,0], paddingBottomRight: [800,0], maxZoom: map.getZoom()});
Kemudian saya memasang kembali penanda pada tingkat zoom 19 dan menjalankannya lagi. Pada level 15 hampir tidak terlihat jumlah pergeserannya. Namun, pada level 19, itu bergeser sekitar setengah halaman. Apakah saya melakukan sesuatu yang salah dalam panggilan fungsi saya?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat