Câmera e visualização

Os mapas no SDK do Maps para Android podem ser inclinados e girados com gestos fáceis. Assim, os usuários podem ajustá-los na orientação que preferirem. Em qualquer nível de zoom, é possível movimentar o mapa ou mudar a perspectiva com pouquíssima latência, graças ao volume reduzido de blocos com base em vetores do mapa.

Exemplos de código

O repositório ApiDemos no GitHub inclui um exemplo que demonstra os recursos da câmera:

Introdução

Assim como o Google Maps na Web, o SDK do Google Maps para Android representa a superfície do mundo (uma esfera) na tela do seu dispositivo (uma superfície plana), utilizando a projeção de Mercator. Nas direções Leste e Oeste, o mapa é repetido infinitamente à medida que o mundo se integra perfeitamente ao redor de si mesmo. Nas direções Norte e Sul, o mapa é limitado a aproximadamente 85 graus ao Norte e 85 graus ao Sul.

Observação: uma projeção de Mercator tem uma largura longitudinal finita e uma altura latitudinal infinita. Nós "cortamos" as imagens de Mapas básicos usando a projeção de Mercator em aproximadamente +/- 85 graus para que o formato resultante do mapa seja quadrado, o que proporciona uma lógica mais fácil para a seleção de blocos.

Com o SDK do Maps para Android, é possível alterar o ponto de vista do usuário do mapa modificando a câmera.

As mudanças da câmera não alteram marcadores, polilinhas ou outros elementos gráficos adicionados, embora você possa mudar suas adições para que se adequem melhor à nova visualização.

Como você pode detectar os gestos do usuário no mapa, é possível mudar o mapa em resposta às solicitações do usuário. Por exemplo, o método de callback OnMapClickListener.onMapClick() responde a um único toque no mapa. Como o método recebe a latitude e longitude do local do toque, você pode responder movimentando ou aplicando zoom a esse ponto. Métodos semelhantes estão disponíveis para responder aos toques no balão de um marcador ou para responder a um gesto de arrastar em um marcador.

Você também pode detectar os movimentos da câmera para que o app receba uma notificação quando a câmera começar a se mover, estiver se movendo ou interromper o movimento. Para mais detalhes, consulte o guia sobre eventos de mudança da câmera.

Construções em 3D no mapa

Quando vistas de perto, diversas cidades têm construções visíveis em 3D, como na imagem abaixo, de Vancouver, Canadá. Para desativar as construções em 3D, chame GoogleMap.setBuildingsEnabled(false).

Um mapa de Vancouver, Canadá

A posição da câmera

A visualização de mapa é modelada como uma câmera apontada para um plano nivelado. A posição da câmera, e, consequentemente, a renderização do mapa, é especificada pelas seguintes propriedades: alvo (localização de latitude/longitude), rolamento, inclinação e zoom.

Diagrama de propriedades da câmera

Alvo (local)

O alvo da câmera é o local do centro do mapa, especificado como coordenadas de latitude e longitude.

Rolamento (orientação)

O rolamento da câmera é a direção apontada por uma linha vertical no mapa, medida em graus no sentido horário a partir do Norte. Ao dirigir um carro, muitas vezes giramos um mapa de estradas para alinhá-lo com a direção da viagem, enquanto ao fazer uma trilha usando um mapa e uma bússola, geralmente orientamos o mapa para que a linha vertical aponte para o Norte. Com a API Maps você pode alterar o alinhamento ou rolamento de um mapa. Por exemplo, um rolamento de 90 graus resulta em um mapa no qual a direção para cima aponta para Leste.

Inclinação (ângulo de visão)

A inclinação define a posição da câmera em arco, entre uma posição central diretamente sobre o mapa e a superfície terrestre, medida em graus a partir do nadir (a direção que aponta diretamente abaixo da câmera). Quando você muda o ângulo de visão, o mapa é apresentado em perspectiva, com elementos distantes aparecendo menores, e elementos próximos aparecendo maiores. Isso é ilustrado abaixo.

Nas imagens a seguir, o ângulo de visão é 0 grau. A primeira imagem mostra um esquema da situação. O número 1 é a posição da câmera, e 2 é a posição atual do mapa. O mapa resultante está abaixo.

Captura de tela de um mapa com uma câmera posicionada em um ângulo de visão de 0 grau, com um nível 18 de zoom.
O mapa exibido com ângulo de visão padrão da câmera.
Diagrama que mostra a posição padrão da câmera, diretamente sobre a posição do mapa, em um ângulo de 0 grau.
O ângulo de visão padrão da câmera.

Nas imagens abaixo, o ângulo de visão é de 45 graus. A câmera não está inclinada a 45 graus. Em vez disso, ela se move no meio do caminho ao longo de um arco, entre diretamente para cima (0 grau) e o solo (90 graus), para a posição 3. A câmera ainda está direcionada para o ponto central do mapa, mas agora a área representada pela linha na posição 4 está visível.

Captura de tela de um mapa com uma câmera posicionada em um ângulo de visão de 45 graus, com um nível 18 de zoom.
O mapa exibido com um ângulo de visão de 45 graus.
Diagrama que mostra o ângulo de visão da câmera ajustado em 45 graus, com o nível de zoom ainda definido em 18.
Um ângulo de visão de 45 graus.

O mapa nesta captura de tela ainda está centralizado sobre o mesmo ponto do mapa original. No entanto, mais elementos apareceram na parte superior. À medida que você ultrapassa o ângulo de 45 graus, os elementos entre a câmera e a posição do mapa aparecem proporcionalmente maiores, enquanto os elementos que estão além da posição do mapa aparecem proporcionalmente menores, produzindo um efeito tridimensional.

Zoom

O nível de zoom da câmera determina a escala do mapa. Em níveis mais elevados de zoom, é possível ver mais detalhes na tela, enquanto em níveis de zoom menores, uma parte maior do mundo pode ser vista. Em um nível de zoom 0, a escala do mapa representa o mundo todo com uma largura de aproximadamente 256 dp (pixels de densidade independente).

Um aumento de zoom de 1 dobra a largura do mundo na tela. Assim, com o nível de zoom em N, a largura do mundo é aproximadamente 256 x 2N dp, ou seja, em nível de zoom 2, o mundo todo tem aproximadamente 1.024 dp de largura. O nível de zoom não precisa ser um número inteiro. A variação permitidas pelo mapa depende de diversos fatores, incluindo local, tipo de mapa e tamanho da tela. Qualquer número fora do intervalo será convertido para o próximo valor válido mais próximo, que pode ser o nível de zoom mínimo ou máximo. A lista a seguir mostra o nível aproximado de detalhamento que você consegue ver em cada nível de zoom:

  • 1: mundo
  • 5: terra/continente
  • 10: cidade
  • 15: ruas
  • 20: construções

As imagens a seguir mostram a aparência visual dos diferentes níveis de zoom:

Captura de tela de um mapa com nível 5 de zoom
Um mapa no nível de zoom 5.
Captura de tela de um mapa com nível 15 de zoom
Um mapa no nível de zoom 15.
Captura de tela de um mapa com nível 20 de zoom
Um mapa no nível de zoom 20.

Observação: devido ao tamanho e à densidade da tela, alguns dispositivos podem não ser compatíveis com os níveis de zoom mais baixos. Use GoogleMap.getMinimumZoomLevel() para ver o nível de zoom mínimo possível para o mapa. Se você precisa mostrar o mundo inteiro na janela de visualização, pode ser melhor usar o Modo Lite.

Como mover a câmera

Com a API Maps, você pode alterar a parte do mundo visível no mapa. Isso é possível, alterando a posição da câmera (em oposição ao movimento do mapa).

Quando você movimenta a câmera, você tem a opção de animar o movimento resultante da câmera. A animação alterna entre os atributos atuais e os novos atributos da câmera. Você também pode controlar a duração da animação.

Para mudar a posição da câmera, especifique para onde você quer movê-la, usando uma CameraUpdate. Com a API Maps, você pode criar vários tipos diferentes de CameraUpdate usando CameraUpdateFactory. As seguintes opções estão disponíveis:

Alterar o nível de zoom e definir o mínimo e o máximo

CameraUpdateFactory.zoomIn() e CameraUpdateFactory.zoomOut() oferecem um CameraUpdate que muda o nível de zoom em 1.0 sem afetar outras propriedades.

CameraUpdateFactory.zoomTo(float) oferece um CameraUpdate que muda o nível de zoom para o valor fornecido, mantendo todas as outras propriedades.

CameraUpdateFactory.zoomBy(float) e CameraUpdateFactory.zoomBy(float, Point) oferece CameraUpdate que aumenta (ou diminui, se o valor for negativo) o nível de zoom pelo valor fornecido. O último corrige o ponto determinado na tela, de forma que ele permanece no mesmo local (latitude/longitude) e, assim, pode alterar o local da câmera para essa finalidade.

Pode ser útil definir um nível de zoom mínimo e/ou máximo preferido. Por exemplo, isso pode ajudar a experiência do usuário quando o app exibir uma área definida em volta de um ponto de interesse ou quando houver uma sobreposição de blocos personalizada com um conjunto limitado de níveis de zoom.

Java

private GoogleMap map;
    map.setMinZoomPreference(6.0f);
    map.setMaxZoomPreference(14.0f);
      

Kotlin

private lateinit var map: GoogleMap

    map.setMinZoomPreference(6.0f)
    map.setMaxZoomPreference(14.0f)
      

Há considerações técnicas que podem evitar que a API permita que os usuários aumentem ou diminuam muito o zoom. Por exemplo, um mapa de satélite ou de terreno pode ter um zoom máximo menor que os blocos do Mapa básico.

Como alterar a posição da câmera

Há dois métodos convenientes para as alterações comuns de posição. CameraUpdateFactory.newLatLng(LatLng) oferece um CameraUpdate que muda a latitude e a longitude da câmera, preservando todas as outras propriedades. CameraUpdateFactory.newLatLngZoom(LatLng, float) oferece uma classe CameraUpdate que muda a latitude, a longitude e o zoom da câmera, preservando todas as outras propriedades.

Para ter flexibilidade total na alteração da posição da câmera, use CameraUpdateFactory.newCameraPosition(CameraPosition), que fornece um CameraUpdate, movendo a câmera para a posição determinada. Um CameraPosition pode ser obtido diretamente, usando new CameraPosition() ou com um CameraPosition.Builder usando new CameraPosition.Builder().

Movimento (rolagem)

CameraUpdateFactory.scrollBy(float, float) oferece um CameraUpdate que muda a latitude e longitude da câmera de modo que o mapa se mova pelo número especificado de pixels. Um valor X positivo move a câmera para a direita, fazendo com que o mapa pareça ser movido para a esquerda. Um valor Y positivo move a câmera para baixo, fazendo com que o mapa pareça ser movido para cima. Por outro lado, os valores negativos de X fazem a câmera se mover para a esquerda, de modo que o mapa pareça ter sido deslocado para a direita, enquanto valores negativos de Y fazem a câmera se mover para cima. A rolagem está relacionada à orientação atual da câmera. Por exemplo, se a câmera tem um rolamento de 90 graus, Leste será para "cima".

Como definir limites

Como definir os limites do mapa

Pode ser útil mover a câmera de maneira que uma área inteira de interesse fique visível no maior nível de zoom possível. Por exemplo, se você está exibindo todos os postos de gasolina em um raio de 5 km da posição atual do usuário, mova a câmera de modo que todos eles fiquem visíveis na tela. Para fazer isso, primeiro calcule os LatLngBounds que você quer ver na tela. Você pode usar CameraUpdateFactory.newLatLngBounds(LatLngBounds bounds, int padding) para ter uma classe CameraUpdate que muda a posição da câmera, de modo que as classes LatLngBounds especificadas se encaixem totalmente no mapa, considerando o preenchimento (em pixels) definido. A classe CameraUpdate retornada garante que a lacuna (em pixels) entre os limites indicados e a borda do mapa seja pelo menos igual ao preenchimento especificado. A inclinação e o rolamento do mapa serão 0.

Java

LatLngBounds australiaBounds = new LatLngBounds(
    new LatLng(-44, 113), // SW bounds
    new LatLng(-10, 154)  // NE bounds
);
map.moveCamera(CameraUpdateFactory.newLatLngBounds(australiaBounds, 0));
      

Kotlin

val australiaBounds = LatLngBounds(
    LatLng((-44.0), 113.0),  // SW bounds
    LatLng((-10.0), 154.0) // NE bounds
)
map.moveCamera(CameraUpdateFactory.newLatLngBounds(australiaBounds, 0))
      

Centralizar o mapa em uma área

Em alguns casos, você precisa centralizar a câmera dentro dos limites, em vez de incluir as bordas extremas, por exemplo, para centralizar a câmera em um país, mantendo um zoom constante. Nesse caso, é possível usar um método semelhante ao criar uma classe LatLngBounds e usar CameraUpdateFactory.newLatLngZoom(LatLng latLng, float zoom) com LatLngBounds.getCenter(). O método getCenter() retorna o centro geográfico de LatLngBounds.

Java

LatLngBounds australiaBounds = new LatLngBounds(
    new LatLng(-44, 113), // SW bounds
    new LatLng(-10, 154)  // NE bounds
);
map.moveCamera(CameraUpdateFactory.newLatLngZoom(australiaBounds.getCenter(), 10));
      

Kotlin

val australiaBounds = LatLngBounds(
    LatLng((-44.0), 113.0),  // SW bounds
    LatLng((-10.0), 154.0) // NE bounds
)
map.moveCamera(CameraUpdateFactory.newLatLngZoom(australiaBounds.center, 10f))
      

Uma sobrecarga do método, newLatLngBounds(boundary, width, height, padding), permite especificar a largura e a altura de um retângulo em pixels para que correspondam às dimensões do mapa. O retângulo é posicionado de modo que seu centro seja igual ao do ponto de visão do mapa (para que, se as dimensões especificadas forem as mesmas da visualização do mapa, o retângulo coincidirá com a visualização do mapa). A CameraUpdate retornada moverá a câmera para que o LatLngBounds especificado fique centralizado na tela dentro do retângulo determinado, no maior nível de zoom possível, considerando-se o preenchimento necessário.

Observação: só utilize o método mais simples newLatLngBounds(boundary, padding) para gerar um CameraUpdate se ele for usado para mover a câmera, depois que o mapa passar pelo layout. Durante o layout, a API calcula os limites de exibição do mapa, que são necessários para projetar corretamente a caixa delimitadora. Em comparação, você pode usar a classe CameraUpdate retornada pelo método newLatLngBounds(boundary, width, height, padding) mais complexo a qualquer momento, mesmo antes de o mapa passar pelo layout, já que a API calcula os limites de exibição com base nos argumentos que você transmite.

Restringir o deslocamento do usuário a uma determinada área

Nos cenários acima, você define os limites do mapa, mas o usuário pode rolar ou deslocar o mapa para fora desses limites. Em vez disso, pode ser útil restringir os limites centrais de lat/lng do ponto focal do mapa (o alvo da câmera) para que os usuários somente possam rolar e deslocar o mapa dentro desses limites. Por exemplo, um app de varejo de um shopping ou aeroporto pode querer restringir o mapa a determinados limites, permitindo que os usuários rolem e movimentem o mapa dentro desses limites.

Java

// Create a LatLngBounds that includes the city of Adelaide in Australia.
LatLngBounds adelaideBounds = new LatLngBounds(
    new LatLng(-35.0, 138.58), // SW bounds
    new LatLng(-34.9, 138.61)  // NE bounds
);

// Constrain the camera target to the Adelaide bounds.
map.setLatLngBoundsForCameraTarget(adelaideBounds);
      

Kotlin

// Create a LatLngBounds that includes the city of Adelaide in Australia.
val adelaideBounds = LatLngBounds(
    LatLng(-35.0, 138.58),  // SW bounds
    LatLng(-34.9, 138.61) // NE bounds
)

// Constrain the camera target to the Adelaide bounds.
map.setLatLngBoundsForCameraTarget(adelaideBounds)
      

O diagrama a seguir mostra um cenário em que o alvo da câmera está restrito a uma área ligeiramente maior que a janela de visualização. O usuário pode rolar e deslocar o mapa, desde que o alvo da câmera permaneça dentro da área limitada. A cruz representa o alvo da câmera:

Diagrama mostrando um cenário em que o alvo da câmera está restrito a uma área ligeiramente maior que a janela de visualização.

O mapa sempre preenche a janela de visualização, mesmo que o resultado seja a exibição de áreas fora dos limites definidos. Por exemplo, se você posicionar o alvo da câmera no canto da área limitada, a área além do canto ficará visível na janela de visualização, mas os usuários não poderão rolar o mapa para essa área. O diagrama a seguir ilustra esse cenário. A cruz representa o alvo da câmera:

Diagrama mostrando o alvo da câmera posicionado no canto inferior direito do LatLngBounds da câmera.

No diagrama a seguir, o alvo da câmera tem limites muito restritos, oferecendo ao usuário pouca oportunidade para rolar ou deslocar o mapa. A cruz representa o alvo da câmera:

Diagrama mostrando uma LatLngBounds da câmera menor que a janela de visualização.

Como atualizar a visualização da câmera

Para aplicar uma CameraUpdate ao mapa, você pode mover a câmera instantaneamente ou animá-la com suavidade. Para mover a câmera instantaneamente com a classe CameraUpdate fornecida, chame GoogleMap.moveCamera(CameraUpdate).

Você pode tornar a experiência do usuário mais agradável, especialmente nos movimentos curtos, animando a mudança. Para fazer isso, em vez de chamar GoogleMap.moveCamera, chame GoogleMap.animateCamera. O mapa se move suavemente para os novos atributos. A forma mais detalhada desse método, GoogleMap.animateCamera(cameraUpdate, duration, callback), oferece três argumentos:

cameraUpdate
A CameraUpdate descreve para onde mover a câmera.
callback
Um objeto que implementa GoogleMap.CancellableCallback. Essa interface generalizada para o processamento de tarefas define dois métodos, `onCancel()` e `onFinished()`. Para a animação, os métodos são solicitados nas seguintes circunstâncias:
onFinish()
Invocado se a animação passa para a conclusão sem interrupção.
onCancel()

Invocado se a animação é interrompida pela chamada a stopAnimation() ou pela inicialização de um novo movimento da câmera.

Como alternativa, isso também pode ocorrer se você chamar GoogleMap.stopAnimation().

duration
Duração desejada da animação, em milissegundos, como um int.

Os seguintes snippets de código ilustram algumas das formas comuns de mover a câmera.

Java

LatLng sydney = new LatLng(-33.88,151.21);
LatLng mountainView = new LatLng(37.4, -122.1);

// Move the camera instantly to Sydney with a zoom of 15.
map.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney, 15));

// Zoom in, animating the camera.
map.animateCamera(CameraUpdateFactory.zoomIn());

// Zoom out to zoom level 10, animating with a duration of 2 seconds.
map.animateCamera(CameraUpdateFactory.zoomTo(10), 2000, null);

// Construct a CameraPosition focusing on Mountain View and animate the camera to that position.
CameraPosition cameraPosition = new CameraPosition.Builder()
    .target(mountainView )      // Sets the center of the map to Mountain View
    .zoom(17)                   // Sets the zoom
    .bearing(90)                // Sets the orientation of the camera to east
    .tilt(30)                   // Sets the tilt of the camera to 30 degrees
    .build();                   // Creates a CameraPosition from the builder
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
      

Kotlin

val sydney = LatLng(-33.88, 151.21)
val mountainView = LatLng(37.4, -122.1)

// Move the camera instantly to Sydney with a zoom of 15.
map.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney, 15f))

// Zoom in, animating the camera.
map.animateCamera(CameraUpdateFactory.zoomIn())

// Zoom out to zoom level 10, animating with a duration of 2 seconds.
map.animateCamera(CameraUpdateFactory.zoomTo(10f), 2000, null)

// Construct a CameraPosition focusing on Mountain View and animate the camera to that position.
val cameraPosition = CameraPosition.Builder()
    .target(mountainView) // Sets the center of the map to Mountain View
    .zoom(17f)            // Sets the zoom
    .bearing(90f)         // Sets the orientation of the camera to east
    .tilt(30f)            // Sets the tilt of the camera to 30 degrees
    .build()              // Creates a CameraPosition from the builder
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))