Bot de aviso de queimadas
Usando dados públicos para informar focos de calor nas redondezas
Se você acompanha algum noticiário ou portal de notícias, com certeza está sabendo sobre as queimadas que estão acontecendo no centro-oeste brasileiro, especificamente na vegetação da Amazônia. Uma área gigantesca em chamas, provocando desde o perigo a vida de espécies como a onça-pintada até mesmo a do ser humano, afinal, existem inúmeras aglomerações de pessoas nessa região, sejam elas em cidades ou mesmo em fazendas.
Mas afinal, o que fazer para ajudar nesse momento tão difícil? Foi assim que surgiu a ideia desse projeto.
O objetivo aqui é bem simples, avisar as pessoas do quão distante existe um foco de calor, ou seja, uma queimada. Suponha que você seja um morador da cidade X, nosso objetivo é que você acorde e fale com o nosso chatbot:
“- Olá Bot de Queimadas, qual a queimada mais perto de mim?”
“- Olá, no momento a mais próxima está a 23 km”
Para isso iremos usar algumas bibliotecas bem interessantes do R chamadas rvest e stringr.
A biblioteca rvest permite que façamos buscas na internet e coletas de dados, ação comumente chamada de webscraping ou raspagem na web. Já stringr é usada como uma ferramenta de manipulação de textos, veremos adiante o motivo da importação delas.
Além dessas, importaremos também:
library(telegram)
library(telegram.bot)
library(geosphere)
library(readr)
As bibliotecas acima nos ajudarão no conceito de chatbot do Telegram e na manipulação de distâncias geográficas. Quanto a conta no telegram, recomendo que você entre em contato com o Bot Father que ele te explicará passo a passo de como fazer essa criação.
Boa, agora que você já tem a chave do seu Bot, podemos começar nosso projeto de fato:
bot <- TGBot$new(token = “your_key”)
bot$getMe()
bot$set_default_chat_id(“chat_id”)
No código acima você coloca a chave de acesso do seu bot e qual o chat_id da conversa, pra isso basta você mandar uma mensagem para o bot e rodar:
msgs <- bot$getUpdates()
Com isso você encontrará em msgs um dataframe com todas as configurações da conversa.
Agora que você montou a configuração inteira do bot, é hora de fazer ele conversar com você. Para isso você terá que ter em mente dois pontos principais:
- A menos que você coloque ele para rodar em uma nuvem, ele vai ter que ficar rodando 24 horas.
- Você precisa de tempo para digitar suas informações.
- Ele precisa interpretar de algum jeito o que você falou e gerar uma devolutiva com informação pertinente ao objetivo do projeto, no caso, a distância do foco de calor mais próximo.
Bom, trabalharemos aqui com a hipótese de que você não deseja gastar com algum serviço como AWS ainda, ou seja, você deixará o algoritmo rodando. Recomendo que você procure mais sobre agendamento de tarefas, pode ser útil nesses casos.
Como primeiro comando, estaremos interessados em chamar o bot para conversar, para isso configuraremos o seguinte código:
msgs <- bot$getUpdates()
ind = nrow(msgs)
while(ind == nrow(msgs)){
msgs <- bot$getUpdates()
Sys.sleep(1)
}
Com isso o algoritmo detecta quando você mandar alguma mensagem para acordar o bot, afinal, o histórico de mensagens vai ter aumentado um registro (no caso, uma linha). Dando sequência, queremos que nosso bot seja educado, não é mesmo?
bot$sendMessage(“Bem vindo ao Bot de Queimadas!”)
Sys.sleep(3)
bot$sendMessage(‘Para iniciar o monitoramento, mande “start”’)
msgs <- bot$getUpdates()
ind_start = nrow(msgs)
while(ind_start == nrow(msgs)){
msgs <- bot$getUpdates()
Sys.sleep(1)
}
msgs <- bot$getUpdates()
Assim, ajustamos uma mensagem de boas vindas. Junto a isso, aproveitamos para ver se você não apertou algum botão sem querer e iniciou o serviço do bot.
Tendo a confirmação por uma mensagem de start, começamos nosso trabalho na WEB:
aux = msgs$message$text %>% tail(1)
if(aux == “start”){
bot$sendMessage(‘Obrigado!’)
Sys.sleep(2)
bot$set_default_chat_id(“chat_id”)
bot$sendMessage(‘Agora insira seu CEP sem traço, por favor’)
msgs <- bot$getUpdates()
ind_cep = nrow(msgs)
while(ind_cep == nrow(msgs)){
msgs <- bot$getUpdates()
Sys.sleep(1)
}
}
msgs <- bot$getUpdates()
Veja, existem varias formas de como localizar um usuário, seja por endereço, CEP ou latitude e longitude. Entretanto, dificilmente você saberá sua latitude e longitude. Por outro lado, é muito mais fácil você saber o seu CEP, e mesmo que você não saiba, o site dos correios está ai para isso.
cep = as.numeric(msgs$message$text %>% tail(1))
url = paste(“https://www.mapacep.com.br/index.php?busca-cep=",cep)
url = gsub(“ “,””,url)
page <- read_html(url)
page %>%
html_nodes(xpath = ‘/html/body/main/div[3]/div/div[1]/p’) %>%
html_text() -> tab
Chegou a hora de desbravar o rvest! A menos que você tenha interesse em comprar a API do Google para captar a latitude e longitude por endereço essa é uma ferramenta bem útil. O que fazemos é:
- Juntamos o CEP e um link que capta a latitude e longitude
- Investigamos o código fonte
- Captamos a tabela que o site retorna por CEP
Incrivel né? Mais incrível ainda é o que fazemos depois:
latitude = str_extract(tab, “Latitude: -[0–9][0–9].[0–9][0–9][0–9][0–9][0–9][0–9][0–9]”)
latitude = as.numeric(gsub(“Latitude: “,””,latitude))
if(is.na(latitude) == TRUE){
latitude = str_extract(tab, “Latitude: -[0–9].[0–9][0–9][0–9][0–9][0–9][0–9][0–9]”)
latitude = as.numeric(gsub(“Latitude: “,””,latitude))
}
longitude = str_extract(tab, “Longitude: -[0–9][0–9].[0–9][0–9][0–9][0–9][0–9][0–9][0–9]”)
longitude = as.numeric(gsub(“Longitude: “,””,longitude))
if(is.na(longitude) == TRUE){
longitude = str_extract(tab, “longitude: -[0–9].[0–9][0–9][0–9][0–9][0–9][0–9][0–9]”)
longitude = as.numeric(gsub(“longitude: “,””,longitude))
}
cep_latlong = data.frame(latitude,longitude)
A “tabela” que o rvest retorna nesse caso é um grande texto. Mas não xingue o pobre rvest, nem mesmo o site que nos forneceram esses dados, é facilmente transformável em número como vemos a seguir, só tome cuidado pois existem latitudes e longitudes que podem ter 1 ou 2 números antes do ponto.
Ora ora, nosso bot está quase ganhando vida!
Agora vejamos, você já sabe a sua latitude e longitude, mas qual é a localização do foco de calor mais próximo?
Para conseguir esse tipo de dado captaremos os dados abertos fornecidos pelo INPE. Acho que cabe aqui o elogio para o brilhante trabalho que a equipe desse setor governamental vem fazendo, os dados estão em uma qualidade muito boa e você terá pouco trabalho de limpeza aqui.
CSV_URL = ‘http://queimadas.dgi.inpe.br/queimadas/dados-abertos/download/?utm_campaign=dados-abertos&outputFormat=csv&utm_medium=landing-page&time=24h&utm_content=focos_brasil_24h&id=focos_brasil&utm_source=landing-page' foco_atual = read_csv(CSV_URL) if(nrow(foco_atual) == 0){
foco_atual = read.csv(‘http://queimadas.dgi.inpe.br/queimadas/dados-abertos/download/?utm_campaign=dados-abertos&outputFormat=csv&utm_medium=landing-page&time=48h&utm_content=focos_brasil_ac_48h&id=focos_brasil_ac&utm_source=landing-page')
bot$sendMessage(‘O sistema ainda não tem dados atualizados para o dia de hoje, mas estamos buscando nas referências de ontem’)
}
O que fazemos é somente a coleta do dado disponível mais recente, caso o de 24 horas ainda não esteja disponível pegamos o de 48 horas.
distancia = c()
for (i in 1:nrow(foco_atual)) {
distancia[i] = distm(c(cep_latlong$longitude[1], cep_latlong$latitude[1]), c(foco_atual$longitude[i], foco_atual$latitude[i]), fun = distHaversine)/1000
}
bot$sendMessage(‘Baseado nos dados atualizados 24 horas por dia fornecidos pelo INPE, temos a queimada mais perto em quilometros vista pelo seguinte numero’)
bot$sendMessage(format(round(min(distancia), 2), nsmall = 2))
ind = nrow(msgs)
Agora fica simples, afinal, com a biblioteca de cálculo de distância geográfica que importamos basta chamar uma função e coletar a distância mínima em quilômetros obtida. Mas você pode estar se perguntando, “Legal, mas isso vai rodar uma vez só”.
Dá uma olhadinha agora com o “while” envolvendo todo processo:
while (TRUE) {msgs <- bot$getUpdates()
ind = nrow(msgs)
while(ind == nrow(msgs)){
msgs <- bot$getUpdates()
Sys.sleep(1)
}
bot$sendMessage(“Bem vindo ao Bot de Queimadas!”)
Sys.sleep(3)
bot$sendMessage(‘Para iniciar o monitoramento, mande “start”’)
msgs <- bot$getUpdates()
ind_start = nrow(msgs)
while(ind_start == nrow(msgs)){
msgs <- bot$getUpdates()
Sys.sleep(1)
}
msgs <- bot$getUpdates()
aux = msgs$message$text %>% tail(1)
if(aux == “start”){
bot$sendMessage(‘Obrigado!’)
Sys.sleep(2)
bot$set_default_chat_id(“chat_id”)
bot$sendMessage(‘Agora insira seu CEP sem traço, por favor’)
msgs <- bot$getUpdates()
ind_cep = nrow(msgs)
while(ind_cep == nrow(msgs)){
msgs <- bot$getUpdates()
Sys.sleep(1)
}
}
msgs <- bot$getUpdates()
cep = as.numeric(msgs$message$text %>% tail(1))
url = paste(“https://www.mapacep.com.br/index.php?busca-cep=”,cep)
url = gsub(“ “,””,url)
page <- read_html(url)
page %>%
html_nodes(xpath = ‘/html/body/main/div[3]/div/div[1]/p’) %>%
html_text() -> tab
latitude = str_extract(tab, “Latitude: -[0–9][0–9].[0–9][0–9][0–9][0–9][0–9][0–9][0–9]”)
latitude = as.numeric(gsub(“Latitude: “,””,latitude))
if(is.na(latitude) == TRUE){
latitude = str_extract(tab, “Latitude: -[0–9].[0–9][0–9][0–9][0–9][0–9][0–9][0–9]”)
latitude = as.numeric(gsub(“Latitude: “,””,latitude))
}
longitude = str_extract(tab, “Longitude: -[0–9][0–9].[0–9][0–9][0–9][0–9][0–9][0–9][0–9]”)
longitude = as.numeric(gsub(“Longitude: “,””,longitude))
if(is.na(longitude) == TRUE){
longitude = str_extract(tab, “longitude: -[0–9].[0–9][0–9][0–9][0–9][0–9][0–9][0–9]”)
longitude = as.numeric(gsub(“longitude: “,””,longitude))
}
cep_latlong = data.frame(latitude,longitude)CSV_URL = ‘http://queimadas.dgi.inpe.br/queimadas/dados-abertos/download/?utm_campaign=dados-abertos&outputFormat=csv&utm_medium=landing-page&time=24h&utm_content=focos_brasil_24h&id=focos_brasil&utm_source=landing-page'
foco_atual = read_csv(CSV_URL)
if(nrow(foco_atual) == 0){
foco_atual = read.csv(‘http://queimadas.dgi.inpe.br/queimadas/dados-abertos/download/?utm_campaign=dados-abertos&outputFormat=csv&utm_medium=landing-page&time=48h&utm_content=focos_brasil_ac_48h&id=focos_brasil_ac&utm_source=landing-page')
bot$sendMessage(‘O sistema ainda não tem dados atualizados para o dia de hoje, mas estamos buscando nas referências de ontem’)
}
distancia = c()
for (i in 1:nrow(foco_atual)) {
distancia[i] = distm(c(cep_latlong$longitude[1], cep_latlong$latitude[1]), c(foco_atual$longitude[i], foco_atual$latitude[i]), fun = distHaversine)/1000
}
bot$sendMessage(‘Baseado nos dados atualizados 24 horas por dia fornecidos pelo INPE, temos a queimada mais perto em quilometros vista pelo seguinte numero’)
bot$sendMessage(format(round(min(distancia), 2), nsmall = 2))
ind = nrow(msgs)
}
Prontinho, seu bot está pronto. Clique aqui e veja um vídeo de como será o resultado!