Ich habe mir das angesehen.
Insbesondere habe ich in veapicore.js , Version 7.0.20120123200232.91 , nachgeschaut, verfügbar unter
http://ecn.dev.virtualearth.net/mapcontrol/v7.0/js/bin/7.0.20120123200232.91/en-us/veapicore.js
Dieses Modul wird heruntergeladen, wenn Sie das Bing-Maps-Steuerelement einbinden, etwa so:
<script charset="UTF-8" type="text/javascript"
src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0">
</script>
Ich bin auf zwei unterschiedliche Probleme gestoßen.
- Die Funktion MapMath.locationRectToMercatorZoom (eine interne Funktion, die nicht für die direkte Verwendung durch Anwendungen gedacht ist) gibt immer einen Zoom von 1 zurück, wenn die Bounding Box des LocationRects den 180sten Meridian überspannt. Dies ist nicht korrekt. Diese Funktion wird von der Funktion Map.setView() verwendet, um den Zoom automatisch einzustellen, was dazu führt, dass der Zoom sehr weit außen liegt.
- Die Funktion LocationRect.fromLocations() verwendet einen naiven Ansatz zur Bestimmung des Begrenzungsrahmens für eine Gruppe von Orten. Es ist nicht garantiert, dass es sich um eine "Minimum Bounding Box" oder ein "Minimum Bounding Rectangle" handelt. Die von dieser Methode zurückgegebene Box umspannt nie den 180. Meridian, soweit ich das beurteilen kann. Das LocationRect, das für eine Reihe von Orten zurückgegeben wird, die die Grenzen der neuseeländischen Inseln darstellen, beginnt zum Beispiel am -176. Breitengrad und erstreckt sich nach Osten bis zum +165. Das ist einfach falsch.
Ich habe diese Probleme durch monkey-Parcheando des Codes in veapicore.js behoben.
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);
};
}
Diese Funktionen müssen aufgerufen werden einmal vor dem Gebrauch, sondern nach dem Laden. Das erste wird mit Verzögerung geladen, also kann man das monkey-Parcheando nicht ausführen, wenn das Dokument bereit ist; man muss warten, bis man ein Microsoft.Maps.Map
.
Der erste macht einfach das Richtige, wenn er ein LocationRect hat. Die ursprüngliche Methode spiegelt die Ost- und Westkanten in den Fällen, in denen sich das Rechteck über den 180.
Die zweite Funktion fixiert die fromLocations
Methode. Die ursprüngliche Implementierung durchläuft alle Orte und nimmt den minimalen Längengrad als "links" und den maximalen Längengrad als "rechts". Dies schlägt fehl, wenn der minimale Längengrad gerade östlich des 180. Längengrades (z. B. -178) und der maximale Längengrad gerade westlich derselben Linie (z. B. +165) liegt. Der resultierende Begrenzungsrahmen sollte den 180. Meridian umspannen, aber in Wirklichkeit geht der mit diesem naiven Ansatz berechnete Wert in die andere Richtung.
Die korrigierte Implementierung berechnet diese Box und außerdem eine zweite Bounding Box. Für die zweite Boundingbox wird nicht der Längengradwert verwendet, sondern der Längengradwert oder der Längengrad + 360, wenn der Längengrad negativ ist. Die resultierende Transformation ändert den Längengrad von einem Wert zwischen -180 und 180 in einen Wert zwischen 0 und 360. Anschließend berechnet die Funktion das Maximum und Minimum dieser neuen Wertegruppe.
Das Ergebnis sind zwei Bounding Boxes: eine mit Längengraden im Bereich von -180 bis +180 und eine weitere mit Längengraden im Bereich von 0 bis 360. Diese Boxen haben unterschiedliche Breiten.
Die feste Implementierung wählt das Kästchen mit der geringeren Breite und vermutet, dass das kleinere Kästchen die richtige Antwort ist. Diese Heuristik versagt, wenn Sie versuchen, den Begrenzungsrahmen für eine Gruppe von Punkten zu berechnen, die größer als die halbe Erde ist.
Ein Beispiel für die Verwendung könnte folgendermaßen aussehen:
monkeyPatchFromLocations();
bounds = Microsoft.Maps.LocationRect.fromLocations(allPoints);
monkeyPatchMapMath();
map1.setView({bounds:bounds});
Diese Seite demonstriert: http://jsbin.com/emobav/4
Ein Doppelklick auf die Karte führt nie dazu, dass die weit herauszoomen Wirkung, wie sie in http://jsbin.com/emobav/2