Como calcular o nível de zoom ideal para exibir dois ou mais pontos em um mapa

11

Queremos exibir vários marcadores em um mapa estático e queremos calcular o nível de zoom ideal, como o Google Maps faz. Já calculamos o retângulo delimitador e o ponto central do mapa, mas agora é difícil calcular o nível de zoom correto para exibir todo o retângulo delimitador. Alguém pode nos indicar a direção certa?

Tobias Schwarz
fonte
Não é muito sofisticado, mas por que não multiplicar o MBR por 120% e aumentar o zoom? Qualquer outra coisa depende da sua definição de "ideal", não é?
Thomm
+1 à ideia de ThomM. É exatamente isso que o ArcGIS faz.
Ragi Yaser Burhum 02/02/12
1
Desculpe, mas o que é esse MBR?
1111 Rodrigo

Respostas:

4

Para obter o nível de zoom, você precisará conhecer as dimensões de pixel do seu mapa. Você também precisará fazer suas contas em coordenadas esféricas de mercator.

  1. Converta latitude, longitude em mercator esférico x, y.
  2. Obtenha distância entre seus dois pontos no mercator esférico.
  3. O equador tem cerca de 40m de comprimento projetado e os ladrilhos têm 256 pixels de largura, portanto o comprimento de pixel desse mapa em um determinado nível de zoom é de cerca de 256 * distância / 40000000 * 2 ^ zoom . Tente zoom = 0, zoom = 1, zoom = 2 até que a distância seja muito longa para as dimensões de pixel do seu mapa.
Michal Migurski
fonte
1
Espero ter entendido remotamente o que você está realmente perguntando. = \
Michal Migurski
Encontrei esta pergunta enquanto procurava por esta resposta, obrigado.
21912 BenjaminGolder
@MichalMigurski, você pode explicar como calcular a distância entre 2 pontos no mercator esférico? especialmente as coordenadas x ... estou presa, obrigada.
Otmezger
4

Este é o código C # que eu uso no 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: caixa delimitadora em cordas longas / lat (x = long, y = lat)
  • paddingFactor: isso pode ser usado para obter o efeito "120%" a que ThomM se refere. O valor de 1,2 chegaria a 120%.

Note que no meu caso zoompode ser um número real. No caso de mapas da Web, você precisa de um valor inteiro de zoom, portanto, use algo como (int)Math.Floor(zoom)para obtê-lo.

Obviamente, esse código se aplica apenas à projeção do Web Mercator.

Igor Brejc
fonte
1
Obrigado por isso. O código do CenterMapOnPoint está disponível?
Mctyre321
Perfeito! Eu usei isso para zoom calcular de BoundingBox em osmdroid SDK e funciona :)
Billda
Onde encontro essas bibliotecas? Não consigo encontrar uma montagem GeometryUtils ou Bounds2. Preciso fazer o download de algo do maperitive? Eu só vejo um aplicativo lá.
Dowlers 03/07/19
O @Dowlers GeometryUtilsé usado aqui apenas para converter graus em radianos e vice-versa, é uma fórmula matemática simples. Bounds2é basicamente apenas uma estrutura retangular. Esse código foi fornecido mais como um pseudocódigo do que como algo diretamente passível de cópia.
Igor Brejc
Ahh, peguei você. Devo admitir que, depois de fazer a pergunta, dei uma espiada no aplicativo preventivo e encontrei as DLLs necessárias. Mas notei as restrições de licenciamento e segui outra rota. Em vez disso, usei o código deste artigo: rbrundritt.wordpress.com/2009/07/21/…
Dowlers
1

Se você estiver usando o OpenLayers, Map.getZoomForExtentcalculará o nível de zoom mais alto que pode caber em toda a extensão do mapa. O extentprecisa estar na projeção do mapa. Você também pode usar a fill_factorpara evitar a exibição de pontos na borda do mapa e max_zoompara limitar o possível zoom:

extent = extent.scale(1/fill_ratio);
var zoom = Math.min(map.getZoomForExtent(map_extent), max_zoom);
map.setCenter(extent.getCenterLonLat(), zoom);
Alex Morega
fonte
isso não resolveu meu problema, mas me levou a encontrar o método fitBounds do Leaflet, que salvou meu dia.
SamuelDev