Zajrzałem do tego.
W szczególności Zajrzałem veapicore.js, wersję 7.0.2012.91, dostępnego na
http://ecn.dev.virtualearth.net/mapcontrol/v7.0/js/bin/7.0.2012.91/en-us/veapicore.js
Moduł ten jest pobierany, gdy obejmuje kontrolę mapy Bing, na przykład:
<script charset="UTF-8" type="text/javascript"
src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0">
</script>
Znalazłem to, co moim zdaniem to dwa różne problemy.
• Funkcja MapMath.locationRectToMercatorZoom (funkcja wewnętrzna, nieprzeznaczona do bezpośredniego użycia przez aplikacje) zawsze zwraca zoom o wartości 1, gdy pole ograniczające LocationRect obejmuje 180. południk. To jest niepoprawne. Ta funkcja jest używana przez funkcję Map.setView() do automatycznego ustawiania zoomu, co powoduje zjawisko wygaszenia zoomu.
• Funkcja LocationRect.fromLocations() używa naiwnego podejścia do określania obwiedni dla zestawu lokalizacji. W rzeczywistości nie ma gwarancji, że będzie to "minimalny prostokąt ograniczający" lub "minimalny prostokąt ograniczający". Skrzynka zwrócona z tej metody nigdy nie rozciąga się na 180-ym południku, o ile wiem. Na przykład funkcja LocationRect zwrócona dla zestawu lokalizacji reprezentujących granice wysp Nowej Zelandii rozpocznie się od -176 szerokości geograficznej i rozciągnie się na wschód, aż do + 165. południka. To po prostu źle.
Naprawiłem te problemy, kodując małpy kod w 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);
};
}
Funkcje te muszą być nazywane raz przed użyciem, ale po załadowaniu. Pierwszy wydaje się być opóźniony, więc nie możesz zrobić łatania małpy na gotowym dokumencie; musisz poczekać, aż utworzysz Microsoft.Maps.Map
.
Pierwszy z nich robi to, co właściwe, biorąc pod uwagę parametr LocationRect. Oryginalna metoda odwraca wschodnie i zachodnie krawędzie w przypadkach, gdy prostokąt obejmuje 180. południk.
Druga funkcja służy do ustalenia metody fromLocations
. Oryginalna implementacja iteruje we wszystkich lokalizacjach i przyjmuje minimalną długość geograficzną jako "lewą" i maksymalną długość geograficzną jako "prawo". Dzieje się tak, gdy minimalna długość geograficzna znajduje się na wschód od 180. południka (powiedzmy -178), a maksymalna długość geograficzna jest na zachód od tej samej linii (powiedzmy +165). Wynikowa ramka ograniczająca powinna obejmować 180-y południk, ale w rzeczywistości wartość obliczona przy użyciu tego naiwnego podejścia jest bardzo duża.
Poprawiona implementacja oblicza to pole, a także oblicza drugą ramkę ograniczającą. W przypadku drugiego, zamiast używać wartości długości, używa ona wartości długości lub długości geograficznej + 360, gdy długość geograficzna jest ujemna. Wynikowa transformacja zmienia długość geograficzną z wartości z zakresu od -180 do 180 na wartość z zakresu od 0 do 360. Następnie funkcja oblicza maksymalny i minimalny nowy zestaw wartości.
Wynikiem są dwie ramki ograniczające: jedna o długościach od -180 do +180, a druga o długościach od 0 do 360. Te pola będą miały różne szerokości.
Ustalona implementacja wybiera pole o węższej szerokości, dzięki czemu można się domyślić, że mniejsze pole jest poprawną odpowiedzią. Ta heurystyka złamie się, jeśli próbujesz obliczyć obwiednię dla zbioru punktów, który jest większy niż połowa ziemi.
Przykładem użycia może wyglądać następująco:
monkeyPatchFromLocations();
bounds = Microsoft.Maps.LocationRect.fromLocations(allPoints);
monkeyPatchMapMath();
map1.setView({bounds:bounds});
Ta strona pokazuje: http://jsbin.com/emobav/4
Podwójne kliknięcie na mapie nie powoduje zoom waaay się efekt, jak odnotowano w http://jsbin.com/emobav/2
* Czy to nie działa, gdy obejmujących 180? Nie rozumiem, dlaczego nie byłoby ... * Chris, to nie działa, ponieważ jest błąd w bibliotece map Microsoftu. Przeczytaj, co napisałem w mojej odpowiedzi. Łatwo jest wykazać błąd, przekazując dowolny zestaw punktów obejmujący 180. Algorytm, którego używają do obliczenia obwiedni lub centrum, jest naiwny. Przeczytaj moją odpowiedź, wyjaśniłem to wszystko. – Cheeso
@Cheeso * Druga funkcja naprawia metodę fromLocations. * Przykro mi, nie zauważyłem, że to zLocations było również błędne podczas czytania za pierwszym razem. Źle, bo to spowodowałoby, że getLocations() byłby świetnym kandydatem! Brawo na twoim znalezieniu i napraw Cheeso, dobrze! – clamchoda