Leaflet: Разрешить смещение по центру карты.

Созданный на 31 июл. 2012  ·  31Комментарии  ·  Источник: Leaflet/Leaflet

Привет. Было бы здорово иметь возможность смещать центр карты относительно области просмотра. Мой основной вариант использования заключается в том, что у меня есть большая коробка, накладывающаяся на карту слева, и я хотел бы, чтобы центр карты был правее. Это означает, что когда я выполняю панорамирование или центрирование, на самом деле это будет смещение на определенное количество пикселей или процент области просмотра. Это будет настройка уровня карты.

Я хотел эту функцию во многих библиотеках сопоставления JS, но еще не нашел ее. К сожалению, он должен быть довольно низкого уровня в API.

Извините, у меня нет кода, просто подумал и хотел его записать.

Спасибо за классный проект!

Самый полезный комментарий

Вот как я в настоящее время реализовал panTo со смещением в нашем проекте по адресу https://github.com/codeforamerica/lv-trucks-map (действующий сайт по адресу http://clvfoodtrucks.com/). Он изменяет объект-прототип 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);

Это решает ваш вариант использования?

Привет. Так оно и есть, и, вероятно, достаточно хорошо. Но проблема здесь в том, что он предполагает, что окно просмотра находится там, где вы хотите центрироваться, а затем смещаете его, поэтому это будет двухшаговое движение. Вместо того, чтобы принимать широту-долготу, смещать, а затем панорамировать; единое движение.

О, мои расчеты неверны. Вам необходимо установить фактический центр карты таким образом, чтобы центр смещения находился в целевой точке, а смещение было равно overlayWidth / 2 . Итак, вот рабочий код для одного движения:

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

map.setView(targetLatLng, targetZoom);

Это тот же случай, что и у нас — слегка непрозрачная боковая панель, наложенная поверх карты. Всякий раз, когда мы вызывали fitBounds(), это было бы немного неправильно, и нам нужно было бы сделать еще одно движение, чтобы сделать это правильно. Мы рассматривали запрос на вытягивание для этого, но в итоге просто переместили боковую панель рядом с картой, так как посчитали, что это может быть немного работы, и не думали, что это достаточно распространенный вариант использования.

@mourner Это имеет большой смысл. Думаю, я пропустил методы проекта(). Это обрабатывает большую часть моего варианта использования. Большое спасибо.

Как и @ajbeaven , смещение fitBounds для слоя (GeoJSON) было бы очень круто, но кажется, что это будет намного сложнее. Я не думаю, что простое смещение достаточно хорошо, так как границы области просмотра эффективно меняются, и я не знаю, как это сделать за один вызов.

В любом случае, не стесняйтесь закрыть это, если нет смысла добавлять саму библиотеку. Я думаю, что могу управлять своим конкретным проектом с помощью предоставленного вами кода. Спасибо.

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

Привет. Я использую fitBounds с LatLng[] в качестве параметра и пытался использовать фрагменты кода, которые вы предоставили выше, но безуспешно.
В моем веб-сайте есть наложение боковой панели, под которым я хочу видеть карту, но я не хочу, чтобы маркеры отображались под этим наложением. (Было бы здорово также сместить центр карты, но это можно сделать вручную.)

Не могли бы вы опубликовать полный фрагмент кода для fitBounds на _offset map_?
Меня очень смущает то, что вы написали выше. Проблема в том, что у меня нет ни targetLatLng , ни targetPoint , так как я использую массив точек.

Кстати. было бы действительно здорово иметь возможность определять количество полуперекрытого пространства с каждой стороны контейнера, где вы все равно рисуете карту, но не размещаете там какие-либо элементы управления маркерами.

Вы можете увидеть код, который я реализовал здесь. (это ни в коем случае не отличный пример кода, но идея есть). Он также встроен в объект, который содержит свойство карты, которое является картой Leaflet. Он имеет следующие методы:

приложениеSetView
приложениеSetBounds
приложениеGetBoundsZoom

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

Повторное открытие, так как было бы полезно добавить эту встроенную функцию.

Наткнулся на это обсуждение, так как мне нужно было иметь список в левой части экрана размером 550 пикселей с картой за ним. Я решил, что, вероятно, будет проще переопределить некоторые базовые функции 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

Или передайте только второй аргумент, и нижний правый отступ будет по умолчанию равен верхнему левому.

Подпись немного изменилась. Теперь вы используете его так:

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

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

Планируется ли работа по добавлению отступов к другим функциям?

Возможно, да. По крайней мере, для setView и panTo .

Должен быть ярлык для применения одного и того же отступа со всех сторон. Что-то вроде этого.

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

Вместо нынешнего...

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

@averrips Этот Mixin был очень полезен, спасибо.

Я еще не смог найти решение, так что вот (очень просто):

 функция переустановки (карта, широта, смещениеx, смещение) {
 var center = map.project(latlng);
 center = new L.point(center.x+offsetx,center.y+offsety);
 var target = map.unproject(center);
 карта.panTo(цель);
 }

Пожалуйста, добавьте такую ​​же функциональность для "setView" и "panTo".
Спасибо!

Кроме того, есть ли способ дополнить левый | правый элементы управления?

Вот как я в настоящее время реализовал panTo со смещением в нашем проекте по адресу https://github.com/codeforamerica/lv-trucks-map (действующий сайт по адресу http://clvfoodtrucks.com/). Он изменяет объект-прототип 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 рейтинги