Queremos mostrar varios marcadores en un mapa estático y queremos calcular el nivel de zoom óptimo como hace Google Maps. Ya hemos calculado el rectángulo delimitador y el punto central del mapa, pero ahora nos cuesta calcular el nivel de zoom correcto para mostrar todo el rectángulo delimitador. ¿Puede alguien indicarnos la dirección correcta?
Respuestas
¿Demasiados anuncios?Para obtener el nivel de zoom, necesitarás conocer las dimensiones en píxeles de tu mapa. También tendrás que hacer tus cálculos en coordenadas mercator esféricas.
- Convertir latitud, longitud a mercador esférico x, y.
- Obtenga la distancia entre sus dos puntos en mercator esférico.
- El ecuador tiene unos 40 metros de longitud proyectada y las baldosas tienen 256 píxeles de ancho, por lo que la longitud en píxeles de ese mapa a un nivel de zoom determinado es de unos 256 * distancia/40000000 * 2^zoom . Pruebe con zoom=0, zoom=1, zoom=2 hasta que la distancia sea demasiado larga para las dimensiones en píxeles de su mapa.
Este es el código C# que utilizo 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
Caja delimitadora en coordenadas long/lat (x=long, y=lat)paddingFactor
: se puede utilizar para conseguir el efecto "120%" al que se refiere ThomM. Con un valor de 1,2 se obtendría el 120%.
Tenga en cuenta que en mi caso zoom
puede ser un número real. En el caso de los mapas web, se necesita un valor de zoom entero, por lo que se debe utilizar algo como (int)Math.Floor(zoom)
para conseguirlo.
Por supuesto, este código sólo se aplica a la proyección Web Mercator.
Si utiliza OpenLayers, entonces Map.getZoomForExtent
calculará el nivel de zoom más alto que pueda caber en toda la extensión del mapa. La página web extent
tiene que estar en la proyección del mapa. También puede utilizar un fill_factor
para evitar la visualización de puntos en el borde del mapa, y max_zoom
para limitar el zoom posible:
extent = extent.scale(1/fill_ratio);
var zoom = Math.min(map.getZoomForExtent(map_extent), max_zoom);
map.setCenter(extent.getCenterLonLat(), zoom);