我对此进行了调查。
特别是我查看了 veapicore.js ,版本 7.0.20120123200232.91 ,可在
http://ecn.dev.virtualearth.net/mapcontrol/v7.0/js/bin/7.0.20120123200232.91/en-us/veapicore.js
当你包含必应地图控件时,会下载此模块,如下所示:
<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 度开始向东延伸,一直到 +165 度子午线。这是错误的。
我通过猴子修补 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
双击地图不会导致缩小效果,如http://jsbin.com/emobav/2