Cómo calcular el nivel de zoom óptimo para mostrar dos o más puntos en un mapa

11

Queremos mostrar varios marcadores en un mapa estático y queremos calcular el nivel de zoom óptimo como lo hace Google Maps. Ya calculamos el rectángulo delimitador y el punto central del mapa, pero ahora nos resulta difícil calcular el nivel de zoom correcto para mostrar todo el rectángulo delimitador. ¿Alguien puede señalarnos en la dirección correcta?

Tobias Schwarz
fuente
No es muy sofisticado, pero ¿por qué no simplemente multiplicar el MBR por 120% y ampliarlo? Cualquier otra cosa depende de tu definición de "óptimo", ¿no?
ThomM
+1 a la idea de ThomM. Esto es exactamente lo que hace ArcGIS.
Ragi Yaser Burhum
1
Lo siento, pero ¿qué es este MBR?
Rodrigo

Respuestas:

4

Para obtener el nivel de zoom, deberá conocer las dimensiones en píxeles de su mapa. También necesitarás hacer tus cálculos en coordenadas esféricas de mercator.

  1. Convierta la latitud, longitud en mercator esférico x, y.
  2. Obtenga distancia entre sus dos puntos en mercator esférico.
  3. El ecuador tiene una proyección de unos 40 metros de largo y los mosaicos tienen 256 píxeles de ancho, por lo que la longitud de píxeles de ese mapa en un nivel de zoom dado es de aproximadamente 256 * distancia / 40000000 * 2 ^ zoom . Pruebe zoom = 0, zoom = 1, zoom = 2 hasta que la distancia sea demasiado larga para las dimensiones en píxeles de su mapa.
Michal Migurski
fuente
1
Espero haber entendido remotamente lo que realmente estás preguntando. = \
Michal Migurski
Encontré esta pregunta mientras buscaba esta respuesta, gracias.
BenjaminGolder
@MichalMigurski ¿puede explicar cómo calcular la distancia entre 2 puntos en mercator esférico? especialmente las coordenadas x ... estoy atrapado gracias.
otmezger
4

Este es el código C # que uso en 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: cuadro delimitador en long / lat coords (x = long, y = lat)
  • paddingFactor: esto se puede usar para obtener el efecto "120%" al que se refiere ThomM. El valor de 1.2 le daría el 120%.

Tenga en cuenta que en mi caso zoompuede ser un número real. En el caso de los mapas web, necesita un valor de zoom entero, por lo que debe usar algo como (int)Math.Floor(zoom)obtenerlo.

Por supuesto, este código solo se aplica a la proyección de Web Mercator.

Igor Brejc
fuente
1
Gracias por esto. ¿Está disponible el código para CenterMapOnPoint?
mcintyre321
¡Perfecto! He usado esto para calcular el zoom de BoundingBox en OsmDroid SDK y funciona :)
Billda
¿Dónde encuentro estas bibliotecas? No puedo encontrar un ensamblaje GeometryUtils o Bounds2. ¿Necesito descargar algo de maperitive? Solo veo una aplicación allí.
Dowlers
@Dowlers GeometryUtilssolo se usa aquí para convertir grados a radianes y viceversa, es una fórmula matemática simple. Bounds2es básicamente solo una estructura rectangular. Este código se proporcionó más como un pseudocódigo que como algo directamente copiable.
Igor Brejc
Ahh te tengo. Tengo que admitir que después de hacer la pregunta, busqué en la aplicación maperitiva y encontré los archivos .dlls que necesitaba. Pero noté las restricciones de licencia, así que tomé otra ruta. En su lugar utilicé el código de este artículo: rbrundritt.wordpress.com/2009/07/21/…
Dowlers
1

Si está utilizando OpenLayers Map.getZoomForExtent, calculará el nivel de zoom más alto que pueda ajustarse a toda la extensión en el mapa. La extentnecesidad de estar en la proyección del mapa. También puede usar a fill_factorpara evitar mostrar puntos en el borde del mapa y max_zoomlimitar el posible zoom:

extent = extent.scale(1/fill_ratio);
var zoom = Math.min(map.getZoomForExtent(map_extent), max_zoom);
map.setCenter(extent.getCenterLonLat(), zoom);
Alex Morega
fuente
Esto no solucionó mi problema, pero me llevó a encontrar el método fitBounds de Leaflet, que me salvó el día.
SamuelDev