Google Maps API 3 fitBounds填充 - 确保标记不会被重叠控件遮挡

时间:2012-04-26 18:34:52

标签: google-maps google-maps-api-3 padding google-maps-api-2 geo

我希望在调用map.fitBounds()后能够将填充添加到地图视图中,因此无论地图控件或打开时会覆盖标记的滑动面板之类的东西都可以看到所有标记。 Leaftlet有option to add padding to fitBounds,但谷歌地图没有。{/ p>

有时,最北端的标记部分隐藏在视口上方。最西边的标记通常位于缩放滑块下方。使用API​​ 2,可以通过减少地图视口中的给定填充来形成虚拟视口,然后调用方法showBounds()来计算并执行基于该虚拟视口的缩放和居中:

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

可以在showBounds()示例链接下找到API 2的工作示例{。{3}}。

我在API V3中找不到类似的功能,但希望还有另一种方法可以实现。也许我可以抓住东北和西南点,然后添加假坐标以在包含它们之后进一步扩展边界?

更新

here,以防下面的代码不起作用)

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>

问题在于:

Codepen

我已尝试添加enter image description here中记录的控件,但地图并未完全了解它 - 请参阅Custom controls this fiddle。其中一个标记仍被控件遮挡。

8 个答案:

答案 0 :(得分:23)

这是某种黑客攻击解决方案,但在fitBounds之后,您可以缩小一个级别,这样就可以为标记获得足够的填充。

假设map变量是您对地图对象的引用;

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

答案 1 :(得分:12)

截至2017年6月,Maps JavaScript API支持fitBounds()方法中的padding参数。

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

有关详细信息,请参阅文档

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

答案 2 :(得分:10)

我通过扩展地图边界来解决这个问题,包括一个足以将标记推入视图的latlng。

首先,您需要创建叠加视图

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

一旦你有了一个叠加助手,你需要获得地图投影并根据它进行计算。

请注意,我在地图上的控件是地图最右侧的420像素宽,100%高度div。您显然需要更改代码以适应您的控件。

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 :(得分:8)

更新

Google Maps API现在支持fitBounds方法中的原生“填充”参数(从版本3.32开始,如果更早,请更正我)。

我没有机会测试它,但如果你能升级 - 我会建议使用原生方式。如果您使用的是版本&lt; 3.32并且无法升级 - 我的解决方案适合您。

我按working solution erzzo进行了改进并稍微改进了一下。
示例

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

参数说明:

  1. gMap - 谷歌地图实例
  2. bounds - 谷歌地图LatLngBounds对象适合
  3. paddingXY - Object Literal:2种可能的格式:
    • {x,y} - 用于水平和垂直填充(x =左=右,y =顶=底)
    • {left,right,top,bottom}
  4. 要复制的功能列表

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

答案 4 :(得分:4)

您可以将map.fitBounds()用于API V3,并使用与map.showBounds()中提到的相同的填充语法。

仅使用map.fitBounds(bounds, {top:30,right:10,left:50});对我有用。

(这可能是对xomena或Roman86'帖子的评论,我没有足够的声誉进行评论)

答案 5 :(得分:1)

我将为此问题提供更通用的解决方案。 如果我们有一个职位,例如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 :(得分:0)

我做的方式看起来很干净。例如。在地图的每一侧使用像素坐标基础应用 10% 的填充。这是 Maps API v3.44:

map.fitBounds(bounds)
let mapDiv = map.getDiv()

let padding = {
  bottom: mapDiv.offsetHeight * 0.1,
  left: mapDiv.offsetWidth * 0.1,
  right: mapDiv.offsetWidth * 0.1,
  top: mapDiv.offsetHeight * 0.1,
}
map.fitBounds(bounds, padding);

答案 7 :(得分:-1)

另一种方法是使用计算距离的额外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);

在上面的示例中,通过从最西点的经度减去.9将LatLong点添加到边界,将边界向西移动大约52英里。

整点(pos.lng() - 1.0)大约是58英里,所以你可以猜出一个好的距离,或者在确定你需要什么样的填充时使用其他方法来计算纵向偏移。