Estratégias para explorar variáveis ambientais e backgrounds

No exercício anterior exploramos como as variáveis se relacionam ao longo de espaços geográficos distintos. Para isso, amostramos valores de variáveis ambientais a partir de pontos aleatórios baseada na extensão das variáveis ambientais.

Uma das estratégias disponíveis para amostrar o ambiente onde uma espécie vive é capturar as áreas onde a espécie teria acesso. Deste modo, evitamos a comparação com ranges ambientais muito distantes e nos focamos nas redondezas dos pontos onde observamos os nossos pontos de amostragem.

Estratégia 1: amostrando ao redor

Uma forma bastante simples é criar circulos (ou buffers) de "determinado tamanho" ao redor dos pontos de amostragem e utilizar sua área para extrair múltiplos pontos. Para isso precisaremos:

  1. Pontos de ocorrência
  2. Criar círculos ao redor
  3. Criar pontos aleatórios dentro dos círculos
  4. extrair valores a partir desses pontos

Mãos a obra..

* Carregando os pacotes necessários:

#0.carregando pacotes necessários
packages<-c("raster", "dismo")
for (x in packages){
  if(require(x, character.only = T)){
    cat(paste("Biblioteca", x, "carregada com sucesso!", "\n"))
  } else{
    install.packages("raster")
    install.packages("dismo")
  }  
}
## Loading required package: raster
## Loading required package: sp
## Biblioteca raster carregada com sucesso!
## Loading required package: dismo
## Biblioteca dismo carregada com sucesso!

* Importando dados necessários:

Primeiro importaremos as ocorrências e visualizaremos o início da tabela.

#carregando os dados de ocorrência
eg.ann<-read.csv("../../Aula 4 - Load and Data Cleaning/input.coordinates.txt", sep = "\t")
#checando dados
head(eg.ann)
##              species decimalLongitude decimalLatitude
## 1 Euglossa annectans         -45.8464       -23.53220
## 2 Euglossa annectans         -45.4264       -23.83110
## 3 Euglossa annectans         -46.1883       -23.52280
## 4 Euglossa annectans         -46.6361       -23.54750
## 5 Euglossa annectans         -43.9642       -19.90174
## 6 Euglossa annectans         -46.8842       -23.18640

Agora, importaremos as vairáveis e plotaremos para checar se foram carregadas corretamente:

#dados climáticos
#bio1
temperatura<-raster("/media/wilson/MIDGARD/Documents/AARE - MNE/worldclim_layers/wc2.1_10m_bio/wc2.1_10m_bio_1.tif")
#bio5:
precipitacao<-raster("/media/wilson/MIDGARD/Documents/AARE - MNE/worldclim_layers/wc2.1_10m_bio/wc2.1_10m_bio_12.tif")

#juntando ambas em um único objeto
clima<-stack(temperatura, precipitacao)

#Plotando para ver se está tudo ok...
plot(clima$wc2.1_10m_bio_1, col = hcl.colors(100, palette = "RdYlBu", rev = T), main="Temperatura em °C")+
  points(eg.ann[,2:3])

## integer(0)
plot(clima$wc2.1_10m_bio_12, col = rainbow(50, start=0.5, end=0.9), main = "Precipitação em mm")+
  points(eg.ann[,2:3])

## integer(0)

Para fins de comparação, vamos extrair os dados da extenção da América do Sul, assim como fizemos no exercício anterior.

AS<-extract(clima, randomPoints(mask = clima, n = 1000, ext = extent(-82, -35, -54, 13)))

* Criando círculos para importar os dados…

#Primeiro criamos um dataframe vazio com o mesmo número de pontos que temos de entrada
spdf<-data.frame("id" = 1:nrow(eg.ann))

#Em seguida, transformamos os pontos lat e long da entrada em um dataframe de objetos espaciais
#Aqui utilizamos a função SpatialPointsDataFrame do pacote sp para criar esses pontos dizendo que
#o objeto spdf será a saída com o argumento `data`
spdf<-SpatialPointsDataFrame(eg.ann[,2:3], data = spdf)

#veja como ele se parece
spdf
## class       : SpatialPointsDataFrame 
## features    : 10 
## extent      : -56.48747, -43.27721, -23.8311, -19.90174  (xmin, xmax, ymin, ymax)
## crs         : NA 
## variables   : 1
## names       : id 
## min values  :  1 
## max values  : 10

Observe que o objeto spdf é uma classe complexa. Assim como um raster, ele possui atributos de um objeto georreferenciado do tipo shapefile. Note que há 10 ‘features’ ou ‘feições’ nesse objeto, as quais recebem os atributos que criamos no dataframe, no caso o ‘id’ que demos ao criar o dataframe vazio. Note ainda que o campo crs está vazio, e como ele quarda informações da projeção, precisamos arrumari isso.

Abrindo parênteses
Dica: você pode criar quantos campos quiser para cada uma das ocorrências anotando características que ache relevante, como localidade onde amostrou, temperatura ou horário da amostragem… Basta, na hora de criar o dataframe “vazio” adicionar um campo a mais com informações para cada registro que tiver", o resultado seria uma tabela de atributos, como aquela do QGIS.

Para corrigir a projeção, vamos usar a camada raster que importamos e copiar a projeção de lá com o seguinte comando:

#agora colocamos o dataset de pontos na mesma projeção que as camadas que criamos
crs(spdf)<-crs(clima)
#para visualizar o crs da nossa camada
crs(spdf)
## CRS arguments: +proj=longlat +datum=WGS84 +no_defs

Note que temos uma projeção longlat e que o datum agora é o WGS84.

Caso os seus dados de entrada estejam anotados em outra projeção que não a WGS84, será necessário reprojetar. Veja o help ?raster.

Enfim, vamos gerar os círculos. Utilizaremos uma função do pacote dismo chamada circle. Essa função foi desenhada para outro propósito, mas vai servir bem aqui. Precisamos apenas passar um conjunto de pontos longlat para ela e dizer o tamamnho do diâmetro que queremos o círculo em m2:

#criamos um objeto chamado `circle` para abrigar os circulos com 50km de diâmetro
#que serão nossos bufferes ao entorno dos pontos de coleta.
#o parâmetro dissolve = F diz para sobrepor os círculos. Facilita para visualizar a sobreposição.
circle <- circles(spdf, d=50000, lonlat=T, dissolve=F)
## Loading required namespace: rgeos
#podemos plotá-los
plot(circle)

Resta agora extrair os valores de dentro dos círculos:

#seta um número fixo para o início da busca aleatória, assim sempre teremos os mesmos pontos.
set.seed(1234)
# cria pontos aleatórios dentro dos círculos do objeto circleBg
#note que os polígonos estão no slote `polygon` do objeto circle. 
#Depois dizemos que queremos 100 pontos no total e que será tipo aleatório, com 1km de distância entre eles (iter=1000).
circleBg = spsample(circle@polygons, 100, type='random', iter=1000)
## Warning in proj4string(obj): CRS object has comment, which is lost in output
#plotando para ver se tudo correu bem
plot(circleBg)

Veja que o plot formado apresenta vários pontos nas regiões dos círculos.

Olhando o mesmo no mapa

#dizendo os limites do plot
limits <- c(-60, -40, -30, -19)
#plontando a temperatura, os círculos e os pontos
plot(temperatura, xlim=limits[1:2], ylim=limits[3:4])+
plot(circle, add=T)+
  points(circleBg, pch=3)+
  points(eg.ann[,2:3], col="blue", pch=20)

## integer(0)

A imagem resultante deve apresentar o raster de temperatura, com os círculos da área de buffer, mais os pontos aleatórios em forma de cruz e os pontos azuis como as localidades onde de fato observamos nossa espécie.

Agora, nos resta extrair os valores a partir dos pontos aleatórios (“cruz”) que criamos.

#criamos um objeto chamado circvalues para abrigar os dados extraídos com a função extract do pacote raster.
circvalues<-extract(clima, circleBg)

#vamos olhar o objeto de saída que resultou
head(circvalues)
##      wc2.1_10m_bio_1 wc2.1_10m_bio_12
## [1,]        22.09769             1169
## [2,]        18.05770             1795
## [3,]              NA               NA
## [4,]        23.33420             1494
## [5,]        21.06733             1419
## [6,]        22.70186             1563

Note que temos alguns NA resultante da tentativa de amostragem em região sem dados climáticos, como no mar. Precisamos removê-los! Para tantos vamos utilizar a função na.exclude, savando o resultado no objeto egval.

egval<- na.exclude(circvalues)

head(egval)
##      wc2.1_10m_bio_1 wc2.1_10m_bio_12
## [1,]        22.09769             1169
## [2,]        18.05770             1795
## [3,]        23.33420             1494
## [4,]        21.06733             1419
## [5,]        22.70186             1563
## [6,]        23.47592             1488

Para fins de comparação, vamos extrair também valores apenas dos dados de ocorrência

eginput.val<-extract(clima, eg.ann[,2:3])
head(eginput.val)
##      wc2.1_10m_bio_1 wc2.1_10m_bio_12
## [1,]        17.49352             1909
## [2,]        20.77830             2266
## [3,]        18.14023             1694
## [4,]        18.87724             1505
## [5,]        20.74716             1527
## [6,]        18.21002             1352

Agora vamos plotar tudo isso para comparar a relação entre os conjuntos de dados extraídos:

plot(AS, col= rgb(0,0,0, 1), xlim = c(14, 24), ylim=c(1000, 3500), pch=3, 
     
     xlab="Temperatura", ylab="Precipitação" )+
  
  lines(AS[c(chull(AS), chull(AS)[1]), ], col="blue", type="l")+
  
  polygon(egval[c(chull(egval), chull(egval)[1]),], col=rgb(0,0,1, 0.25), border = rgb(0,0,1, 0.8))+
  
  points(egval, col= rgb(0,0,1, 0.8), pch=19)+
  points(eginput.val, col= rgb(1,0,0, 0.8), pch=19)

## integer(0)

Veja que há uma relação interessante entre os os pontos de ocorrência (em vermelho), os pontos amostrados pelos circulos (em azul) e os pontos amostrados por extensão (cruzes negras). Os três conjuntos mostram aspectos diferentes do ambiente. Contudo, os pontos amostrados pela estratégia de círculos é com certeza proveniente de regiões geográficas acessíveis, enquanto os pontos representados pelas cruzes podem ou não ser.

Estratégia 2: Amostrando de limítes geográficos:

É bastante fácil imaginar que podemos ter um polígono ou um raster que represente uma área de interesse onde achamos que nossa espécie deve estar restrita.

Há com certeza, várias formas de obter um polígono ou um raster que delimite nossa área, mas vamos pensar que gostaríamos de ver o ambiente no estado do Rio de Janeiro e temos um shapefile para isso. Aqui iremos utilizar a função read_state do pacote geobr para obter esse shapefile.

Abrindo parêntese Vale uma nota sobre o pacote geobr. Este pacote tem várias fontes de dados disponibilizadas pelo IBGE em alta e baixa resoluções. Para mais detalhes, visite o site do projeto ou instale o pacote e veja o help: ?geobr.

#Para instalar o pacote, rode a linha comentada a baixo
#install.packages("geobr")

#para carregar o pacote:
library("geobr")
## Loading required namespace: sf
#baixando o shape do estado
rj<-read_state(code_state = "RJ", year = 2018)
## Using year 2018
## 
Downloading: 770 B        
Downloading: 1.2 kB         
Downloading: 100 kB
#pedindo ao R para mostrar o objeto no terminal
rj
## Simple feature collection with 1 feature and 5 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -44.88932 ymin: -23.36893 xmax: -40.95794 ymax: -20.76323
## Geodetic CRS:  SIRGAS 2000
##   code_state abbrev_state     name_state code_region name_region
## 1         33           RJ Rio De Janeiro           3     Sudeste
##                             geom
## 1 MULTIPOLYGON (((-41.7872 -2...
#plotando apenas a geometria do estado
plot(rj$geom)

Há contudo um “problema”. O datum do shape é o Geodetic Reference System 1980 anotado como “GRS80”, como estamos utilizando “WGS84”, precisaremos reprojetar. Para isso utilizaremos o pacote sf (simple feature) para tratar o polígono.

#install.packages("sf")
#install.packages("lwgeom", INSTALL_opts = "--no-lock")
library("sf")
## Linking to GEOS 3.8.0, GDAL 3.0.4, PROJ 6.3.1
library("lwgeom")
## Linking to liblwgeom 3.0.0beta1 r16016, GEOS 3.8.0, PROJ 6.3.1
#vamos primeiro capturar o crs das variáveis que estamos utilizando através do objeto clima que criamos lá no começo.
target_crs<-crs(clima)

#agora utilizaremos a função st_transforme, informando o polígono e o crs que desejamos.
rj<-st_transform(rj, crs = st_crs(target_crs))

#para conferir, vamos plotar tudo.
plot(temperatura, xlim=c(-45,-40), ylim=c(-24,-20.5))+
  plot(rj$geom, add=T)

## integer(0)

Com nosso polígono perfeitamente alinhado em mãos, fica fácil extrair os dados. Vamos primeiro criar os pontos aleátorios lá dentro:

#seta um número fixo para o início da busca aleatória, assim sempre teremos os mesmos pontos.
set.seed(1234)
# cria pontos aleatórios dentro dos círculos do objeto circleBg
#note que os polígonos estão no slote `polygon` do objeto circle. 
#Depois dizemos que queremos 100 pontos no total e que será tipo aleatório, com 1km de distância entre eles (iter=1000).
rjbg <- st_sample(rj, 1000)
## although coordinates are longitude/latitude, st_intersects assumes that they are planar
## although coordinates are longitude/latitude, st_intersects assumes that they are planar
## although coordinates are longitude/latitude, st_intersects assumes that they are planar
## although coordinates are longitude/latitude, st_intersects assumes that they are planar
#plotando para ver se tudo correu bem
plot(rjbg, pch=3)

Uma vez que temos os pontos, podemos agora extrair os dados exatamente como fizemos anteriormente. Contudo, teremos que antes converter o nossos pontos extraídos do estado do Rio de Janeiro para um tipo que o pacote raster compreenda, que é o tipo spatial do pacote sp. Faremos isso em uma linha.

#Uma vez que objetos sf são listas, precisamos convertêlos para um tipo espacial via pacote `sp`.
#fazemos isso com a função `as()`. Paramais detalhes, veja ?as.
rjbg.as.sp<-as(rjbg, "Spatial")

Veja rapidamente a diferença entre os dois tipos

#compare a estrutura dos dois tipos
cat("\n Esse é o objeto tipo 'simple feature' do pacote `sf`: \n \n")
## 
##  Esse é o objeto tipo 'simple feature' do pacote `sf`: 
## 
str(rjbg)
## sfc_POINT of length 1000; first list element:  'XY' Named num [1:2] -41.5 -21.9
##  - attr(*, "names")= chr [1:2] "lon" "lat"
head(rjbg)
## Geometry set for 6 features 
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: -42.27069 ymin: -22.71853 xmax: -41.25895 ymax: -21.6757
## CRS:           +proj=longlat +datum=WGS84 +no_defs
## First 5 geometries:
## POINT (-41.50474 -21.91981)
## POINT (-42.27069 -22.71853)
## POINT (-42.16255 -22.48329)
## POINT (-41.25895 -21.70602)
## POINT (-41.59759 -21.6757)
cat("\n \n Esse é o objeto tipo 'spatial' do pacote `sp`: \n \n")
## 
##  
##  Esse é o objeto tipo 'spatial' do pacote `sp`: 
## 
str(rjbg.as.sp)
## Formal class 'SpatialPoints' [package "sp"] with 3 slots
##   ..@ coords     : num [1:1000, 1:2] -41.5 -42.3 -42.2 -41.3 -41.6 ...
##   .. ..- attr(*, "dimnames")=List of 2
##   .. .. ..$ : NULL
##   .. .. ..$ : chr [1:2] "lon" "lat"
##   ..@ bbox       : num [1:2, 1:2] -44.8 -23.3 -41 -20.8
##   .. ..- attr(*, "dimnames")=List of 2
##   .. .. ..$ : chr [1:2] "lon" "lat"
##   .. .. ..$ : chr [1:2] "min" "max"
##   ..@ proj4string:Formal class 'CRS' [package "sp"] with 1 slot
##   .. .. ..@ projargs: chr "+proj=longlat +datum=WGS84 +no_defs"
head(rjbg.as.sp)
## class       : SpatialPoints 
## features    : 1 
## extent      : -41.50474, -41.50474, -21.91981, -21.91981  (xmin, xmax, ymin, ymax)
## crs         : +proj=longlat +datum=WGS84 +no_defs

Note que rjbg do tipo sf é um conjunto de features, enquanto o rjbg.as.sp é uma única feature, além disso rjbg.as.sp possui 3 slots (coords, bbox, proj4string), e o slot coords pode ser mais facilmente acessado pelo pacote raster.

Tecnicalidades à parte, agora sim podemos extrair os dados climáticos.

#criando um objeto para abrigar os dados extraídos
rjvalues<-extract(clima, rjbg.as.sp)
cat("Veja os dados climáticos extraídos: \n \n")
## Veja os dados climáticos extraídos: 
## 
head(rjvalues)
##      wc2.1_10m_bio_1 wc2.1_10m_bio_12
## [1,]        23.48619             1110
## [2,]        22.92298             1152
## [3,]        22.22953             1202
## [4,]        23.92244             1057
## [5,]        22.86900             1090
## [6,]        23.10254             1236

Vamos plotar e comparar

plot(AS, col= rgb(0,0,0, 1), xlim = c(14, 24), ylim=c(1000, 3500), pch=3, xlab="Temperatura", ylab="Precipitação" )+
  lines(AS[c(chull(AS), chull(AS)[1]), ], col="blue", type="l")+
  polygon(rjvalues[c(chull(rjvalues), chull(rjvalues)[1]),], col=rgb(0,0,1, 0.25), border = rgb(0,0,1, 0.8))+
  points(rjvalues, col= rgb(0,0,1, 0.8), pch=19)+
  polygon(eginput.val[c(chull(eginput.val), chull(eginput.val)[1]),], col=rgb(1,0,0, 0.25), border = rgb(1,0,0, 0.8))+
  points(eginput.val, col= rgb(1,0,0, 0.8), pch=19)

## integer(0)

Note como os dados extraídos do estado do Rio de Janeiro (pontos azuis) se comportam em relação ao geral da América do Sul (cruzes negras) e como o a nossa espécie tem boa sobreposição com o estado do Rio de Janeiro.

Consireções finais:

Extrair dados de camadas ambientais é um passo primordial para modelage. Vimos nesse breve exercício, duas maneiras de proceder essa extração. A primeira amostrando dados a partir de buffers e a segunda a partir de um polígono limite. A escolha do procedimento correto vai depender do nosso objetivo, das particularidades do organismo e da questão por trás da análise.

Espero que o exercício tenha sido útil. Bons estudos.


Prof. Dr. Wilson Frantine-Silva
Pesquisador PNPD Universidade Estadual do Norte Fluminense Darcy Ribeiro - LCA, PPGERN.