谷歌地图API V3的fitbounds()函数会缩小地图视图,但不会放大。

10

我创建了一个相当复杂的门店定位器。用户输入他们的邮政编码,然后一个表返回带有地图上相应标记的结果。他们可以翻页查看结果,标记会越来越远,fitbounds()函数可以很好地缩小到适合标记的适当缩放级别。问题是,如果您回到更近的位置,fitbounds()不会缩小。即使您输入新的搜索,它也不会围绕这些新标记进行缩放 - 它会在它之前保持中心但保持先前的缩放级别。希望这讲得清楚。如果有人知道我需要添加什么来使其缩小到更接近的结果,请帮忙。以下是我在我的标记推送函数结尾处调用的谷歌函数:

  map.setCenter(bounds.getCenter());
  map.panToBounds(bounds);
  map.fitBounds(bounds);

谢谢!

12个回答

11

这里不需要什么花哨的东西。首先适配边界,然后将其移动到视窗中心。这将为您提供合适的缩放并包含整个边界。

map.fitBounds(bounds);
map.panToBounds(bounds);

2
这似乎有效。我想知道如果没有fitBoundspanToBounds应该做什么。 - dbkaplun

11
这是问题的症结所在:我们设置了
var bounds = new google.maps.LatLngBounds();
为了使我们的标记适应地图上的有界区域,GMaps将始终异步缩小以相应地适应fitBounds(),但不会缩小以实现相同的效果(如@broady之前所述)。对于许多应用程序来说,这并不理想,因为一旦您在地图上显示了一系列标记导致地图缩小(可能<10),它将不会自动缩回到原来的大小,除非用户手動操作。
GMaps将继续使用(最合适的词)最缩小的标记集合状态的范围。在每次调用新标记之前将其设置为“null”将为您提供一个全新的地图。
要自动缩放,请简单地将LatLngBounds();设置为“null”,如下面的伪代码示例所示:
bounds = new google.maps.LatLngBounds(null);

伪代码示例:

// map stuff/initiation
...

var bounds = new google.maps.LatLngBounds();
var gmarkers = [];

function CreateMarker (obj) {
    myLatLng = new google.maps.LatLng(obj['latitude'], obj['longitude']);
    marker = new google.maps.Marker({
        position: myLatLng,
        map: map
    });
    google.maps.event.addListener(marker, 'click', (function(marker, i) {
        return function() {
            infowindow.setContent(obj['job']);
            infowindow.open(map, marker);
        }
    })(marker, i));
    bounds.extend(myLatLng);
    gmarkers.push(marker);
}

....

// here's an AJAX method I use to grab marker coords from a database:

$.ajax({
    beforeSend: function() {
        clear_markers(gmarkers); // see below for clear_markers() function declaration
    },
    cache: false,
    data: params,
    dataType: 'json',
    timeout: 0,
    type: 'POST',
    url: '/map/get_markers.php?_=<?php echo md5(session_id() . time() . mt_rand(1,9999)); ?>',
    success: function(data) {
        if (data) {
            if (data['count'] > 0) {
                var obj;
                var results = data['results'];

                // Plot the markers
                for (r in results) {
                    if (r < (data['count'])) {
                        CreateMarker(results[r]);
                    }
                }
            }
        }
    },
    complete: function() {
        map.fitBounds(bounds);
    }
});

// clear_markers()
function clear_markers(a) {
    if (a) {
        for (i in a) {
            a[i].setMap(null);
        }
        a.length = 0;
    }
    bounds = new google.maps.LatLngBounds(null); // this is where the magic happens; setting LatLngBounds to null resets the current bounds and allows the new call for zoom in/out to be made directly against the latest markers to be plotted on the map
}

谢谢John Smith!你的建议是我在删除标记后唯一能够缩放回来的方法。我很感激你帖子中的细节。 - gorelog

7
没错,fitBounds 只确保提供的边界可见。它不会缩放到适当的级别。
你可以先调用 setZoom(20),然后再调用 fitBounds。

1
对于以后阅读此内容的任何人,fitBounds 在当前版本中进行缩放,panToBounds 不会,并且与上述描述相匹配。 - Camro

4

很有趣... 我使用PHP循环遍历所有标记(即将)坐标并计算southWest和northEast的值;原点的坐标位于两者之间。如果所有标记坐标非常接近,由fitBounds设置的缩放因子比地图创建时使用的15要高得多(放大)。这就是为什么我添加了最后一行的原因...

var map;

function mapInit() {
  var origin = new google.maps.LatLng(59.33344615, 18.0678188);
  var options = {
    zoom: 15,
    center: origin,
    mapTypeControlOptions: {
      mapTypeIds: [google.maps.MapTypeId.ROADMAP, google.maps.MapTypeId.HYBRID]
    },
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };

  map = new google.maps.Map(document.getElementById("googlemap"), options);

  var southWest = new google.maps.LatLng(59.3308415, 18.0643054);
  var northEast = new google.maps.LatLng(59.3360508, 18.0713322);
  var bounds = new google.maps.LatLngBounds(southWest, northEast);
  map.fitBounds(bounds);

  google.maps.event.addListenerOnce(map, "idle", function() {
    if (map.getZoom() > 16) map.setZoom(16);
  });

所以,要么Google自从你发问之后重新编写了这个函数,要么..我需要更多关于你的代码的信息。


谢谢,只有这个函数对我起作用了。之前尝试了很多解决方案,但都不行。 根据我的需求稍作修改后如下: google.maps.event.addListenerOnce(map, "idle", function() { if (map.getZoom() < parseInt(getParameterByName("zoomlevel"))) map.setZoom(parseInt(getParameterByName("zoomlevel"))); }); - Darkcoder

4

以下是完整代码示例,使用fitBounds(bounds, padding)的第二个参数设置为0可以帮助我:

function initMap() {
    var mapOptions = {
        center: new google.maps.LatLng(0, 0),
        zoom: 1,
        minZoom: 1
    };
    map = new google.maps.Map(document.getElementById('officeMap'), mapOptions);
    google.maps.event.addListenerOnce(map, 'idle', function() {
        //Map is ready
        worldViewFit(map);
    });
}
function worldViewFit(mapObj) {
    var worldBounds = new google.maps.LatLngBounds(
        new google.maps.LatLng(70.4043,-143.5291),  //Top-left
        new google.maps.LatLng(-46.11251, 163.4288)  //Bottom-right
    );
    mapObj.fitBounds(worldBounds, 0);
    var actualBounds = mapObj.getBounds();
    if(actualBounds.getSouthWest().lng() == -180 && actualBounds.getNorthEast().lng() == 180) {
        mapObj.setZoom(mapObj.getZoom()+1);
    }
}

在经过一天的搜索和调试后,这个答案是解决方案。谢谢Tom。 - Kayote

3

当我用fitBounds在同一张地图上再次添加新的标记时,我也遇到了地图缩小的问题。下面是我使用的解决方法:

// This is a stationary point tho dynamic will work too

var myLat = someLat,
    myLng = someLong;

var map = false,
    markersArray = [];

function makeOrderMap(lat, lng) { // pass in dynamic point when updating map

  var mapOptions = {
      center: new google.maps.LatLng(myLat, myLng),
      zoom: 16,
      mapTypeId: google.maps.MapTypeId.ROADMAP
  };
  deleteOverlays(); // remove any existing markers..
  if(!map) {  
    map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
  } 

  // Find the mid-point to center the map

  midLat = (parseFloat(lat) + parseFloat(myLat)) / 2;
  midLng = (parseFloat(lng) + parseFloat(myLng)) / 2;
  map.setCenter(new google.maps.LatLng(midLat, midLng));
  var newSpot = new google.maps.LatLng(lat, lng); 

  placeMarker(mapOptions.center);    
  placeMarker(newSpot);   

  // determine the distance between points for deciding the zoom level

  var dist = distHaversine(mapOptions.center, newSpot);

  var zoom = 10;
  if(dist < 1.25) {
    zoom = 15;
  } else if(dist < 2.5) {
    zoom = 14;
  } else if(dist < 5) {
    zoom = 13;
  } else if(dist < 10) {
    zoom = 12;
  } else if(dist < 20) {
    zoom = 11;
  }
  map.setZoom(zoom);  
}


rad = function(x) {return x*Math.PI/180;}

distHaversine = function(p1, p2) {
  var R = 6371; // earth's mean radius in km
  var dLat  = rad(p2.lat() - p1.lat());
  var dLong = rad(p2.lng() - p1.lng());

  var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
          Math.cos(rad(p1.lat())) * Math.cos(rad(p2.lat())) * Math.sin(dLong/2) * Math.sin(dLong/2);
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
  var d = R * c;

  return d.toFixed(3);
}

function deleteOverlays() {
    if (markersArray) {
        for (i in markersArray) {
            markersArray[i].setMap(null);
        }
    markersArray = [];    
    markersArray.length = 0;
    }    
}

function placeMarker(location, alt_icon) {

    var marker = new google.maps.Marker({
        position: location, 
        map: map
      });

    // add marker in markers array
    markersArray.push(marker);
}

1
谢谢,我已经从您的Angular服务实现中解决了问题:https://gist.github.com/zmijevik/047c40f4fc0bea27fd7c - Joseph Persico

2

原来,当你调用map.getBounds()时,它会返回一个在边缘周围有一定边距的视口。由于这个新范围比当前范围大,地图将始终缩小。我通过避免使用当前地图的边界并保持单独的LatLngBounds变量来解决了这个问题。每次添加点时,我都会调用:

markersBounds.extend(newMarkersLatLng);
map.fitBounds(markersBounds);
map.panToBounds(markersBounds);

为了移除点(我一直在添加它们),你可以使用第一个点创建一个新的LatLngBounds对象,然后扩展每个剩余的点以获得新的边界:
var markersBounds = new google.maps.LatLngBounds(markers[0].getPosition(),
                                                 markers[0].getPosition());
for(var i=1; i<markers.length; i++){
    markersBounds.extend(markers[i].getPosition());
}

map.fitBounds(markersBounds);
map.panToBounds(markersBounds);

1
今天我遇到了这个普遍问题,想分享一下解决方案。我意识到这与你的“门店定位器”问题有些不同,但在许多方面都适用。在我的情况下,我在地图上有一组标记,并添加了一个,希望确保现在所有标记都在视野内。此代码将检查每个现有标记是否在视野内,并在此过程中构建一个新的边界框,以包含它们(如果需要)。最后,如果发现有任何标记不在视野内,就会重置视野,使它们全部在视野内。
(这使用Dojo的数组模块,只是基本数组迭代的便捷包装器)
var origBounds = map.getBounds(),
    newBounds = new google.maps.LatLngBounds(null),
    anyFailed = false;
array.forEach(markers, function (m) {
    newBounds.extend(m.position);
    if (!origBounds.contains(m.position)) {
        anyFailed = true;
    }
});
if (anyFailed) {
    map.setCenter(newBounds.getCenter());
    map.fitBounds(newBounds);
}

你可以轻松地修改这个代码,只需确保新标记在视图中可见,而不是循环并对新标记进行contains()检查。


1

请查看GMap文档中有关marker.setMap(null);的引用(用于从地图中删除标记):

请注意,上述方法不会删除标记。它将标记从地图上移除。如果您希望删除标记,则应将其从地图上移除,然后将标记本身设置为null。

我的问题是,当我从地图上删除标记时,它们仍存储在map.markers中。因此,在设置bounds时,它还会抓取所有先前的标记:

map.markers.forEach( (marker) => {
  var latlng = new google.maps.LatLng(marker.position.lat(), marker.position.lng())
  bounds.extend(latlng)
})

使用 bounds 控制台日志显示,由于此原因,边界将始终容纳所有早期的边界。

我的解决方案是在一个单独的 markers 数组/对象中跟踪标记,该数组/对象存储每个标记的唯一键(在我的情况下,与地点自动完成输入字段相关联),并在从地图中删除标记时从中移除标记。这样,当生成 bounds 时,我可以检查当前的 map.markers 是否在(最新的)markers 数组中。

map.markers.forEach( (marker) => {
  // check if map marker is in this.markers (to make sure only current)
  for (const [key, value] of Object.entries(markers)) {
    if (marker == value) {
      var latlng = new google.maps.LatLng(marker.position.lat(), marker.position.lng())
      bounds.extend(latlng)
    }
  }
})

现在,在更改标记后,我的地图可以正确缩放。如果有人能提出对上述方法的任何改进,我将非常乐意听取建议!
Rens

0
fitBounds: function(bounds, mapId)
    {
        //bounds: bounds, id: map id
        if (bounds==null) return false;
        maps[mapId].fitBounds(bounds);
    },  

这应该会有所帮助,我使用这种方法并且在地图v2上有一点不同的方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接