我正在使用v7 Bing Maps Javascript“控件”(我不知道为什么它被称为“控件”......)。我正在打电话给Microsoft.Maps.Map.setView({bounds: bounds})
,但它并没有像我期望的那样工作。
我有一组多边形,其点数跨越第180个子午线。一个例子是新西兰岛屿的边界 - 其中一些位于第180个子午线以西,一些部分(查塔姆岛)位于东部。
当我使用这些边界创建一个多边形并调用setView()
时,地图会缩小 waaaaaay 。
为什么呢?以及如何避免它?
This page提供了问题的演示。
这是代码。
var map, MM = Microsoft.Maps;
function showMap(m) {
var options = {
mapTypeId: MM.MapTypeId.road // aerial,
// center will be recalculated
// zoom will be recalculated
},
map1 = new MM.Map(m, options);
return map1;
}
function doubleclickCallback(e) {
e.handled = true;
var bounds = map.getBounds();
map.setView({ bounds: bounds });
}
function init() {
var mapDiv = document.getElementById("map1");
map = showMap(mapDiv);
MM.Events.addHandler(map, "dblclick", doubleclickCallback);
}
如果双击地图中没有第180个子午线的地图,则不会发生任何事情。如果在地图显示第180个子午线时双击,则地图将重置为缩放级别1。
答案 0 :(得分:11)
特别是我查看了veapicore.js,版本7.0.20120123200232.91,可在
获取http://ecn.dev.virtualearth.net/mapcontrol/v7.0/js/bin/7.0.20120123200232.91/en-us/veapicore.js
当您包含bing maps控件时,将下载此模块,如下所示:
<script charset="UTF-8" type="text/javascript"
src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0">
</script>
我发现我认为是两个截然不同的问题。
•当LocationRect的边界框跨越第180个子午线时,MapMath.locationRectToMercatorZoom函数(内部函数,不适合应用程序直接使用)始终返回1的缩放。这是不正确的。 Map.setView()函数使用此函数自动设置缩放,这会导致缩放等待现象。
•LocationRect.fromLocations()使用简单的方法来确定一组位置的边界框。实际上,它不能保证是“最小边界框”或“最小边界矩形”。据我所知,从该方法返回的框永远不会跨越第180个子午线。例如,为一组代表新西兰岛屿边界的位置返回的LocationRect将从-176纬度开始向东伸展,一直到+ 165th子午线。这是错的。
我通过在veapicore.js中修补代码修复了这些问题。
function monkeyPatchMapMath() {
Microsoft.Maps.InternalNamespaceForDelay.MapMath.
locationRectToMercatorZoom = function (windowDimensions, bounds) {
var ins = Microsoft.Maps.InternalNamespaceForDelay,
d = windowDimensions,
g = Microsoft.Maps.Globals,
n = bounds.getNorth(),
s = bounds.getSouth(),
e = bounds.getEast(),
w = bounds.getWest(),
f = ((e+360 - w) % 360)/360,
//f = Math.abs(w - e) / 360,
u = Math.abs(ins.MercatorCube.latitudeToY(n) -
ins.MercatorCube.latitudeToY(s)),
r = Math.min(d.width / (g.zoomOriginWidth * f),
d.height / (g.zoomOriginWidth * u));
return ins.VectorMath.log2(r);
};
}
function monkeyPatchFromLocations() {
Microsoft.Maps.LocationRect.fromLocations = function () {
var com = Microsoft.Maps.InternalNamespaceForDelay.Common,
o = com.isArray(arguments[0]) ? arguments[0] : arguments,
latMax, latMin, lngMin1, lngMin2, lngMax1, lngMax2, c,
lngMin, lngMax, LL, dx1, dx2,
pt = Microsoft.Maps.AltitudeReference,
s, e, n, f = o.length;
while (f--)
n = o[f],
isFinite(n.latitude) && isFinite(n.longitude) &&
(latMax = latMax === c ? n.latitude : Math.max(latMax, n.latitude),
latMin = latMin === c ? n.latitude : Math.min(latMin, n.latitude),
lngMax1 = lngMax1 === c ? n.longitude : Math.max(lngMax1, n.longitude),
lngMin1 = lngMin1 === c ? n.longitude : Math.min(lngMin1, n.longitude),
LL = n.longitude,
(LL < 0) && (LL += 360),
lngMax2 = lngMax2 === c ? LL : Math.max(lngMax2, LL),
lngMin2 = lngMin2 === c ? LL : Math.min(lngMin2, LL),
isFinite(n.altitude) && pt.isValid(n.altitudeReference) &&
(e = n.altitude, s = n.altitudeReference));
dx1 = lngMax1 - lngMin1,
dx2 = lngMax2 - lngMin2,
lngMax = (dx1 > dx2) ? lngMax2 : lngMax1,
lngMin = (dx1 > dx2) ? lngMin2 : lngMin1;
return Microsoft.Maps.LocationRect.fromEdges(latMax, lngMin, latMin, lngMax, e, s);
};
}
这些功能在使用前需要被称为一次,但是在加载之后。我认为第一个是延迟加载的,所以你不能在文件准备好的情况下进行猴子修补;您需要等到创建Microsoft.Maps.Map
之后。
第一个在给定LocationRect的情况下做了正确的事情。在矩形跨越第180个子午线的情况下,原始方法翻转东西边缘。
第二个函数修复了fromLocations
方法。原始实现遍历所有位置,并将最小经度设为“左”,最大经度为“右”。当最小经度位于第180个子午线的东边(例如,-178)时,这会失败,并且最大经度值恰好位于同一直线的西边(例如,+ 165)。得到的边界框应该跨越第180个子午线,但实际上使用这种天真的方法计算出来的值很长。
更正的实现计算该框,并计算第二个边界框。对于第二个,当经度为负时,它使用经度值或经度+ 360,而不是使用经度值。生成的变换将经度从-180到180的值更改为范围从0到360的值。然后该函数计算该新值集的最大值和最小值。
结果是两个边界框:一个边界的范围从-180到+180,另一个边界的范围从0到360.这些边框的宽度不同。
固定实现选择宽度较窄的框,猜测较小的框是正确的答案。如果你试图计算一组大于地球一半的点的边界框,这种启发式方法就会破裂。
示例用法可能如下所示:
monkeyPatchFromLocations();
bounds = Microsoft.Maps.LocationRect.fromLocations(allPoints);
monkeyPatchMapMath();
map1.setView({bounds:bounds});
此页面演示了:http://jsbin.com/emobav/4
双击地图永远不会导致 zoom waaay out 效果,如http://jsbin.com/emobav/2中所示
答案 1 :(得分:2)
也许是一种更简单的方法。
bounds = Microsoft.Maps.LocationRect.fromLocations(allPoints);
LocationRect.fromLocations accepts a list of locations / array
你在澳大利亚缠绕的多边形包含一个函数来返回一个名为getLocations()的位置数组。
似乎可以像这样调用它(双重检查语法);
var viewBoundaries = Microsoft.Maps.LocationRect.fromLocations(polygon.getLocations());
map.setView({ bounds: viewBoundaries });
map.setView({ zoom: 10 });
跨越180时这不起作用吗?我不明白为什么它不会,因为它只是使用多边形中的点。如果是这样,以下对您来说可能是一个非常简单的解决方案。
你说每当你双击地图时,就会平移地图。这很有意义,因为您只向地图添加了一个事件处理程序,并在以下代码中将边界设置为地图的边界:
function doubleclickCallback(e) {
e.handled = true;
var bounds = map.getBounds();
map.setView({ bounds: bounds });
}
MM.Events.addHandler(map, "dblclick", doubleclickCallback);
我认为你需要将你的点击处理程序添加到多边形,以便将视图跨越到指定的多边形。
Microsoft.Maps.Events.addHandler(polygon, 'click', doubleclickCallback);
然后在你的doubleclickCallback中:
function doubleclickCallback(e) {
// Now we are getting the boundaries of our polygon
var bounds = e.target.getLocations();
map.setView({ bounds: bounds });
map.setView({ zoom: 9});
}
答案 2 :(得分:1)
至少在v7.0 / 7.0.20130619132259.11中,这似乎在veapicore.js中修复了