Adicionar uma camada do mapa de calor (SDK do Android)

Observação

Desativação do SDK do Android do Azure Mapas

O SDK Nativo do Azure Mapas para Android já foi preterido e será desativado em 31/03/25. Para evitar interrupções de serviço, migre para o SDK da Web do Azure Mapas até 31/03/25. Para obter mais informações, confira O guia de migração do SDK do Android do Azure Mapas.

Mapas de calor, também conhecidos como mapas de densidade de ponto, são um tipo de visualização de dados. Eles são usados para representar a densidade de dados usando uma gama de cores e mostrar os "pontos de acesso" dos dados em um mapa. Mapas de calor são uma ótima maneira de renderizar conjuntos de valores com um grande número de pontos.

A renderização de dezenas de milhares de pontos como símbolos pode abranger a maior parte da área do mapa. Esse caso provavelmente resultará em muitos símbolos sobrepostos. O que dificulta uma compreensão melhor dos dados. No entanto, a visualização desse mesmo conjunto de dados como um mapa de calor facilita a visualização da densidade e da densidade relativa de cada ponto de dados.

Você pode usar mapas de calor em vários cenários diferentes, incluindo:

  • Dados de temperatura: fornece aproximações de qual é a temperatura entre dois pontos de dados.
  • Dados para sensores de ruído: mostra não apenas a intensidade do ruído em que o sensor está, mas também pode fornecer informações sobre a dissipação ao longo de uma distância. O nível de ruído em um site pode não estar alto. Se a área de cobertura de ruído de vários sensores se sobrepuser, é possível que essa área sobreposta venha a ter níveis de ruído maiores. Dessa forma, a área sobreposta estaria visível no mapa de calor.
  • Rastreamento de GPS: inclui a velocidade como um mapa de altura ponderada, em que a intensidade de cada ponto de dados é baseada na velocidade. Por exemplo, essa funcionalidade fornece uma maneira de ver onde um veículo estava acelerando.

Dica

Por padrão, as camadas do mapa de calor renderizam as coordenadas de todas as geometrias em uma fonte de dados. Para limitar a camada de modo que ela só renderize recursos de geometria de ponto, defina a opção filter da camada como eq(geometryType(), "Point"). Se você também quiser incluir recursos do MultiPoint, defina a opção filter da camada como any(eq(geometryType(), "Point"), eq(geometryType(), "MultiPoint")).


Pré-requisitos

Conclua as etapas no documento Início Rápido: Criar um aplicativo Android. Os blocos de código neste artigo podem ser inseridos no manipulador de eventos onReady dos mapas.

Adicionar uma camada do mapa de calor

Para renderizar uma fonte de dados de pontos como mapa de calor, passe sua fonte de dados para uma instância da classe HeatMapLayer e adicione-a ao mapa.

O exemplo de código a seguir carrega um feed GeoJSON de terremotos da última semana e os renderiza como um mapa de calor. Cada ponto de dados é renderizado com um raio de 10 pixels em todos os níveis de zoom. Para garantir uma melhor experiência do usuário, o mapa de calor está abaixo da camada de rótulo para que os rótulos fiquem claramente visíveis. Os dados neste exemplo são do Programa de riscos de terremoto USGS. Este exemplo carrega dados GeoJSON da Web usando o bloco de código do utilitário de importação de dados fornecido no documento Criar uma fonte de dados.

//Create a data source and add it to the map.
DataSource source = new DataSource();

//Import the geojson data and add it to the data source.
source.importDataFromUrl("https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson");

//Add data source to the map.
map.sources.add(source);

//Create a heat map layer.
HeatMapLayer layer = new HeatMapLayer(source,
  heatmapRadius(10f),
  heatmapOpacity(0.8f)
);

//Add the layer to the map, below the labels.
map.layers.add(layer, "labels");
//Create a data source and add it to the map.
val source = DataSource()

//Import the geojson data and add it to the data source.
source.importDataFromUrl("https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")

//Add data source to the map.
map.sources.add(source)

//Create a heat map layer.
val layer = HeatMapLayer(
    source,
    heatmapRadius(10f),
    heatmapOpacity(0.8f)
)

//Add the layer to the map, below the labels.
map.layers.add(layer, "labels")

A captura de tela a seguir mostra um mapa carregando um mapa de calor usando o código acima.

Mapear com camada do mapa de calor de terremotos recentes

Personalize a camada do mapa de calor

O exemplo anterior personalizou o mapa de calor, definindo as opções de raio e opacidade. A camada do mapa de calor fornece várias opções de personalização, incluindo:

  • heatmapRadius: Define um raio de pixel no qual renderizar cada ponto de dados. Você pode definir o raio como um número fixo ou como uma expressão. Usando uma expressão, você pode dimensionar o raio com base no nível de zoom, e representar uma área espacial consistente no mapa (por exemplo, um raio de 5 milhas).

  • heatmapColor: Especifica como o mapa de calor é colorido. Um gradiente de cor é um recurso comum dos mapas de calor. Você pode obter o efeito com uma interpolate expressão. Você também pode usar uma step expressão para colorir o mapa de calor, dividindo a densidade visualmente em intervalos que se assemelham a uma contorno ou a um mapa de estilo de radar. Essas paletas de cores definem as cores do valor de densidade mínimo ao máximo.

    Você especifica valores de cor para mapas de calor como uma expressão sobre o valor heatmapDensity. A cor da área em que não há dados é definida no índice 0 da expressão de "interpolação", ou a cor padrão de uma expressão "de nível". Você pode usar esse valor para definir uma cor de plano de fundo. Frequentemente, o valor é definido como transparente ou preto semitransparente.

    Aqui estão exemplos de expressões de cores:

    Expressão de cor de interpolação Expressão de cor de nível
    interpolate(
        linear(),
        heatmapDensity(),
        stop(0, color(Color.TRANSPARENT)),
        stop(0.01, color(Color.MAGENTA)),
        stop(0.5, color(parseColor("#fb00fb"))),
        stop(1, color(parseColor("#00c3ff")))
    )`
    step(
        heatmapDensity(),
        color(Color.TRANSPARENT),
        stop(0.01, color(parseColor("#000080"))),
        stop(0.25, color(parseColor("#000080"))),
        stop(0.5, color(Color.GREEN)),
        stop(0.5, color(Color.YELLOW)),
        stop(1, color(Color.RED))
    )
  • heatmapOpacity: Especifica o quanto opaca ou transparente é a camada do mapa de calor.

  • heatmapIntensity: Aplica um multiplicador para o peso de cada ponto de dados para aumentar a intensidade geral do mapa de calor. Isso causa uma diferença no peso dos pontos de dados, facilitando a visualização.

  • heatmapWeightPor padrão, todos os pontos de dados têm um peso de 1, portanto, e são ponderados igualmente. A opção de peso atua como um multiplicador, e você pode defini-la como um número ou uma expressão. Se um número for definido como o peso, é equivalente a colocar cada ponto de dados no mapa duas vezes. Por exemplo, se o peso for 2, a densidade dobrará. Definir a opção de peso para um número renderiza o mapa de calor de maneira semelhante a usar a opção de intensidade.

    No entanto, se você usar uma expressão, o peso de cada ponto de dados poderá ser baseado nas propriedades de cada ponto de dados. Por exemplo, suponha que cada ponto de dados representa um terremoto. O valor de magnitude tem sido uma métrica importante para cada ponto de dados de terremoto. Terremotos ocorrem o tempo todo, mas a maioria tem uma magnitude baixa e não são percebidos. Use o valor de magnitude em uma expressão para atribuir o peso a cada ponto de dados. Usando o valor de magnitude para atribuir o peso, você obtém uma representação melhor da significância dos terremotos dentro do mapa de calor.

  • minZoom e maxZoom: o intervalo de nível de zoom em que a camada deve ser exibida.

  • filter: uma expressão de filtro usada para limitar o que foi recuperado da origem e renderizado na camada.

  • sourceLayer: se a fonte de dados conectada à camada for uma fonte de bloco de vetor, uma camada de origem dentro dos blocos de vetor deverá ser especificada.

  • visible: oculta ou mostra a camada.

O trecho de código a seguir é um exemplo de um mapa de calor em que uma expressão de interpolação de linha é usada para criar um gradiente de cores suave. A propriedade mag definida nos dados é usada com uma interpolação exponencial para definir o peso ou a relevância de cada ponto de dados.

HeatMapLayer layer = new HeatMapLayer(source,
    heatmapRadius(10f),

    //A linear interpolation is used to create a smooth color gradient based on the heat map density.
    heatmapColor(
        interpolate(
            linear(),
            heatmapDensity(),
            stop(0, color(Color.TRANSPARENT)),
            stop(0.01, color(Color.BLACK)),
            stop(0.25, color(Color.MAGENTA)),
            stop(0.5, color(Color.RED)),
            stop(0.75, color(Color.YELLOW)),
            stop(1, color(Color.WHITE))
        )
    ),

    //Using an exponential interpolation since earthquake magnitudes are on an exponential scale.
    heatmapWeight(
       interpolate(
            exponential(2),
            get("mag"),
            stop(0,0),

            //Any earthquake above a magnitude of 6 will have a weight of 1
            stop(6, 1)
       )
    )
);
val layer = HeatMapLayer(source,
    heatmapRadius(10f),

    //A linear interpolation is used to create a smooth color gradient based on the heat map density.
    heatmapColor(
        interpolate(
            linear(),
            heatmapDensity(),
            stop(0, color(Color.TRANSPARENT)),
            stop(0.01, color(Color.BLACK)),
            stop(0.25, color(Color.MAGENTA)),
            stop(0.5, color(Color.RED)),
            stop(0.75, color(Color.YELLOW)),
            stop(1, color(Color.WHITE))
        )
    ),

    //Using an exponential interpolation since earthquake magnitudes are on an exponential scale.
    heatmapWeight(
       interpolate(
            exponential(2),
            get("mag"),
            stop(0,0),

            //Any earthquake above a magnitude of 6 will have a weight of 1
            stop(6, 1)
       )
    )
)

A captura de tela a seguir mostra a camada do mapa de calor personalizada acima usando os mesmos dados do exemplo do mapa de calor anterior.

Mapear com camada do mapa de calor personalizada de terremotos recentes

Mapa de calor com zoom consistente

Por padrão, o raios de pontos de dados renderizados na camada do mapa de calor têm um raio de pixel fixo para todos os níveis de zoom. À medida que você aplica zoom no mapa, os dados são agregados e a camada do mapa de calor fica diferente. O vídeo a seguir mostra o comportamento padrão do mapa de calor onde ele mantém um raio de pixel ao ampliar o mapa.

Animação mostrando um mapa ampliando com uma camada do mapa de calor mostrando um tamanho de pixel consistente

Use uma expressão zoom para dimensionar o raio para cada nível de zoom, de modo que cada ponto de dados cubra a mesma área física do mapa. Essa expressão faz com que a camada do mapa de calor pareça mais estática e consistente. Cada nível de zoom do mapa tem duas vezes mais pixels vertical e horizontalmente do que o nível de zoom anterior.

Dimensionar o raio para que ele fique duplo com cada nível de zoom cria um mapa de calor que parece consistente em todos os níveis de zoom. Para aplicar esse dimensionamento, use zoom com uma expressão base 2 exponential interpolation, com o conjunto de raio de pixel definido para o nível de zoom mínimo e um raio dimensionado para o nível máximo de zoom calculado como 2 * Math.pow(2, minZoom - maxZoom), como mostrado no exemplo a seguir. Aplique zoom no mapa para ver como o mapa de calor é dimensionado com o nível de zoom.

HeatMapLayer layer = new HeatMapLayer(source,
  heatmapRadius(
    interpolate(
      exponential(2),
      zoom(),

      //For zoom level 1 set the radius to 2 pixels.
      stop(1, 2f),

      //Between zoom level 1 and 19, exponentially scale the radius from 2 pixels to 2 * (maxZoom - minZoom)^2 pixels.
      stop(19, Math.pow(2, 19 - 1) * 2f)
    )
  ),
  heatmapOpacity(0.75f)
);
val layer = HeatMapLayer(source,
  heatmapRadius(
    interpolate(
      exponential(2),
      zoom(),

      //For zoom level 1 set the radius to 2 pixels.
      stop(1, 2f),

      //Between zoom level 1 and 19, exponentially scale the radius from 2 pixels to 2 * (maxZoom - minZoom)^2 pixels.
      stop(19, Math.pow(2.0, 19 - 1.0) * 2f)
    )
  ),
  heatmapOpacity(0.75f)
)

O vídeo a seguir mostra um mapa executando o código acima, que dimensiona o raio enquanto o mapa está sendo ampliado para criar uma renderização consistente do mapa de calor entre níveis de zoom.

Animação mostrando um mapa ampliando com uma camada do mapa de calor mostrando um tamanho de geoespacial consistente

A expressão zoom só pode ser usada nas expressões step e interpolate. A expressão a seguir pode ser usada para aproximar um raio em metros. Essa expressão usa um espaço reservado radiusMeters que você deve substituir pelo raio desejado. Essa expressão calcula o raio de pixel aproximado para um nível de zoom no equador para níveis de zoom 0 e 24, e usa uma expressão exponential interpolation para dimensionar entre esses valores da mesma maneira que o sistema lado a lado no mapa.

interpolate(
    exponential(2),
    zoom(),
    stop(1, product(radiusMeters, 0.000012776039596366526)),
    stop(24, product(radiusMeters, 214.34637593279402))
)

Dica

Quando você habilita o clustering na fonte de dados, os pontos próximos uns dos outros são agrupados em conjunto como um ponto clusterizado. Você pode usar a contagem de pontos de cada cluster como a expressão de peso para o mapa de calor. Isso pode reduzir significativamente o número de pontos a serem renderizados. A contagem de ponto de um cluster é armazenada em uma propriedade point_count do recurso de ponto:

HeatMapLayer layer = new HeatMapLayer(dataSource,
   heatmapWeight(get("point_count"))
);

Se o raio de clustering for apenas alguns pixels, haverá uma pequena diferença visual na renderização. Raios maiores agrupam mais pontos nos clusters de cada um, e melhoram o desempenho do mapa de calor.

interpolate(
    exponential(2),
    zoom(),
    stop(1, product(radiusMeters, 0.000012776039596366526)),
    stop(24, product(radiusMeters, 214.34637593279402))
)

Dica

Quando você habilita o clustering na fonte de dados, os pontos próximos uns dos outros são agrupados em conjunto como um ponto clusterizado. Você pode usar a contagem de pontos de cada cluster como a expressão de peso para o mapa de calor. Isso pode reduzir significativamente o número de pontos a serem renderizados. A contagem de ponto de um cluster é armazenada em uma propriedade point_count do recurso de ponto:

var layer = new HeatMapLayer(dataSource,
   heatmapWeight(get("point_count"))
)

Se o raio de clustering for apenas alguns pixels, haverá uma pequena diferença visual na renderização. Raios maiores agrupam mais pontos nos clusters de cada um, e melhoram o desempenho do mapa de calor.

Próximas etapas

Para obter mais exemplos de código para adicionar aos seus mapas, consulte os seguintes artigos: