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

jsu*_*urf 27 google-maps padding geo google-maps-api-3 google-maps-api-2

我希望能够在调用a后将填充添加到地图视图中map.fitBounds(),因此无论地图控件还是打开时覆盖标记的滑动面板之类的东西都可以看到所有标记.Leaftlet可以选择为fitBounds添加填充,但Google Maps没有.

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

map.showBounds(bounds, {top:30,right:10,left:50});
Run Code Online (Sandbox Code Playgroud)

本作API 2所述的工作实施例中,可以发现这里的showBounds()的例子链接下.

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

UPDATE

(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);
}
Run Code Online (Sandbox Code Playgroud)
#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;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

问题是这样的:

在此输入图像描述

我试着增加一个控制作为记录自定义控件,但地图是不完全意识到这一点-看到这个小提琴从分叉地图自定义控件例子.其中一个标记仍然被对照模糊.

keu*_*une 23

这是某种黑客十岁上下的解决方案,但之后fitBounds,你可以放大一个级别了,所以你得到足够的填充您的标记.

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

map.setZoom(map.getZoom() - 1);
Run Code Online (Sandbox Code Playgroud)

  • 便宜,快捷,肮脏.我喜欢! (4认同)
  • 因为fitBounds()是异步的,要使其工作,您可能需要为代码添加一个监听器,如http://stackoverflow.com/a/11976581/286486中所述 (3认同)

xom*_*ena 13

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

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

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

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

  • "请参阅文档以获取更多详细信息" - 这样会很好,但没有详细信息.只提到了一个名为`padding`的参数,它带有一个数字.没有关于其语义或默认值的详细信息.虽然Google Maps API指南很好,但缺少参考文档,此案例也不例外. (4认同)
  • https://developers.google.com/maps/documentation/javascript/reference/coordinates#Padding 您可以添加一个数字或 {left:50, top:200} // right, bottom 等。 (3认同)
  • 很高兴看到,但对于许多用例来说仍然是一个不充分的解决方案,因为通常只有某些区域需要填充,并且(记录不足)功能似乎向所有方面添加了填充。 (2认同)

Sea*_*kin 10

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

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

var overlayHelper = new google.maps.OverlayView();
overlayHelper.onAdd = function() {};
overlayHelper.onRemove = function() {};
overlayHelper.draw = function() {};
overlayHelper.setMap(map);
Run Code Online (Sandbox Code Playgroud)

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

请注意,我在地图上的控件是地图最右侧的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);
}
Run Code Online (Sandbox Code Playgroud)

如果您在第一次加载地图时执行此操作,则需要将其包装在map事件中以等待空闲.这允许覆盖视图初始化.不要在事件回调中包含叠加帮助程序创建.

google.maps.event.addListenerOnce(map, 'idle', function() { <above code> });
Run Code Online (Sandbox Code Playgroud)


Rom*_*n86 9

更新

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

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


我采用了erzzo的工作解决方案并对其进行了一些改进.

fitBoundsWithPadding(googleMapInstance, PolygonLatLngBounds, {left:250, bottom:10});
Run Code Online (Sandbox Code Playgroud)

参数说明:

  1. gMap - 谷歌地图实例
  2. bounds - 谷歌地图LatLngBounds对象适合
  3. paddingXY - Object Literal:2种可能的格式:
    • {x,y} - 用于水平和垂直填充(x =左=右,y =顶=底)
    • {left,right,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);
        }
    }
Run Code Online (Sandbox Code Playgroud)


小智 5

您可以将map.fitBounds()API V3与提到的填充语法一起使用map.showBounds()

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

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