Come calcolare il livello di zoom ottimale per visualizzare due o più punti su una mappa


12

Vogliamo visualizzare diversi marcatori su una mappa statica e vogliamo calcolare il livello di zoom ottimale come fa Google Maps. Abbiamo già calcolato il rettangolo di delimitazione e il punto centrale della mappa, ma ora abbiamo difficoltà a calcolare il livello di zoom corretto per visualizzare l'intero rettangolo di delimitazione. Qualcuno può indicarci la giusta direzione?


Non è molto sofisticato, ma perché non moltiplicare l'MBR del 120% e ingrandirlo? Nient'altro dipende dalla tua definizione di "ottimale", vero?
ThomM,

+1 all'idea di ThomM. Questo è esattamente ciò che fa ArcGIS.
Ragi Yaser Burhum,

1
Siamo spiacenti, ma cos'è questo MBR?
Rodrigo,

Risposte:


5

Per ottenere il livello di zoom, devi conoscere le dimensioni in pixel della tua mappa. Dovrai anche fare i tuoi calcoli in coordinate sferiche del mercatore.

  1. Converti latitudine, longitudine in mercatore sferico x, y.
  2. Ottieni la distanza tra i due punti nel mercatore sferico.
  3. L'equatore è lungo circa 40 metri e le piastrelle hanno una larghezza di 256 pixel, quindi la lunghezza dei pixel di quella mappa a un determinato livello di zoom è di circa 256 * distanza / 40000000 * 2 ^ zoom . Prova zoom = 0, zoom = 1, zoom = 2 fino a quando la distanza non è troppo lunga per le dimensioni in pixel della mappa.

1
Spero di aver capito da remoto cosa stai effettivamente chiedendo. = \
Michal Migurski

Ho trovato questa domanda mentre cercavo questa risposta, grazie.
BenjaminGolder,

@MichalMigurski puoi spiegare come calcolare la distanza tra 2 punti nel mercatore sferico? specialmente le coordinate x ... sono bloccato grazie.
otmezger,

5

Questo è il codice C # che uso in Maperitive :

    public void ZoomToArea (Bounds2 mapArea, float paddingFactor)
    {
        double ry1 = Math.Log((Math.Sin(GeometryUtils.Deg2Rad(mapArea.MinY)) + 1) 
            / Math.Cos(GeometryUtils.Deg2Rad(mapArea.MinY)));
        double ry2 = Math.Log((Math.Sin(GeometryUtils.Deg2Rad(mapArea.MaxY)) + 1) 
            / Math.Cos(GeometryUtils.Deg2Rad(mapArea.MaxY)));
        double ryc = (ry1 + ry2) / 2;
        double centerY = GeometryUtils.Rad2Deg(Math.Atan(Math.Sinh(ryc)));

        double resolutionHorizontal = mapArea.DeltaX / Viewport.Width;

        double vy0 = Math.Log(Math.Tan(Math.PI*(0.25 + centerY/360)));
        double vy1 = Math.Log(Math.Tan(Math.PI*(0.25 + mapArea.MaxY/360)));
        double viewHeightHalf = Viewport.Height/2.0f;
        double zoomFactorPowered = viewHeightHalf
            / (40.7436654315252*(vy1 - vy0));
        double resolutionVertical = 360.0 / (zoomFactorPowered * 256);

        double resolution = Math.Max(resolutionHorizontal, resolutionVertical) 
            * paddingFactor;
        double zoom = Math.Log(360 / (resolution * 256), 2);
        double lon = mapArea.Center.X;
        double lat = centerY;

        CenterMapOnPoint(new PointD2(lon, lat), zoom);
    }
  • mapArea: riquadro di selezione in coordinate long / lat (x = long, y = lat)
  • paddingFactor: può essere usato per ottenere l'effetto "120%" a cui ThomM fa riferimento. Il valore di 1,2 ti farebbe guadagnare il 120%.

Nota che nel mio caso zoompuò essere un numero reale. Nel caso delle mappe Web, è necessario un valore di zoom intero, quindi è necessario utilizzare qualcosa di simile (int)Math.Floor(zoom)per ottenerlo.

Naturalmente, questo codice si applica solo alla proiezione di Web Mercator.


1
Grazie per questo. Il codice per CenterMapOnPoint è disponibile?
mcintyre321,

Perfetto! Ho usato questo per calcolare lo zoom di BoundingBox in OsmDroid SDK e funziona :)
Billda,

Dove trovo queste librerie? Non riesco a trovare un assemblaggio GeometryUtils o Bounds2. Devo scaricare qualcosa da maperitive? Vedo solo un'app lì.
Dowlers,

@Dowlers GeometryUtilsè appena usato qui per convertire i gradi in radianti e viceversa , è una semplice formula matematica. Bounds2è fondamentalmente solo una struttura rettangolare. Questo codice è stato fornito più come uno pseudocodice che qualcosa direttamente copiabile.
Igor Brejc,

1
Questo è fantastico @ IgorBrejc- grazie mille! L'ho convertito in Python qui nel caso in cui altri stiano scrivendo il codice Python: gist.github.com/mappingvermont/d534539fa3ebe4a1e242644e528bf7b9
Charlie Hofmann

1

Se stai usando OpenLayers Map.getZoomForExtent, calcolerà il livello di zoom più alto che può adattarsi all'intera estensione sulla mappa. Le extentnecessità di essere in proiezione della mappa. Puoi anche usare a fill_factorper evitare di visualizzare punti sul bordo della mappa e max_zoomper limitare il possibile zoom:

extent = extent.scale(1/fill_ratio);
var zoom = Math.min(map.getZoomForExtent(map_extent), max_zoom);
map.setCenter(extent.getCenterLonLat(), zoom);

questo non ha risolto il mio problema, ma mi ha portato a trovare il metodo fitBounds di Leaflet, che mi ha salvato la giornata.
SamuelDev,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.