He investigado esto.
En particular me miraba en veapicore.js, versión 7.0.2012.91, disponible en
http://ecn.dev.virtualearth.net/mapcontrol/v7.0/js/bin/7.0.2012.91/en-us/veapicore.js
Este módulo se descarga cuando se incluye el control de los mapas de Bing, como esto:
<script charset="UTF-8" type="text/javascript"
src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0">
</script>
Encontré lo que creo que son dos problemas distintos.
• La función MapMath.locationRectToMercatorZoom (una función interna, no destinada para uso directo por las aplicaciones) siempre devuelve un zoom de 1 cuando el cuadro delimitador del LocationRect se extiende por el meridiano 180º. Esto es incorrecto. Esta función es utilizada por la función Map.setView() para establecer automáticamente el zoom, lo que da como resultado el fenómeno de zoom waaay out.
• LocationRect.fromLocations() utiliza un enfoque ingenuo para determinar el cuadro delimitador para un conjunto de ubicaciones. De hecho, no se garantiza que sea un "cuadro delimitador mínimo" o un "rectángulo delimitador mínimo". La caja devuelta por ese método nunca se extiende por el meridiano 180, por lo que puedo ver. Por ejemplo, el LocationRect devuelto para un conjunto de ubicaciones que representan las fronteras de las islas de Nueva Zelanda, se iniciará en la latitud -176º y se extenderá hacia el este, hasta el meridiano + 165º. Esto es simplemente incorrecto
Resolví estos problemas mediante el parche del código en 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);
};
}
Estas funciones necesitan ser llamados una vez antes de su uso, pero después de la carga. El primero tiene carga de retraso, creo, así que no puedes hacer el parche de mono en el documento listo; debe esperar hasta después de haber creado un Microsoft.Maps.Map
.
El primero simplemente hace lo correcto dado un LocationRect. El método original voltea los bordes este y oeste en los casos en que el rectángulo se extiende por el meridiano 180º.
La segunda función corrige el método fromLocations
. La implementación original recorre todas las ubicaciones y toma la longitud mínima para ser la "izquierda" y la longitud máxima para ser la "derecha". Esto falla cuando la longitud mínima está justo al este del meridiano 180 (por ejemplo, -178), y el valor de longitud máxima está justo al oeste de la misma línea (digamos, +165). El cuadro delimitador resultante debe abarcar el meridiano 180 pero, de hecho, el valor calculado con este enfoque ingenuo sigue el camino más largo.
La implementación corregida calcula ese cuadro y también calcula un segundo cuadro delimitador. Para el segundo, en lugar de usar el valor de longitud, utiliza el valor de longitud o la longitud + 360, cuando la longitud es negativa. La transformación resultante cambia la longitud de un valor que oscila entre -180 y 180, en un valor que va de 0 a 360. Y luego la función calcula el máximo y mínimo de ese nuevo conjunto de valores.
El resultado son dos cuadros delimitadores: uno con longitudes que van de -180 a +180, y otro con longitudes que van de 0 a 360. Estos cuadros serán de diferentes anchuras.
La implementación fija elige la caja con el ancho más estrecho, adivinando que la caja más pequeña es la respuesta correcta. Esta heurística se romperá si está intentando calcular el cuadro delimitador para un conjunto de puntos que es más grande que la mitad de la Tierra.
Un ejemplo de uso podría tener este aspecto:
monkeyPatchFromLocations();
bounds = Microsoft.Maps.LocationRect.fromLocations(allPoints);
monkeyPatchMapMath();
map1.setView({bounds:bounds});
Esta página demuestra: http://jsbin.com/emobav/4
Haciendo doble clic sobre el mapa nunca hace que el zoom MUUCHO cabo efecto, como se vio en http://jsbin.com/emobav/2
* ¿Este no funciona cuando atraviesa el 180? No veo por qué no ... * Chris, no funciona, porque hay un error en la biblioteca de mapas de Microsoft. Lea lo que escribí en mi respuesta. Es fácil demostrar el error pasando cualquier conjunto de puntos que abarquen los 180. El algoritmo que utilizan para calcular el cuadro delimitador o el centro es ingenuo. Lea mi respuesta, expliqué todo esto. – Cheeso
@ Cheeso * La segunda función corrige el método fromLocations. * Lo siento, no entendí que fromLocations también tenía errores al leer la primera vez. ¡Malo porque eso hubiera convertido a getLocations() en un gran candidato! ¡Bravo en tu búsqueda y arregla a Cheeso, buena persona! – clamchoda