Подтвердить что ты не робот

Приложение Google Maps API 3 fitBounds - убедитесь, что маркеры не скрыты с помощью наложенных элементов управления

Я хотел бы, чтобы добавить дополнение к виду карты после вызова map.fitBounds(), поэтому все маркеры могут быть видны независимо от элементов управления картой или вещей, таких как скользящие панели, которые будут закрывать маркеры при открытии. У Leaftlet есть возможность добавить дополнение к fitBounds, но Google Maps этого не делает.

Иногда северные маркеры частично скрываются над окном просмотра. Самые западные маркеры также часто лежат под ползунком масштабирования. С API 2 можно было создать виртуальный видовой экран, уменьшив данные прокладки из showBounds() просмотра карты, а затем вызовите метод showBounds() для вычисления и выполнения масштабирования и центрирования на основе этого виртуального видового экрана:

map.showBounds(bounds, {top:30,right:10,left:50});

Рабочий пример этого для API 2 можно найти здесь по ссылке showBounds().

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

ОБНОВИТЬ

(Codepen, если код ниже не работает)

function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    draggable: true,
    streetViewControl: false,
    zoomControl: false
  });

  var marker1 = new google.maps.Marker({
    position: {lat: 37, lng: -121},
    map: map,
  });

  var marker2 = new google.maps.Marker({
    position: {lat: 39.3, lng: -122},
    map: map,
  });
 
  var bounds = new google.maps.LatLngBounds();
  bounds.extend(marker1.position);
  bounds.extend(marker2.position);
  map.fitBounds(bounds);
}
#map {
  height: 640px;
  width: 360px;
}
#overlays {
  position: absolute;
  height: 50px;
  width: 340px;
  background: white;
  margin: -80px 10px;
  text-align: center;
  line-height: 50px;
}
/* Optional: Makes the sample page fill the window. */
html, body {
  height: 100%;
  margin: 0;
  padding: 0;
}
<html>
  <head>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
    <meta charset="utf-8">
    <title>Simple markers</title>

  </head>
  <body>
    <div id="map"></div>
    <div id="overlays">Controls / Order pizza / ETA / etc.</div>
  
    <script async defer
    src="https://maps.googleapis.com/maps/api/js?&callback=initMap">
    </script>
  </body>
</html>
4b9b3361

Ответ 1

Это какое-то хак-иш-решение, но после fitBounds вы можете увеличить один уровень, так что вы получите достаточное количество отступов для своих маркеров.

Предположим, что переменная map - это ваша ссылка на объект карты;

map.setZoom(map.getZoom() - 1);

Ответ 2

Я решил эту проблему, расширив границы карты, включив в нее latlng, которые достаточно продвинули маркеры.

Во-первых, вам нужно создать представление оверлея

var overlayHelper = new google.maps.OverlayView();
overlayHelper.onAdd = function() {};
overlayHelper.onRemove = function() {};
overlayHelper.draw = function() {};
overlayHelper.setMap(map);

Как только у вас есть помощник наложения, вам нужно получить проекцию карты и выполнить вычисления на основе этого.

Обратите внимание, что элемент управления, который у меня есть на моей карте, представляет собой развертку шириной 420 пикселей, 100% высоты в крайнем правом углу карты. Очевидно, вам нужно будет изменить код для размещения ваших элементов управления.

var mapCanvas = $("#map_canvas"),
    controlLeft = mapCanvas.width() - 420, // canvas width minus width of the overlayed control
    projection = overlayHelper.getProjection(),
    widestPoint = 0, 
    latlng, 
    point;

// the markers were created elsewhere and already extended the bounds on creation
map.fitBounds(mapBounds);

// check if any markers are hidden behind the overlayed control
for (var m in markers) {
    point = projection.fromLatLngToContainerPixel(markers[m].getPosition());
    if (point.x > controlLeft && point.x > widestPoint) {
        widestPoint = point.x;
    }
}

if (widestPoint > 0) {
    point = new google.maps.Point(
                mapCanvas.width() + (widestPoint - controlLeft), 
                mapCanvas.height() / 2); // middle of map height, since we only want to reposition bounds to the left and not up and down

    latlng = projection.fromContainerPixelToLatLng(point);
    mapBounds.extend(latlng);
    map.fitBounds(mapBounds);
}

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

google.maps.event.addListenerOnce(map, 'idle', function() { <above code> });

Ответ 3

По состоянию на июнь 2017 года API JavaScript Maps поддерживает параметр дополнения в методе fitBounds().

fitBounds(bounds:LatLngBounds|LatLngBoundsLiteral, padding?:number)

Подробнее см. в документации

https://developers.google.com/maps/documentation/javascript/reference#Map

Ответ 4

обновленный

API Карт Google теперь поддерживает собственный параметр "padding" в методе fitBounds (из версии 3.32, исправьте меня, если раньше).

У меня не было возможности проверить его, но если вы сможете обновить - я бы рекомендовал использовать родной путь. Если вы используете версию <3.32 и не можете обновить - мое решение для вас.


Я принял рабочее решение от erzzo и немного улучшил его.
пример

fitBoundsWithPadding(googleMapInstance, PolygonLatLngBounds, {left:250, bottom:10});

Описание аргументов:

  1. gMap - экземпляр карты Google
  2. bounds - google maps Объект LatLngBounds для соответствия
  3. paddingXY - Object Literal: 2 возможных формата:
    • {x, y} - для горизонтальных и вертикальных paddings (x = left = right, y = top = bottom)
    • {слева, справа, сверху, снизу}

список функций для копирования

function fitBoundsWithPadding(gMap, bounds, paddingXY) {
        var projection = gMap.getProjection();
        if (projection) {
            if (!$.isPlainObject(paddingXY))
                paddingXY = {x: 0, y: 0};

            var paddings = {
                top: 0,
                right: 0,
                bottom: 0,
                left: 0
            };

            if (paddingXY.left){
                paddings.left = paddingXY.left;
            } else if (paddingXY.x) {
                paddings.left = paddingXY.x;
                paddings.right = paddingXY.x;
            }

            if (paddingXY.right){
                paddings.right = paddingXY.right;
            }

            if (paddingXY.top){
                paddings.top = paddingXY.top;
            } else if (paddingXY.y) {
                paddings.top = paddingXY.y;
                paddings.bottom = paddingXY.y;
            }

            if (paddingXY.bottom){
                paddings.bottom = paddingXY.bottom;
            }

            // copying the bounds object, since we will extend it
            bounds = new google.maps.LatLngBounds(bounds.getSouthWest(), bounds.getNorthEast());

            // SW
            var point1 = projection.fromLatLngToPoint(bounds.getSouthWest());


            // we must call fitBounds 2 times - first is necessary to set up a projection with initial (actual) bounds
            // and then calculate new bounds by adding our pixel-sized paddings to the resulting viewport
            gMap.fitBounds(bounds);

            var point2 = new google.maps.Point(
                ( (typeof(paddings.left) == 'number' ? paddings.left : 0) / Math.pow(2, gMap.getZoom()) ) || 0,
                ( (typeof(paddings.bottom) == 'number' ? paddings.bottom : 0) / Math.pow(2, gMap.getZoom()) ) || 0
            );

            var newPoint = projection.fromPointToLatLng(new google.maps.Point(
                point1.x - point2.x,
                point1.y + point2.y
            ));

            bounds.extend(newPoint);

            // NE
            point1 = projection.fromLatLngToPoint(bounds.getNorthEast());
            point2 = new google.maps.Point(
                ( (typeof(paddings.right) == 'number' ? paddings.right : 0) / Math.pow(2, gMap.getZoom()) ) || 0,
                ( (typeof(paddings.top) == 'number' ? paddings.top : 0) / Math.pow(2, gMap.getZoom()) ) || 0
            );
            newPoint = projection.fromPointToLatLng(new google.maps.Point(
                point1.x + point2.x,
                point1.y - point2.y
            ));

            bounds.extend(newPoint);

            gMap.fitBounds(bounds);
        }
    }

Ответ 5

Я предоставлю более общее решение для этой проблемы. Если у нас есть позиция, например. marker.getPosition(), мы можем найти другую позицию (x, y) от него, используя эту функцию.

function extendedLocation(position, x, y) {
    var projection = map.getProjection();
    var newMarkerX = projection.fromLatLngToPoint(position).x + x/(Math.pow(2, map.getZoom()))
    var newMarkerY = projection.fromLatLngToPoint(position).y + y/(Math.pow(2, map.getZoom()))
    var newMarkerPoint = new google.maps.Point(newMarkerX, newMarkerY);
    return projection.fromPointToLatLng(newMarkerPoint)
}

Примечание: положительное x находится в правильном направлении, а положительное y - в нисходящем направлении. Итак, в общем случае, чтобы показать маркер, нам нужно передать отрицательное значение y, например. extendedLocation(marker.getPosition(), 4, -18)

Если у вас есть постоянный слайдер или любой такой элемент в верхней части, предположим, высота 30 px, просто используйте параметр y в функции как -30.

Может быть создана более обобщенная функция, которая возвращает массив из 4 точек, каждый (x, y) пикселей от заданного направления вверх, вниз, вправо и влево.

function getAllExtendedPosition(position, x, y){
   var positionArray = [];
   postitionArray.push(extendedLocation(position, x, y);
   postitionArray.push(extendedLocation(position, -x, -y);
   postitionArray.push(extendedLocation(position, -x, y);
   postitionArray.push(extendedLocation(position, x, -y);
   return positionArray
}

Ответ 6

Вы можете использовать map.fitBounds() с API V3 с тем же синтаксисом дополнения, который вы упомянули с помощью map.showBounds().

Просто используйте map.fitBounds(bounds, {top:30,right:10,left:50}); работал на меня.

(это может быть комментарий к xomena или Roman86 'post, у меня недостаточно репутации для комментариев)

Ответ 7

Другой способ сделать это - расширить границы с помощью дополнительной точки LatLong на расчетном расстоянии. Объект google.maps.LatLngBounds() имеет функции для получения точек SouthWest и NorthEast ограничивающего прямоугольника, которые могут быть использованы для вычисления расстояния X миль на север, юг, восток или запад.

Например, если вы пытались подтолкнуть маркеры вправо для учета элементов наложения в левой части карты, вы можете попробовать следующее:

// create your LatLngBounds object, like you're already probably doing
var bounds = new google.maps.LatLngBounds();

// for each marker call bounds.extend(pos) to extend the base boundaries

// once your loop is complete
// *** add a calculated LatLng point to the west of your boundary ***
bounds.extend(new google.maps.LatLng(bounds.getSouthWest().lat(), bounds.getSouthWest().lng() - .9));

// finally, center your map on the modified boundaries
map.fitBounds(bounds);

В приведенном выше примере добавление точки LatLong к границам путем вычитания .9 из долготы самой западной точки перемещает границу примерно в 52 милях дальше на запад.

Целая точка (pos.lng() - 1.0) составляет около 58 миль, поэтому вы можете либо угадать хорошее расстояние, либо использовать какой-либо другой метод для вычисления этого продольного смещения при определении того, какой тип прокладки вам нужен.