Vamos supor que você está trabalhando com dados do mundo real. E nesses dados você tem as latitudes e longitudes de algumas entidades. E ainda mais, você quer colocar essas entidades num mapa e mostrar isso para as suas coleguinhas. Como fazer?

gif de um mapa com cordoes

Bem, você tem algumas opções…

  • Colocar manualmente pontos no mapa usando o Google Maps, o que é até okay quando você tem 2 ou 3 entidades mas quando isso passa de 10 já fica meio chato né?!
  • Usar uma biblioteca/pacote de alguma linguagem de programação que faça isso. Particularmente minha opção favorita 💃🏻

Existem algumas bibliotecas disponíveis para isso, mas hoje vamos falar da Folium.

Folium

O propósito dessa biblioteca é unir o poder de manipulação de dados que Python possui com a força de visualização de mapas da biblioteca JavaScript Leaflet. A ideia aqui é tornar ainda mais fácil a missão de colocar pontos em um mapa.

Cadê o código?

Todo código que eu vou mostrar abaixo ta organizadinho neste diretório no GitHub, nele tem as instruções de preparação do ambiente tanto com docker quanto sem docker.

Instalação

Comece preparando o ambiente:

$ git clone https://github.com/jtemporal/intro-folium.git
$ cd intro-folium/

Com docker

Se você gosta da 🐳 :

$ docker-compose build

Sem docker

Se não é fã da 🐳:

$ pip install -r requirements.txt
$ pip install jupyter

Inciando

Depois disso, ainda no terminal:

Na versão 🐳:

$ docker-compose up jupy

ou sem docker:

$ jupyter notebook

Depois de rodar um dos comandos acima, você verá uma mensagem parecida com a seguinte no seu terminal:

foto do terminal rodando docker

Foto do terminal rodando o projeto com docker

A última linha indica o token de acesso, é só copiá-la e colocar no navegador para abrir o jupyter. Deve aparecer algo assim para você:

foto do navegador rodando jupyter

Foto do navegador rodando o jupyter

Se você rodar o notebook folium_intro.ipynb inteiro você vai obter os mesmos resultados que vou mostrar aqui. Além disso, se você clicar na imagem do mapas que vão vir a seguir, você vai ser levado para a página desses mesmos mapas o que vai de te deixar intergarir com eles 😉

Colocando a mão na massa

anime de garota no codigo

Começamos importando as bibliotecas necessárias e criando um mapa:

import pandas as pd
import folium

brasil = folium.Map(
    location=[-16.1237611, -59.9219642],    # Coordenadas retiradas do Google Maps
    zoom_start=4
)

O folium.Map() precisa apenas do parâmetro location para criar o seu mapa. Esse parâmetro recebe um par latitude-longitude que pode ser uma lista ou uma tupla. Aqui eu passei também um valor (4) para o parâmetro zoom_start, o padrão desse parâmentro é 10, mas um zoom de 10 não deixa o país inteiro na região do mapa por isso, o novo valor de 4. Ao chamar o mapa, temos:

brasil

mapa do brasil

Mapa clicável

Mas agora eu quero colocar pontos no mapa. Aqui vou usar de exemplo um conjunto de dados com geolocalização de empresas que apareceram nos reembolsos analisados pela Operação Serenata de Amor. No repositório intro-folium tem uma versão reduzida desse conjunto de dados. Então vamos começar lendo esses dados e colocando no mapa duas primeiras empresas:

empresas = pd.read_csv('empresas.xz')

empresa1 = empresas.iloc[0]
folium.Marker(
    location=[empresa1['latitude'], empresa1['longitude']],
).add_to(brasil)

empresa2 = empresas.iloc[1]
folium.Marker(
    location=[empresa2['latitude'], empresa2['longitude']],
).add_to(brasil)

O folium.Marker() coloca um “pin” de localização no mapa. Para isso ocorrer precisamos passar novamente a location e ainda indicar a qual mapa adicionar esse pin usando o metodo add_to(). Chamando nomavente o mapa, temos:

brasil

mapa do brasil com duas empresas

Mapa clicável

Resista a curiosidade de colocar todos as empresas num mapa. Com as mais de ciquenta mil empresas deste dataset, isso provavelmente irá travar o seu computador (travou o meu jupyter). Confie em mim, um mapa com todas as empresas do dataset fica assim:

mapa do brasil com todas empresas

Sabendo que mostrar a belezinha acima trava o computador, vamos então selecionar um estado apenas para mostrar:

empresas_pe = empresas[empresas['state'] == 'PE']

Para melhorar a visualização eu também iniciei um novo mapa, esse tem as coordenadas do estado de Pernambuco (meu estado de origem ❤️), veja:

pernambuco = folium.Map(
    location=[-8.3833569, -38.5757127],
    zoom_start=7
)

pernambuco

mapa de Pernambuco

Mapa clicável

E colocando as empresas no mapa:

for _, empresa in empresas_pe.iterrows():
    folium.Marker(
        location=[empresa['latitude'], empresa['longitude']],
    ).add_to(pernambuco)

pernambuco

mapa de Pernambuco com todas as empresas

Mapa clicável

Aqui ja podemos notar uma coisa que vale análise: Apesar de no dataset, apresentarem o estado cadastrado como Pernambuco, encontramos duas empresas que sua latitude e longitude não estão dentro do limite do estado 🤔

Puxa, muito legal isso, mas meio sem graça né? Então vamos dar uma olhada em como colorir um pouco as coisas?

Vamos começar reduzindo ainda mais nossa quantidade de empresas. Selecionei as 110 empresas de Olinda ❤️, e coloquei elas no mapa:

empresas_olinda_pe = empresas[empresas['city'] == 'OLINDA']

olinda = folium.Map(
    location=[-7.9981267, -34.9082027],
    zoom_start=13
)

for _, empresa in empresas_olinda_pe.iterrows():
    folium.Marker(
        location=[empresa['latitude'], empresa['longitude']],
    ).add_to(olinda)

olinda

mapa de Olinda com todas as empresas

Mapa clicável

Depois escolhi alguns bairros dos 22 bairros com empresas para colorir criando um dicionário que mapeia o nome de um bairro para uma cor:

colors = {
 'AMPARO': 'pink',
 'GUADALUPE': 'blue',
 'CASA CAIADA': 'green',
 'PEIXINHOS': 'orange',
 'RIO DOCE': 'red',
 'BAIRRO NOVO': 'purple',
}

olinda = folium.Map(
    location=[-7.9981267, -34.9082027],
    zoom_start=13
)

for _, empresa in empresas_olinda_pe.iterrows():
    if empresa['neighborhood'] in colors.keys():
        folium.Marker(
            location=[empresa['latitude'], empresa['longitude']],
            icon=folium.Icon(color=colors[empresa['neighborhood']])
        ).add_to(olinda)

Aqui usamos um novo parâmetro do folium.Marker(), o icon que recebe um objeto do tipo folium.Icon e assim a coloração que queremos. Note que, antes de colocar os pontos no mapa, eu limpo o mapa de Olinda recriando esse objeto e assim, ao invés de colocar os pins coloridos por cima do mapa antigo com os pins azuis. Ainda mais um detalhe, esse tipo de marker (que parece um 📌) segue as cores do bootstrap, ou seja, é limitado, mas para esse exemplo serve:

olinda

mapa de Olinda com algumas empresas e pins coloridos

Mapa clicável

Três coisinhas a mais

Mapas em páginas web

Você pode salvar seus mapas como páginas HTML usando o comando abaixo, basta identificar o mapa que quer salvar, usar o método .save() e passar um nome do arquivo como como parâmetro. Todas as páginas de mapas desse post foram geradas assim:

olinda.save('mapas_htmls/olinda_some_colored.html')

Informações extras num mapa

Também pode passar informações sobre a entidade que você está colocando no mapa usando o parâmetro popup. Assim ao clicar em um dos pins, um balão mostra alguma informação:

folium.Marker(
    location=[empresa['latitude'], empresa['longitude']],
    popup='sou uma empresa de Olinda',
    icon=folium.Icon(color=colors[empresa['neighborhood']])
).add_to(olinda)

foto do popup de um pin

Foto do balão de uma empresa após o clique

Um arco-iris

Como falei antes, o folium.Marker() possui cores limitadas, então se você precisar de mais cores, ou se você precisar colocar cores diferentes daquelas disponíveis, use ou o folium.CircleMarker() ou o folium.PolygonMarker(). Ambos aceitam hash de cores.

E aí, vai fazer uns mapas também? 😉


Curiosidades e dicas

Mapas (e dados) abertos

A Leaflet, e assim por consequência também o Folium, usa o OpenStreetMap para montar seus mapas. Os mapas da OpenStreetMap são abertos e construídos por uma comunidade de pessoas interessadas em manter/ajudar o projeto, por exemplo, se você encontrar algum erro em alguma parte do mapa você pode enviar correções. Recomendo dar uma olhadinha na Wiki do OpenStreetMap para mais informações.

df.iloc[i]

Forma de visualizar qualquer linha do dataframe como se fosse um print bonito, basta passar como i o índice da linha.

df.iterrows()

É muito útil para percorrer as linhas de um dataframe uma a uma. Esse método retorna sempre um índice e a linha correspondente.