* [[01_curso_atual:alunos:trabalho_final:jailsonleocadio:start|Home]] * [[.:exec]|Exercícios]] * [[.:proposta1jnl]|Trabalho final]] A função em R encontra-se abaixo. **Notas**: * Para execução da função **é necessário conexão com a internet**; * A função requer os pacotes "**jsonlite**" e "**maps**" instalados; * O retorno de um grande número de observações provoca demora na execução da função.; * Na proposta, eu havia indicado que o parâmetro '**captive**' seria por padrão FALSE. Aqui ele não tem valor padrão; os espécimes cativos e não cativos são retornados; * Na proposta, eu havia indicado que o parâmetro '**geo**' seria por padrão FALSE. Aqui ele não tem valor padrão; as observações com ou sem dados de latitude e longitude são retornadas; * Na proposta, eu havia indicado que o parâmetro '**quality_grade**' seria por padrão RESEARCH. Aqui ele não tem valor padrão; as observações podem ser dos três tipo existentes. inaturalist = function(taxon_name = NULL, place = NULL, captive = NULL, geo = NULL, observed_on = NULL, quality_grade = NULL, date = NULL, circle = NULL, maxresults = 500) { # Apenas 'maxresults' tem valor default, os demais argumentos sao definidos inicialmente como nulos if (is.null(c(taxon_name, place, captive, geo, observed_on, quality_grade, date, circle))) { # Verifica se pelo menos um argumento foi informado, com excecao de 'maxresults' stop ("Nenhum parâmetro foi informado!") # Mensagem de erro se nenhum argumento for informado } library(jsonlite) # Carrega biblioteca que permite acessar os dados da base de dados remota first = TRUE # Variavel de controle para definir o primeiro parametro a ser inserido na URL de busca. Apenas o primeiro nao e antecedido por "&" url = "http://api.inaturalist.org/v1/observations?" # URL base para a busca das observacoes if (!is.null(taxon_name)) { # Verifica se o argumento 'taxon_name' foi informado if (class(taxon_name) != "character") { # Verifica se a classe do argumento 'taxon_name' e "character" stop ("Valor do parâmetro 'taxon_name' inválido!") # Mensagem de erro se a classe do argumento 'taxon_name' nao for "character" } option = ifelse(first, "taxon_name=", "&taxon_name=") # Se for o primeiro parametro a ser inserido na URL, adiciona sem '&', do contrario, adiciona com '&' first = FALSE # Define que o primeiro parametro ja foi informado, assim, os proximos parametros serao adicionados sem '&' taxon_name = gsub(" ", "%20", tolower(taxon_name)) # Troca espaços por %20, de acordo com as regras de criacao de URL's url = paste(url, option, taxon_name, sep="") # Combina o argumento na URL de busca } if (!is.null(place)) { # Verifica se o argumento 'place' foi informado if (class(place) != "character") { # Verifica se a classe do argumento 'place' e "character" stop ("Valor do parâmetro 'place' inválido!") # Mensagem de erro se a classe do argumento 'place' nao for "character" } places = c(id=NA, name=NA) # Estrutura a variavel que guardara os locais que serao buscados placex = gsub("([~'`^])", "", iconv(place, to="ASCII//TRANSLIT")) # Regex que remove os acentos graficos do argumento '' url_place = paste("http://api.inaturalist.org/v1/places/autocomplete?q=", gsub(" ", "%20", tolower(placex)), sep="") # Preprara a URL de busca de locais (nao confundir com a URL de busca das observacoes) data_place = fromJSON(url_place) # Realiza a busca de locais apenas para pegar o total encontrados url_place = paste(url_place, "&per_page=", data_place$total_results, sep="") # Redefine a URL de busca de locais para trazer todos de uma vez (por padrao, o total e dividido por pagina e seria necessario percorrer cada uma delas) data_place = fromJSON(url_place) # Realiza novamente busca de locais, dessa vez retornando todos os itens encontrados if (data_place$total_results > 0) { # Verifica se a busca encontrou retornou algo places = data_place$results[c("id", "name")] # Armazena apenas os id's e os nomes dos locais places = places[tolower(gsub("([~'`^])", "", iconv(places$name, to="ASCII//TRANSLIT"))) == tolower(placex),] # Mantem apenas os locais em que os nomes casam com o argumento informado (nao considera acentos) option = ifelse(first, "place_id=", "&place_id=") # Verifica se 'local' e o primeiro parametro informado first = FALSE # Define que o primeiro parametro ja foi informado places_id = paste(places$id, collapse=",") # Separa os id's dos locais encontrados, separados por virgula url = paste(url, option, places_id, sep="") # Combina os id's na URL de busca de observacoes } else { warning(paste("Nenhum local encontrado para ", place, ". A busca não filtrará as observações por local!", sep="")) # Notifica que nao foi encontrado um local que case com o argumento informado e que a busca de observacoes sera realizada sem o parametro } } if (!is.null(captive)) { # Verifica se o argumento 'captive' foi informado if (class(captive) != "logical") { # Verifica se a classe do argumento 'captive' e "logical" stop ("Valor do parâmetro 'captive' inválido!") # Mensagem de erro se a classe do argumento nao for "logical" } option = ifelse(first, "captive=", "&captive=") # Se for o primeiro parametro a ser inserido na URL, adiciona sem & first = FALSE # Define que o primeiro parametro ja foi informado url = paste(url, option, tolower(captive), sep="") # Combina o argumento na URL de busca, com letras minusculas } if (!is.null(geo)) { # Verifica se o argumento 'geo' foi informado if (class(geo) != "logical") { # Verifica se a classe do argumento 'geo' e "logical" stop ("Valor do parâmetro 'geo' inválido!") # Mensagem de erro se a classe do argumento 'geo' nao for "logical" } option = ifelse(first, "geo=", "&geo=") # Se for o primeiro parametro a ser inserido na URL, adiciona sem & first = FALSE # Define que o primeiro parametro ja foi informado url = paste(url, option, tolower(geo), sep="") # Combina o argumento na URL de busca, com letras minusculas } if (!is.null(observed_on)) { # Verifica se o argumento foi informado if (class(observed_on) != "Date") { # Verifica se a classe do argumento 'observed_on' e "Date" stop ("Valor do parâmetro 'observed_on' inválido!") # Mensagem de erro se a classe do argumento nao for "Date" } option = ifelse(first, "observed_on=", "&observed_on=") # Se for o primeiro parametro a ser inserido na URL, adiciona sem & first = FALSE # Define que o primeiro parametro ja foi informado url = paste(url, option, format(observed_on, "%Y-%m-%d"), sep="") # Combina o argumento na URL de busca, no formato %Y-%m-%d } if (!is.null(quality_grade)) { # Verifica se o argumento 'quality_grade' foi informado if ((class(quality_grade) != "character") | !(tolower(quality_grade) %in% c("casual", "needs_id", "research"))) { # Verifica se a classe do argumento e se ele esta dentro das opcoes disponiveis stop ("Valor do parâmetro 'quality_grade' inválido!") # Mensagem de erro se algum problema for encontrado com o argumento } option = ifelse(first, "quality_grade=", "&quality_grade=") # Se 'quality_grade' for o primeiro parametro a ser inserido na URL, adiciona sem & first = FALSE # Define que o primeiro parametro ja foi informado url = paste(url, option, tolower(quality_grade), sep="") # Combina o argumento na URL de busca } if (!is.null(date)) { # Verifica se o argumento 'date' foi informado if (class(date) != "list" | length(date) > 3 | class(unlist(date)) != "numeric") { # Verifica se o argumento e do tipo lista, se tem no maximo 3 elementos e se o tipo de dados deles e "numeric" stop ("Valor do parâmetro 'date' inválido!") # Mensagem de erro se algum problema for encontrado com o argumento } if (!is.null(date$day)) { # Verifica se a opcao 'day' foi informada if (!date$day %in% 1:31) { # Verifica se a opcao 'day' esta entre 1 e 31 stop ("Valor do parâmetro 'day' em 'date' inválido!") # Mensagem de erro se a opcao 'day' estiver em intervalo invalido } option = ifelse(first, "day=", "&day=") # Verifica se a opcao 'day' e o primeiro parametro de busca url = paste(url, option, date$day, sep="") # Combina a opcao 'day' na URL de busca first = FALSE # Define que o primeiro parametro ja foi informado } if (!is.null(date$month)) { # Verifica se a opcao 'month' foi informada if (!date$month %in% 1:12) { # Verifica se a opcao 'month' esta entre 1 e 12 stop ("Valor do parâmetro 'month' em 'date' inválido!") # Mensagem de erro se a opcao 'month' estiver em intervalo invalido } option = ifelse(first, "month=", "&month=") # Verifica se a opcao 'month' e o primeiro parametro de busca url = paste(url, option, date$month, sep="") # Combina a opcao 'month' na URL de busca first = FALSE # Define que o primeiro parametro ja foi informado } if (!is.null(date$year)) { # Verifica se a opcao 'year' foi informada option = ifelse(first, "year=", "&year=") # Verifica se a opcao 'year' e o primeiro parametro de busca url = paste(url, option, date$year, sep="") # Combina a opcao 'year' na URL de busca first = FALSE # Define que o primeiro parametro ja foi informado } } if (!is.null(circle)) { # Verifica se o argumento 'circle' foi informado if ((class(circle) != "list") | (length(circle) != 3 | class(unlist(circle)) != "numeric")) { # Verifica se o argumento e do tipo lista, se tem 3 elementos e se o tipo de dados deles e "numeric" stop ("Valor do parâmetro 'circle' inválido!") # Mensagem de erro se algum problema for encontrado com o argumento } if (!is.null(circle$lat)) { # Verifica se a opcao 'lat' foi informada option = ifelse(first, "lat=", "&lat=") # Verifica se a opcao 'lat' e o primeiro parametro de busca url = paste(url, option, circle$lat, sep="") # Combina a opcao 'lat' na URL de busca first = FALSE # Define que o primeiro parametro ja foi informado. Nesse caso, como as tres opcoes sao obrigatorias, se este foi o primeiro, nao sera necessario verificar nos proximos } else { stop ("Valor do parâmetro 'circle' inválido!") # Se nao tem a opcao 'lat', ha algo de errado no parametro, pois nesse ponto sabemos que existem 3 objetos no argumento } if (!is.null(circle$lng)) { # Verifica se a opcao 'lng' foi informada url = paste(url, "&lng=", circle$lng, sep="") # Combina a opcao 'lng' na URL de busca } else { stop ("Valor do parâmetro 'circle' inválido!") # Se nao tem a opcao 'lng', ha algo de errado no parametro, pois nesse ponto sabemos que existem 3 objetos no argumento } if (!is.null(circle$radius)) { # Verifica se a opcao 'radius' foi informada url = paste(url, "&radius=", circle$radius, sep="") # Combina a opcao 'radius' na URL de busca } else { stop ("Valor do parâmetro 'circle' inválido!") # Se nao tem a opcao 'radius', ha algo de errado no parametro, pois nesse ponto sabemos que existem 3 objetos no argumento } } if (class(maxresults) != "numeric") { # Verifica se o argumento foi informado stop ("Valor do parâmetro 'maxresults' inválido!") # Mensagem de erro se o tipo do argumento nao for "numeric" } url = paste(url, "&order=desc&order_by=created_at", sep="") # Define a ordenacao dos dados que serao encontrados: decrescente por data de criacao data = fromJSON(url) # Realiza a busca das observacoes na base de dados if (data$total_results == 0) { # Verifica se a busca retornou observacoes stop ("Não encontramos observações com os filtros passados!") # Exibe mensagem de erro se nao houver observacoes } data_to_return = c(id=NA, observed_on=NA, captive=NA, quality_grade=NA, latitude=NA, longitude=NA, taxon_name=NA, place_guess=NA, url=NA) # Prepara formato da tabela que sera retornada (essa linha sera removida posteriormente) i = 1; # Inicia contador do loop while(TRUE) { # Loop infinito (o criterio de parada e feito dentro da repeticao) if (i > ceiling(data$total_results / data$per_page)) { # Os dados sao divididos em paginas. Aqui e verificado se todas elas ja foram acessadas. Esse e o criterio de parada break # Para a repeticao se todas as paginas de resultado ja foram acessadas } url_page = paste(url, "&page=", i, sep="") # Preprara a URL para acessar cada pagina individualmente. O valor de 'i' indica a pagina atual data = fromJSON(url_page) # Refaz a busca para uma unica pagina dt1 = NULL # Data.frama temporario para armazenar os dados da pagina dt1 = data$results[c("id", "observed_on", "captive", "quality_grade")] # Seleciona algumas colunas da tabela de resultado if (!is.null(nrow(data$results$geojson))) { # Verifica se existe pelo menos um dado de latitude e longitude na pagina data$results$geojson$coordinates[sapply(data$results$geojson$coordinates, is.null)] = NA # Insere NA nos locais que nao ha dados de latitude e longitude coordinates = unlist(data$results$geojson$coordinates) # Transforma a lista de coordenadas em vetor if (sum(is.na(coordinates)) > 0) { # Verifica se existem NA's nas coordenadas indexes = which(is.na(coordinates)) # Pega os indices das posicoes com NA's for (j in rev(indexes)) { # Percorre os NA's na ordem inversa coordinates = append(coordinates, NA, j) # Adiciona mais um NA logo em seguida. Tinhamos apenas um NA para a dupla latitude e longitude. Agora temos um valor NA para cada } } dt1$longitude = as.numeric(coordinates[seq(from=1, to=(2 * nrow(dt1)), by=2)]) # Separa os indices impares das coordenadas para a coluna longitude dt1$latitude = as.numeric(coordinates[seq(from=2, to=(2 * nrow(dt1)), by=2)]) # Separa os indices pares das coordenadas para a coluna latitude } dt1$taxon_name = unlist(data$results$taxon$name) # Transforma a lista de nomes dos taxos em vetor e adiciona no data.frama temporario dt1$place_guess = unlist(data$results$place_guess) # Transforma a lista de locais em vetor e adiciona no data.frama temporario dt1$url = unlist(data$results$uri) # Transforma a lista de URL's das observacoes em vetor e adiciona no data.frama temporario data_to_return = rbind(data_to_return, dt1) # Combina o data.frama temporario com os dados que serao retornados if (i * data$per_page >= maxresults + 1) { # Verifica se ja atingimos o valor maximo de observacoes definido em 'maxresults' data_to_return = data_to_return[1:(maxresults + 1),] # Caso sim, remove os excedentes break } i = i + 1 # Incrementa o contador (indica a pagina) } data_to_return = data_to_return[-1,] # Remove a primeira linha do datatable (criada no momento de preparacao com NA's) rownames(data_to_return) = NULL # Recalcula o numero das linhas depois da remocao da primeira linha pontos = data.frame(data_to_return$longitude, data_to_return$latitude) # Separa os pontos para plotagem no mapa if (sum(complete.cases(pontos)) > 0) { # Se existir observacoes georreferenciadas library(maps) # Carrega a biblioteca para plotagem do mapa map("world", col="gray35") # Define o mapa a ser exibido map.axes(bty="o", col="gray35", xaxt="n", yaxt="n") # Define as bordas do plot, a cor da linha do mapa e remove os indices abline(h = 0, lty = 2, col="gray66") # Adiciona a linha do equador points(pontos, col='red', pch=4, cex=0.3) # Insere os pontos no mapa } else { warning ("Não encontramos observações com dados georreferenciados!") # Mensagem de aviso se os dados retornados nao possuirem pontos georreferenciados. Nesse caso o mapa nao e construido, so o data.frame e retornado } return (data_to_return) # Retorna as observacoes encontradas em um data.frama } Abaixo, o help da função. inaturalist package:unknown R Documentation Description: A função realiza a busca de observações de espécimes registrados na base de dados do portal iNaturalist (http://www.inaturalist.org) através da sua API pública (http://api.inatualist.org). A busca é realizada de acordo com os parâmetros passados para a função. Nenhum parâmetro é obrigatório, porém é necessário informar pelo menos um (com exceção de 'maxresults'). As observações encontradas são retornada em um data.frame e plotadas em um mapa. A função requer os pacotes "jsonlite" e "maps" instalados. Usage: inaturalist(taxon_name, place, captive, geo, observed_on, quality_grade, date, circle, maxresults = 500) Arguments: taxon_name: um objeto do tipo "character" que informa o táxon dos espécimes que se deseja buscar; place: um objeto do tipo "character" que informa a localização a que as observações devem se restringir. O iNaturalist possui um sistema de cadastro de locais próprios e não há garantia que o parâmetro informado indique um local cadastrado na base de dados. O usuário é notificado se não for encontrados locais que casem com o parâmetro; captive: um objeto do tipo "logical" que indica se a busca deve se restringir às observações de espécimes em cativeiros ou não. Por padrão, todos as opções são retornadas. Informe TRUE para observações de espécimes cativos e FALSE para espécimes não cativos; geo: um objeto do tipo "logical" que indica se a busca deve se restringir às observações com dados de longitude e latitude. Informe TRUE para observações georreferenciadas e FALSE para observações não georreferenciadas; observed_on: um objeto do tipo "Date" que indica se a busca deve se restringir às observações datadas na data informada. Os valores de DIA, MÊS e ANO do objeto serão utilizados para a filtragem; quality_grade: um objeto do tipo "character" que indica se a busca deve se restringir às observações com o nível de qualidade informado. As opções possíveis são: casual, needs_id e research. Para saber o que significa cada um desses níveis, visite http://www.inaturalist.org ; Quando esse parâmetro não é informado, todas as opções são retornadas; date: um objeto do tipo "list" que indica se a busca deve se restringir às observações datadas do dia, mês ou ano informados. Espera-se que a lista possua no máxima 3 itens e que sejam: 'day' para indicar o dia, 'month' para indicar o mês e 'year' para indicar o ano. Pode ser informado apenas um dos itens, dois ou todos; circle: um objeto do tipo "list" que indica se a busca deve se restringir às observações registradas dentro da área definida pelos valores de latitude, longitude e raio indicados. Espera-se que a lista possua obrigatoriamente 3 itens: 'lat' para indicar a latitude do ponto central, 'lng' para indicar a longitude do ponto central e 'radius' para indicar o raio; maxresults: um objeto do tipo "numeric" que indica o limite de observações que devem ser retornadas. O valor padrão é de 500. Details: Parâmetros incorretos provocam mensagens de erro e parada na execução da função. Os dados retornados podem conter NA's. ATENÇÃO: o retorno de um grande número de observações provoca demora na execução da função. Value: Um data.frame com todas as observações obtidas na base de dados remota. As colunas retornadas são: id, observed_on, captive, quality_grade, latitude, longitude, taxon_name, place_guess e url; Um mapa com todos os pontos retornados que possuem informações de latitude e longitude. Warning: Caso as observações encontradas não possuem dados de latitude e longitude, apenas o data.frame é retornado; Se a string informada no parâmetro 'local' não casar com nenhum local cadastrado na base de dados, a busca não considerará o argumento. References: https://www.inaturalist.org/pages/about Author(s): Jailson Nunes Leocadio E-mail: jailsonleocadio@gmail.com Examples: inaturalist("Pitangus sulphuratus", place="são paulo", maxresults=50) inaturalist(circle=list(lat=36.3853522803, lng=-95.9713221258, radius=1000), maxresults=500) inaturalist(captive=FALSE, geo=TRUE, quality_grade="research", date=list(year=2018), maxresults=50) inaturalist(observed_on=as.Date("27/05/18", format="%d/%m/%y"), maxresults=25)