Ho esaminato questo.
In particolare ho guardato in veapicore.js, versione 7.0.2012.91, disponibile presso
http://ecn.dev.virtualearth.net/mapcontrol/v7.0/js/bin/7.0.2012.91/en-us/veapicore.js
Questo modulo viene scaricato quando si include il controllo Bing Maps, in questo modo:
<script charset="UTF-8" type="text/javascript"
src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0">
</script>
Ho trovato quello che penso siano due problemi distinti.
• La funzione MapMath.locationRectToMercatorZoom (una funzione interna, non destinata all'uso diretto da parte delle applicazioni) restituisce sempre uno zoom di 1 quando il riquadro di delimitazione di LocationRect si estende sul 180 ° meridiano. Questo non è corretto Questa funzione è utilizzata dalla funzione Map.setView() per impostare automaticamente lo zoom, che si traduce nel fenomeno di zoom waaay out.
• LocationRect.fromLocations() utilizza un approccio ingenuo per determinare il riquadro di delimitazione per un insieme di posizioni. In effetti non è garantito che sia un "riquadro di limitazione minimo" o un "rettangolo di limitazione minimo". La scatola restituita da quel metodo non copre mai il 180 ° meridiano, per quanto posso dire. Ad esempio, LocationRect restituito per un insieme di località che rappresentano i confini delle isole della Nuova Zelanda, inizierà alla latitudine di -166 e si estenderà verso est fino al meridiano + 165 °. Questo è semplicemente sbagliato.
Ho risolto questi problemi eseguendo il patch-patch del codice in 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);
};
}
Queste funzioni devono essere chiamati volta prima dell'uso, ma dopo il caricamento. Il primo viene caricato con il ritardo, penso, quindi non è possibile eseguire la patch delle scimmie sul documento pronto; è necessario attendere fino a dopo aver creato uno Microsoft.Maps.Map
.
Il primo fa la cosa giusta, dato un LocationRect. Il metodo originale inverte i bordi est e ovest nei casi in cui il rettangolo si estende sul 180 ° meridiano.
La seconda funzione corregge il metodo fromLocations
. L'implementazione originale itera su tutte le posizioni e prende la longitudine minima per essere la "sinistra" e la longitudine massima per essere la "giusta". Questo fallisce quando la longitudine minima è solo ad est del 180 ° meridiano (diciamo, -178), e il valore di longitudine massima è solo ad ovest della stessa linea (per esempio, +165). Il riquadro di delimitazione risultante dovrebbe estendersi sul 180 ° meridiano, ma in realtà il valore calcolato utilizzando questo approccio ingenuo va ben oltre.
L'implementazione corretta calcola tale casella e calcola anche un secondo riquadro di delimitazione. Per la seconda, anziché utilizzare il valore di longitudine, utilizza il valore di longitudine o la longitudine + 360, quando la longitudine è negativa. La trasformazione risultante modifica la longitudine da un valore compreso tra -180 e 180, in un valore compreso tra 0 e 360. Quindi la funzione calcola il massimo e il minimo di quel nuovo insieme di valori.
Il risultato sono due riquadri di delimitazione: uno con lunghezze che vanno da -180 a +180 e un altro con lunghezze che vanno da 0 a 360. Queste caselle saranno di larghezze diverse.
L'implementazione fissa sceglie la casella con la larghezza più stretta, facendo un'ipotesi che la casella più piccola sia la risposta corretta. Questa euristica si interromperà se stai cercando di calcolare il riquadro di delimitazione per un insieme di punti che è più grande della metà della terra.
Un esempio d'uso potrebbe assomigliare a questo:
monkeyPatchFromLocations();
bounds = Microsoft.Maps.LocationRect.fromLocations(allPoints);
monkeyPatchMapMath();
map1.setView({bounds:bounds});
Questa pagina dimostra: http://jsbin.com/emobav/4
Facendo doppio click sulla mappa non fa sì che il zoom mooolto fuori effetto, come si è visto in http://jsbin.com/emobav/2
* Questo non funziona quando si attraversa il 180? Non vedo perché non dovrebbe ... * Chris, non funziona, perché c'è un bug nella libreria delle mappe di Microsoft. Leggi cosa ho scritto nella mia risposta. È facile dimostrare il bug passando un qualsiasi insieme di punti che si estendono su 180. L'algoritmo che usano per calcolare il riquadro di delimitazione o il centro è ingenuo. Leggi la mia risposta, ho spiegato tutto questo. – Cheeso
@Cheeso * La seconda funzione corregge il metodo fromLocations. * Spiacente, non ho capito che quello diLocations era anche buggato durante la lettura della prima volta. A male perché questo avrebbe reso getLocations() un ottimo candidato! Bravo alla tua ricerca e fissa Cheeso, bravo! – clamchoda