{"id":25043180,"url":"https://github.com/moscarde/foliumtools","last_synced_at":"2025-04-14T01:33:01.269Z","repository":{"id":204268337,"uuid":"711025542","full_name":"Moscarde/FoliumTools","owner":"Moscarde","description":"Um breve guia de como utilizar ferramentas básicas do Folium para gerar mapas interativos através de dados geográficos em conjunto com Python, Pandas e GoogleMaps.","archived":false,"fork":false,"pushed_at":"2024-09-09T08:05:31.000Z","size":10537,"stargazers_count":12,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-27T15:48:24.667Z","etag":null,"topics":["data","data-visualization","folium","googlemaps-api","maps","pandas","phyton"],"latest_commit_sha":null,"homepage":"https://colab.research.google.com/drive/1AEf6BUQHY4TFrGWWuBS5RmLsmkhJy6mA?usp=sharing","language":"Jupyter Notebook","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Moscarde.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-28T02:07:23.000Z","updated_at":"2024-12-10T22:11:51.000Z","dependencies_parsed_at":null,"dependency_job_id":"dcb8a966-f429-4dd9-b9c4-b9b58ad92162","html_url":"https://github.com/Moscarde/FoliumTools","commit_stats":null,"previous_names":["moscarde/foliumtools"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Moscarde%2FFoliumTools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Moscarde%2FFoliumTools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Moscarde%2FFoliumTools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Moscarde%2FFoliumTools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Moscarde","download_url":"https://codeload.github.com/Moscarde/FoliumTools/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248808086,"owners_count":21164783,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data","data-visualization","folium","googlemaps-api","maps","pandas","phyton"],"created_at":"2025-02-06T04:53:02.021Z","updated_at":"2025-04-14T01:33:01.239Z","avatar_url":"https://github.com/Moscarde.png","language":"Jupyter Notebook","readme":"# Folium: Criando mapas interativos com dados reais de forma simples\n\u003ccenter\u003e\u003cimg src='images/Header 16-6.jpg'\u003e\u003c/center\u003e\n\n\u003e Folium é uma poderosa biblioteca Python que combina as a capacidades do ecossistema com a versatilidade da biblioteca Leaflet.js. Com o Folium podemos facilmente buscar, tratar e manipular nossos dados em python e, em seguida, visualiza-los de maneira interativa em um mapa Leaflet.\n\n[https://python-visualization.github.io/folium/latest/index.html](https://python-visualization.github.io/folium/latest/index.html)\n\n\u003eNo repositório é possível encontrar os arquivos utilizados durante o estudo. Também existe o link para a visualização do jupter notebook online na [área de links](#links-e-agradecimentos).\n\nNeste artigo vamos trabalhar exibindo dados com algumas ferramentas do Folium: \n\n- Criação do mapa interativo centralizado em uma coordenada geográfica\n- MarkerCluster: posiciona um marcador na coordenada passada; agrupa marcadores próximos de acordo com o zoom\n- HeatMap: cria um ponto de calor na coordenada passada; Possibilita identificar onde possui a maior intensidade de pontos pela cor\n- HeatMapWithTime: Cria uma linha temporal em mapas de calor\n\n### Instalação\n\nA biblioteca Folium pode ser facilmente instalada usando o comando:\n\n`$ pip install folium`\n\nSe você está usando o Conda, o equivalente é\n\n`$ conda install folium -c conda-forge`\n\n### Dependências\n\nO Folium tem as seguintes dependências: branca, Jinja2, Numpy e Requests, que serão instaladas automaticamente com o comando de instalação.\n## **Criando um mapa**\n\nEsse é o exemplo mais simples para a criação de um mapa:\n\n```python\nimport folium\n\nm = folium.Map(location=(-22.9140008, -43.563634))\n```\n\nPronto, está criado! Mas como podemos ver?\n\nSe estivermos em um Jupyter Notebook, podemos visualizar facilmente chamando a instancia:\n\n```python\nm\n```\n\n\u003ccenter\u003e\u003cimg src='screenshots/01-basic-map.jpg'\u003e\u003c/center\u003e\n\nPara visualiza todos os mapas interativos acesse o notebook no final do arquivo.\n\nOu podemos salvar como HTML:\n\n```python\nm.save(\"index.html\")\n```\n\nPodemos também mudar o tipo de mapa que é exibido facilmente adicionando o argumento “tiles” e o tipo de mapa. Por padrão é carregado o tileset *OpenStreetMap* porem vamos ver como fica utilizando o *cartodb positron*\n\n```python\nm = folium.Map(location=(-22.9140008, -43.563634), tiles=\"cartodb positron\")\nm\n```\n\n\u003ccenter\u003e\u003cimg src='screenshots/02-basic-map-custom-tile.jpg'\u003e\u003c/center\u003e\n\nAgora que entendemos um pouco sobre a criação do mapa com o Folium, vamos trabalhar com algumas ferramentas que ele disponibiliza para a visualização dos nossos dados!\n\nMas antes de iniciar, vamos carregar todas as libs:\n\n```python\nimport folium, googlemaps, time\nfrom folium.plugins import HeatMap, MarkerCluster, HeatMapWithTime\nimport pandas as pd\n```\n\n## MarkerCluster\n\nPara utilizar essa primeira ferramenta, pensei em trabalhar com dados reais, como por exemplo: Quais são os Mc Donald’s mais próximos que estão abertos nesse momento?\n\nPodemos conseguir esses dados de maneira fácil utilizando a biblioteca `googlemaps`.\n\nInstanciando o `googlemaps` com a chave API:\n\n```python\nclient = googlemaps.Client(chave_api)\n```\n\n\u003e Caso não tenha uma chave de api do google clouds, crie uma por aqui [https://cloud.google.com/?hl=pt-BR](https://cloud.google.com/?hl=pt-BR), após criar uma, ative as API’s: Places e Geocoding.\n \n\nNão tem uma chave mas quer acompanhar o tutorial? Todas as respostas recebidas pela API estão disponíveis no repositório. Carregue-as da seguinte maneira:\n\n```python\n import json\n with open(\"responses/01-geocode-response.json\", \"r\") as file:\n        response = json.load(file)\n\n# substitua a requisição à api pelas linhas acima\n# lembre de alterar o nome do arquivo\n```\n\nUtilizando o método `geocode` e passando uma string referente a um endereço, nos é retornado um dicionário repleto de informações. Vamos trabalhar nesse momento somente com as coordenadas.\n\n```python\nendereco = \"rio de janeiro, rj, brasil\"\ncoords_endereco = client.geocode(endereco)[0][\"geometry\"][\"location\"]\n\ncoords_endereco\n# {'lat': -22.9068467, 'lng': -43.1728965}\n```\n\nA partir dessa coordenada, vamos fazer uma busca pelos estabelecimentos próximos definindo uma query que, no nosso caso vai ser \"*Mc Donald’s*\"\n\n```python\nlista_coordenadas = []\nkeyword = \"Mc Donalds\"\nresponse = client.places(query=keyword, location=coords_endereco, open_now=True)\n\nfor result in response[\"results\"]:\n        latitude = result[\"geometry\"][\"location\"][\"lat\"]\n        longitude = result[\"geometry\"][\"location\"][\"lng\"]\n        lista_coordenadas.append([latitude, longitude])\n\nlista_coordenadas[0]\n# [-22.9137805, -43.1666602]\n```\n\n### Criando o mapa e adicionando os marcadores\n\nCom os dados em mãos, podemos inserir em um mapa da seguinte forma:\n\n```python\nmapa = folium.Map(location= lista_coordenadas[0], zoom_start= 13)\nmarker = MarkerCluster().add_to(mapa)\n\nMarkerCluster(locations=lista_coordenadas).add_to(mapa)\nmapa.save('mcdonalds_nearby_marker.html')\nmapa\n```\n\n\u003ccenter\u003e\u003cimg src='screenshots/03-mapa-mc-proximo.jpg'\u003e\u003c/center\n\n\nDessa vez definimos um zoom inicial para o nosso mapa com o `zoom_start=13`\n\nO argumento `locations` recebe uma lista de coordenadas e para cada uma delas insere um marcador no respectivo local do mapa.\n\nPodemos também estilizar o marcador, com os argumentos: \n\n```python\nfolium.Marker(\n    location=[-22.9068467, -43.1728965],\n    tooltip=\"Rio de Janeiro\",\n    popup=\"Informações\",\n    icon=folium.Icon(icon=\"user\", color=\"red\"),\n).add_to(mapa)\nmapa\n```\n\u003ccenter\u003e\u003cimg src='screenshots/04-custon-icon.jpg'\u003e\u003c/center\n\n\nVeja mais opções em: [https://github.com/lennardv2/Leaflet.awesome-markers](https://github.com/lennardv2/Leaflet.awesome-markers) e [https://fontawesome.com/search?s=solid\u0026f=classic\u0026o=r](https://fontawesome.com/search?s=solid\u0026f=classic\u0026o=r)\n\nPodemos então concluir que é bem simples apresentar esses dados em um mapa, permitindo uma exploração interativa para análises e insights geográficos. Mas vamos explorar mais algumas ferramentas. Dessa vez vamos tentar com um mapa de calor.\n\n## HeatMap\n\nUm gráfico de calor em um mapa, pode nos ajudar a obter informações valiosas. O mapa de calor facilita a identificação de repetições e padrões em coordenadas geoespaciais.\n\nPara exemplificar com dados reais um mapa de calor, vamos buscar as coordenadas de todos os *Mc Donald’s* de uma cidade. Vamos fazer uma busca parecida com a anterior, porém com um raio maior. Também vamos coletar um pouco mais de dados da resposta, para limpar e refinar os resultados. E para isso, nada melhor que usar o amigo do analista de dados, sim, estou falando do `Pandas`. 🐼\n\nVamos começar fazendo uma nova requisição, utilizando agora o `places_nearby()` para definir um raio maior de busca.\n\n```python\nlista_dfs = []\nkeyword = \"Mc Donalds\"\nresponse = client.places_nearby(location=coords_endereco, keyword=keyword, radius=50000)\n\nwhile True:\n    time.sleep(0.1)\n\n    for store in response[\"results\"]:\n        df = pd.DataFrame(\n            {\n                \"nome\": store[\"name\"],\n                \"endereco\": store[\"vicinity\"],\n                \"latitude\": store[\"geometry\"][\"location\"][\"lat\"],\n                \"longitude\": store[\"geometry\"][\"location\"][\"lng\"],\n            },\n            index=[0],\n        )\n        lista_dfs.append(df)\n\n    if \"next_page_token\" in response:\n        next_page_token = response[\"next_page_token\"]\n        time.sleep(2.3)\n        response = client.places_nearby(\n            location=coords_endereco,\n            keyword=keyword,\n            radius=50000,\n            page_token=next_page_token,\n        )\n\n    else:\n        break\n\ndf_resultados = pd.concat(lista_dfs, ignore_index=True)\ndf_resultados.sample(5)\n```\n\n\u003ccenter\u003e\u003cimg src='screenshots/05-df_resultados.jpg'\u003e\u003c/center\u003e\n\n- Como são muitos resultados, a API divide-os em páginas e envia na resposta um token para a próxima.\n- Após a primeira requisição é iniciado um loop infinito, que é interrompido quando não tem mais páginas. Dentro do loop é iniciado um for loop que itera por cada resultado da página, criando um dataframe para cada um.\n- Caso a página tenha um token para a próxima página, é feito uma nova requisição incluindo esse token como argumento.\n- Por fim, fazemos uma concatenação de DataFrames com `pd.concat()`\n\n\u003e Importante: Evite utilizar o método concat() dentro de loops, isso pode resultar em uma grande perca de eficiência. Para casos como esse, guarde os DataFrames em uma lista temporária e, ao final do loop, utilize o pd.concat() para unir todos os DataFrames da lista.\n\u003e \n\n### Tratando os dados\n\nApós criar o DataFrame com todos os resultados, podemos trata-los para remover possíveis resultados indesejados ou duplicados.\n\nE com os dados limpos, podemos salvar apenas o que nos interessa: as coordenadas.\n\n```python\nlojas_mc_filtrado = df_resultados[\n    (df_resultados[\"nome\"].str.lower().str.contains(\"mcdonald\"))\n    | df_resultados[\"nome\"].str.lower().str.contains(\"mc donald\")\n]\nlojas_mc_filtrado = lojas_mc_filtrado.drop_duplicates(subset=\"endereco\")\n\nlista_coordenadas = lojas_mc_filtrado[['latitude', 'longitude']].values\n\nprint('Quantidade de resultados:', len(lista_coordenadas))\n# Quantidade de resultados: 60\nprint('Exemplo de resultado:', lista_coordenadas[0])\n# Exemplo de resultado: [-22.9056077 -43.1761831]\n```\n\n### Plotando os dados no mapa\n\nAgora vamos utilizar o plugin HeatMap para criar os pontos de calor e em seguida adicionar no mapa com a função `add_to()`\n\n```python\nmap_calor = folium.Map(location=list(coords_endereco.values()), zoom_start=11, tiles=\"Cartodb dark_matter\",)\nHeatMap(lista_coordenadas, radius= 30).add_to(map_calor)\nmap_calor.save('mcdonalds_heatmap.html')\nmap_calor\n```\n\n\u003ccenter\u003e\u003cimg src='screenshots/06-mapa-calor1.jpg' width=48%\u003e \u003cimg src='screenshots/06-mapa-calor2.jpg'  width=48%\u003e\u003c/center\u003e\n\n\n\nCom dados parecidos aos anteriores porém em maior escala podemos identificar facilmente pontos de maior cobertura dos restaurantes.\n\n## HeatmapWithTime\n\nO mapa de calor com tempo possibilita visualizar de uma forma dinâmica como os pontos do mapa evoluem ao longo do tempo. Esse recurso é amplamente utilizado em diversas áreas, como na analise de tráfego de veículos ao longo do dia ou mapeamento de jogadores em esportes ao longo das partidas, entre muitos outros exemplos.\n\nPara exemplificar essa ferramenta vamos trabalhar usando a data de abertura das lojas da empresa renner (disponibilizadas publicamente em seu site: [https://lojasrenner.mzweb.com.br/](https://lojasrenner.mzweb.com.br/)) e analisar o avanço da cobertura da empresa pelo país ao longo do tempo.\n\n### Carregando e tratando dados\n\nPartindo do zero: vamos puxar todos os dados da planilha com a função `pd.read_excel()`. \n\n- Com o argumento `sheet_name` devemos passar o nome referente a aba que contem a lista de lojas.\n- Em `skiprows` vamos passar quantas linhas devemos pular de cabeçalho\n- E finalmente em `usecols`, selecionaremos as colunas que vamos começar a trabalhar\n\n```python\ndf = pd.read_excel(\n    \"Planilhas e Fundamentos .xlsm\", \n    sheet_name= 'Lista de Lojas | Stores List', \n    skiprows= 5,\n    usecols = ['Opening date', 'Country', 'UF', 'State', 'City'])\n\ndf_renner = df.copy()\ndf_renner\n```\n\n\u003ccenter\u003e\u003cimg src='screenshots/07-df_renner-raw.jpg'\u003e\u003c/center\u003e\n\n\u003e Pessoalmente gosto de criar copias de requisições ou de dataframes carregados de arquivos para não precisar executar a operação novamente caso faça algo errado durante a analise.\n\u003e \n\n```python\n# removendo dados faltantes\ndf_renner.dropna(inplace= True)\n\n# tratando a data de abertura\ndf_renner['Opening date'] = pd.to_datetime(df_renner['Opening date']) \n\n# filtrando o pais\ndf_renner = df_renner[df_renner['Country'] == 'Brazil']\n\n# criando uma coluna com endereco\ndf_renner['Address'] = df_renner.apply(lambda row: f\"{row['City']} - {row['State']} - {row['Country']}\", axis= 1)\n\ndf_renner.head(5)\n```\n\n\u003ccenter\u003e\u003cimg src='screenshots/08-df_renner-tratado-fix.jpg'\u003e\u003c/center\u003e\n\nCom os dados do endereço agora podemos fazer uma requisição ao `geocode` para obter as coordenadas do local e salvar em uma nova coluna.\n\n```python\ndef get_coords(address): return client.geocode(address)[0]['geometry']['location']\ndf_renner['Coords'] = df_renner['Address'].apply(lambda coords: tuple(get_coords(coords).values()))\n\ndf_renner_copia = df_renner.copy()\ndf_renner.head(4)\n```\n\n\u003ccenter\u003e\u003cimg src='screenshots/09-df_renner-coords.jpg'\u003e\u003c/center\u003e\n\n\nA partir desse df, deixaremos somente o que vamos precisar para criar os marcadores no mapa: as coordenadas e o ano de abertura da loja.\n\n```python\n# criando uma coluna com o ano\ndf_renner['Opening Year'] = df_renner['Opening date'].dt.year\n\n# removendo dados desnecessários\ndf_renner = df_renner.drop(['Country', 'UF', 'State', 'City', 'Opening date', 'Address'], axis=1)\n\n# ordenando pelo ano\ndf_renner = df_renner.sort_values(by='Opening Year')\n\ndf_renner.head(5)\n```\n\n\u003ccenter\u003e\u003cimg src='screenshots/10-df_renner-coord-year.jpg'\u003e\u003c/center\u003e\n\nEstamos quase lá, para criarmos o mapa de calor com linha do tempo precisamos criar uma `data` e um `index`. O `index` é uma lista com marcadores, para o nosso caso usaremos uma lista com todos os anos passados da abertura da primeira loja até o ano atual. Para a `data`, precisamos criar uma lista do mesmo tamanho do index, com coordenadas ou listas de coordenadas. \n\nVamos fazer alguns loops percorrendo o `index` e identificar qual loja está aberta e qual está fechada. Também podemos atribuir uma intensidade, de 0 à 1 junto com a coordenada. Criaremos uma definição de quanto mais tempo a loja está aberta, maior será a força.\n\n```python\n# criando o index de anos\nprimeiro_ano = df_renner[\"Opening Year\"].min()\nultimo_ano = df_renner[\"Opening Year\"].max()\nyear_index = list(range(primeiro_ano, ultimo_ano))\nyear_index.append(ultimo_ano)\n\ncoords_time_line = []\nfor year in year_index:\n    lojas_coordenadas_por_ano = []\n    for index, row in df_renner.iterrows():\n        # calcula o tempo que a loja está aberta\n        year_diff = year - row[\"Opening Year\"]\n\n        # caso seja menor que zero, não é adicionada ao mapa\n        if year_diff \u003c 0:\n            pass\n        else:\n            # adicionando uma lista com as cordenadas e a intensidade\n            coord = [row[\"Coords\"][0], row[\"Coords\"][1]]\n\n            # para ilustrar, podemos criar uma definição de força de acordo com\n            # a idade da loja\n            if year_diff == 0:\n                coord.append(0.5)\n            elif year_diff == 1:\n                coord.append(0.7)\n            elif year_diff \u003e 1:\n                coord.append(1)\n\n            lojas_coordenadas_por_ano.append(coord)\n\n    coords_time_line.append(lojas_coordenadas_por_ano)\n```\n\nPor fim, estamos com duas variáveis: `year_index` e `coords_time_line`, as duas com o mesmo tamanho. Agora basta criar o mapa:\n\n```python\nmapa_renner = folium.Map(location=[-17, -50], zoom_start=4)\nHeatMap = HeatMapWithTime(\n    data=coords_time_line,\n    index=year_index,\n    auto_play=True,\n    max_opacity=0.3,\n    name=\"Datas\",\n    blur=1,\n    min_speed=1,\n).add_to(mapa_renner)\nmapa_renner\n```\n\n![https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExd2gwbTMwcDMzenZ0YW85N21tZ3Fxbnlid3hqYTIyajVkZDJ1N2FsbCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/2zziEQokMuHYBZ0619/giphy.gif](https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExd2gwbTMwcDMzenZ0YW85N21tZ3Fxbnlid3hqYTIyajVkZDJ1N2FsbCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/2zziEQokMuHYBZ0619/giphy.gif)\n\nCom o HeatMapWithTime, também configuramos mais algumas coisas, como opacidade máxima, blur e velocidade mínima. \n\n## Conclusão\n\nO folium ainda oferece outras funcionalidades além das abordadas nesse artigo. Mas com uma exemplificação breve de algumas de suas ferramentas podemos perceber o potencial da biblioteca para análise e geração de insights a partir de dados geoespaciais. \n\nAliado a outras ferramentas para obtenção e manipulação de dados, o Folium se torna peça chave em um conjunto para a análise e visualização de dados. Sua leveza e baixa dificuldade para gerar mapas interativos possibilitam uma análise exploratória e explanatória eficaz.\n\n### **Referências**\n\n[https://python-visualization.github.io/folium/latest/](https://python-visualization.github.io/folium/latest/)\n\n[https://lojasrenner.mzweb.com.br/](https://lojasrenner.mzweb.com.br/)\n\n### Links e Agradecimentos\n\n[Jupyter Notebook Google Colab](https://colab.research.google.com/drive/1AEf6BUQHY4TFrGWWuBS5RmLsmkhJy6mA?usp=sharing)\n\n[Moscarde/FoliumTools](https://github.com/Moscarde/FoliumTools)\n\n[Perfil no LinkedIn](https://www.linkedin.com/in/moscarde/)\n\n[Perfil no GitHub](https://github.com/Moscarde/)\n\n[Postagem Medium](https://moscarde.medium.com/folium-criando-mapas-interativos-com-dados-reais-de-forma-simples-c20ab89b5c79)\n\nSe você leu até esse ponto, muito obrigado. Esta é minha primeira publicação, Fique à vontade para me seguir nas outras redes sociais e, se possível me conte o que achou do conteúdo. =)\n\nUm agradecimento especial ao [Henrique W. Franco](https://medium.com/u/1f3cc85024dc?source=post_page-----c20ab89b5c79--------------------------------)\nque me inspirou fortemente com diversos estudos de extrema qualidade.","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoscarde%2Ffoliumtools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoscarde%2Ffoliumtools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoscarde%2Ffoliumtools/lists"}