Ferramentas do usuário

Ferramentas do site


lidar:projetos:rcode_invconve

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Inventário Convencional na Fazenda Modelo ~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# Autor: Luiz Carlos Estraviz Rodriguez
#        Departamento de Ciências Florestais
#        ESALQ/USP - 16/Jan/2024
#
# Inventário florestal da Fazenda Modelo
#   - download dos dados mantidos em um repositório github público
#      - shape files dos talhões florestais
#      - LiDAR multitemporal (2013 e 2014)
#   - sugestão de pasta para armazenamento local:
#        C:/LiDAR/PRJ_Modelo/
#
# Linguagem de programação:
#       R (v 4.3)
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rm(list=ls(all=TRUE))                                   # Limpa memória
gc()


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 1. Leitura e organização de dados
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ----
if(!require(tidyverse))  # Para melhor manipulação de dados e funções
  install.packages("tidyverse")
library(tidyverse)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Download do shape da Fazenda Modelo (2 layers: talhoes e parcelas)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gitOnde <- "https://github.com/FlorestaR/dados/blob/main/5_LIDARF/Modelo/SHAPES"
gitNome <- "fazmodelo.zip"
gitArqv <- file.path(gitOnde, gitNome) %>% paste0("?raw=true")

tmpd <- tempdir(check = TRUE)                    # diretório temporário
zipf <- file.path(tmpd, "shapes.zip")              # arquivo temporário

options(timeout=1000) # Reset timeout oferecendo mais tempo de download
if(!file.exists(zipf))  # garante download de dados binários (wb)
  download.file(gitArqv, mode="wb", destfile = zipf) 

unzip(zipf, exdir = tmpd)     # shape é unziped no diretório temporário
unlink(zipf)                                  # deleta o arquivo zipado


if(!require(sf))                           # Para manipulação de shapes
  install.packages("sf")
library(sf)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Lê atributos dos talhoes
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
shpArq <- paste0(tmpd, "/Modelo_talhoes.shp")       # shape com talhões
talhoesComGeo <- read_sf(shpArq)                    # completo com geom
talhoesSemGeo <- tibble(sf::st_drop_geometry(talhoesComGeo))  # s/ geom

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Lê atributos das parcelas
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
shpArq <- paste0(tmpd, "/Modelo_parcelas.shp")     # shape com parcelas
parcelasComGeo <- read_sf(shpArq)               # completo com geom
parcelasSemGeo <- tibble(sf::st_drop_geometry(parcelasComGeo)) # s/ geom

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Cria coluna IDINV na tabela "talhões", extraída da tabela "parcelas"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
talhoes <-
  parcelasSemGeo %>%
  group_by(SUBTALHAO) %>%
  summarise(IDINV = unique(IDINV)) %>%
  left_join(talhoesSemGeo) %>%
  select("SUBTALHAO", "IDINV", "AREA") %>%
  arrange(SUBTALHAO) %>% as.data.frame

talhoesComGeo <- inner_join(talhoesComGeo, talhoes, by="SUBTALHAO") %>%
  select("OBJECTID", "SUBTALHAO", "IDINV", "geometry")

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Reorganiza colunas da tabela "parcelas"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
parcelas <-
  parcelasSemGeo %>%
  select(SUBTALHAO, CHAVE2, DATAREALIZ, IDINV, AREAPARCEL, MHDOM, VTCC) %>% 
  arrange(SUBTALHAO) %>% as.data.frame

# Cria diretórios e pastas para onde alguns dados serão copiados
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
prjNome <- 'PRJ_Modelo'
dirNome <- paste0('C:/LiDAR/', prjNome)
dir.create(dirNome, showWarnings = F)

# Salva e imprime dados reorganizados
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dirNome <- paste0(dirNome, '/DADOS/')
dir.create(dirNome, showWarnings = F)

# Leitura da mais atual versão rio para facilmente ler e salvar Excel
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if (!require("remotes")){install.packages("remotes")}
if (!require(rio))      {remotes::install_github("gesistsa/rio")}
library(rio)
# Salva dados em planilha Excel
arqNome <- paste0(dirNome, prjNome, '.xlsx')
export(list(talhoes = talhoes,
            parcelas  = parcelas),
       file = arqNome)

# Mostra tabelas no Viewer
# Use o "Export" do Viewer para salvar em diferentes formatos
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if(!require(kableExtra))
  install.packages("kableExtra")
library(kableExtra)
# Mostra talhões
AreaTotal <- talhoes$AREA %>% sum
NotaDeRodape <- paste0(": ", AreaTotal)
talhoes %>%
  kbl(caption = "Talhões da Fazenda Modelo", align = "r") %>%
  kable_classic(full_width = F) %>%
  footnote(general = NotaDeRodape, 
           general_title = "Área total",
           footnote_as_chunk = T)
# Mostra parcelas
parcelas %>%
  kbl(caption = "Parcelas da Fazenda Modelo", align = "r") %>%
  kable_classic(full_width = F)

# Cria diretório para salvar mapas, figuras e gráficos
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dirNome <- paste0('C:/LiDAR/', prjNome)
dir.create(dirNome, showWarnings = F)
dirNome <- paste0(dirNome, '/GRAPH/')
dir.create(dirNome, showWarnings = F)

# Cria mapa dos talhões com localização das parcelas (EPSG: 31983)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
arqNome <- paste0(dirNome, prjNome, '.png')
png(arqNome, 30, 20, 'cm', res = 200)                # abre "impressão"

ggplot() +        #         plot dos talhões e parcelas (cor por idade)
  geom_sf(data = talhoesComGeo, colour = "black", fill="white") +
  geom_sf(data = parcelasComGeo, aes(fill = factor(IDINV))) +
  scale_fill_discrete(name = "Idade") +
  guides(fill = guide_legend(reverse=F)) +
  coord_sf(datum=st_crs(31983)) +        # Especifica sistema de coord.
  scale_y_continuous(breaks = seq(from=7356500,to=7359000, by=200)) +
  scale_x_continuous(breaks = seq(from=206200, to=207600,  by=200))

dev.off()                                # fecha "impressão" do aquivo

# ----


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 2. Amostragem Casual Simples (ACS)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ----

# Função para cálculo do Intervalo de Confiança
calcCI = function(err, n, alpha=.05){
  return(
    qt(1 - alpha/2, n-1) * err #/ sqrt(n)
  )
}

# Função para cálculo dos parâmetros de estimação e inferência
calcPars = function(df, N, alpha=.05){
  means = apply(df, 2, mean)
  vars   = apply(df, 2, function(y){
    (var(y) / length(y)) * ((N - length(y)) / N)
  })
  err = sqrt(vars)
  cis = calcCI(err, nrow(df), alpha)
  err_pc = 100*cis/means
  out = rbind(means, vars, err, cis, err_pc, nrow(df)) %>% as.data.frame
  row.names(out) = c('media', 'var', 'dp', 'ic', 'erro', 'n')
  names(out) = names(df)
  return(out)
}

# Cria lista com variáveis de interesse
variaveisDeInteresse <- c("MHDOM", "VTCC")
# Cria dataframe com valores observados das variáveis de interesse
df <- parcelas %>% select(all_of(variaveisDeInteresse))

# Calcula o número máximo de unidades amostrais possíveis (N)
parcelaAreaMedia <- mean(parcelas$AREAPARCEL) / 10000          # por ha
N_ACS <- round(AreaTotal / parcelaAreaMedia , 0)    # N

# Resultados do inventário (estimação + inferência)
#             pelo método Amostragem Casual Simples (ACS)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
paramEstatisticosACS  <- df %>% calcPars(N_ACS)

# Função para cálculo da intensidade amostral recomendada
# (ha por parcela) da ACS que garante ~10% de erro (altere se desejar)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
erro <- 10
tamanhoIdealACS = function(y, N, errDesired=erro/100, alpha=erro/100){
  B  = errDesired * mean(y)
  qt = qt(1 - alpha/2, length(y)-1)
  n  = N*var(y)*qt^2 / (N * B^2 + qt^2 * var(y))
  return(n)
}
# Intensidade amostral usada
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
IAS <- paste0(" usada: 1 parc/",
              round(AreaTotal/paramEstatisticosACS$VTCC[6],0), " ha.")

# Intensidade amostral que seria necessária para ACS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
IA <- round(AreaTotal/tamanhoIdealACS(df$VTCC,N_ACS),0)
erroTexto <- as.character(erro)
IAI <- paste0("\n Necessárias p/ erro de ", erroTexto, 
              "%: 1 parc/", IA, " ha.")

# Mostra resultados da ACS no Viewer
# Use o "Export" do Viewer para salvar em diferentes formatos
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NotaDeRodape <- paste0(IAS, IAI)
paramEstatisticosACS %>%
  kbl(caption = "Amostragem Casual Simples (ACS)", align = "r") %>%
  kable_classic(full_width = F) %>%
  footnote(general = NotaDeRodape,
           general_title = "Intensidade amostral",
           footnote_as_chunk = T)
# ----


# *********************************************************************
# 3. Amostragem Casual Estratificada (ACE)
# **************************************************************** ----
# Escolhe variável de estratificação
grupos = c("IDINV")

# Box-plot da variável de interesse por nível de estratificação
par(mfrow=c( length(grupos), length(variaveisDeInteresse) ))
attach(parcelas)
for(i in grupos){
  for(j in variaveisDeInteresse){
    plot( get(i) %>% factor , get(j), main = j, sub=i)
  }
}
detach(parcelas)

# Estimativa da população para amostragem estratificada
popFromStrata = function(factorStrataList){
  popEstimates = foreach(i = factorStrataList, .combine = 'c') %do% {
    gpMeans = lapply(i, function(x) x[1,,drop=F]) %>% do.call(what = rbind)
    gpVars  = lapply(i, function(x) x[2,,drop=F]) %>% do.call(what = rbind)
    cols = 1:(ncol(gpMeans)-4)
    popMean   = apply(gpMeans[,cols], 2, 
                      function(x) sum(x*gpMeans$N) ) /N_ACS
    popVar    = apply(gpVars[,cols], 2, 
                      function(x) sum( x * (gpVars$N/N_ACS)^2 ) )
    popStdErr = sqrt(popVar)
    popCI     = calcCI(popStdErr, sum(gpMeans$n))
    popPars = data.frame(
      media = popMean,
      var   = popVar,
      dp    = popStdErr,
      ic    = popCI,
      erro  = 100 * popCI / popMean,
      n     = sum( sapply(i, function(x) mean(x$n)) )
    )
    return(list(popPars))
  }
  names(popEstimates) = names(factorStrataList)
  return(popEstimates)
}


if(!require(foreach))                         # Para loops inteligentes
  install.packages("foreach")
library(foreach)

# Cria vetor de (True ou False) para marcar dados que estejam dentro
#      do intervalo de idades de interesse (entre 2 e 6)
rightAges = talhoes$IDINV > 2 & talhoes$IDINV < 6

# Resultados do inventário (estimação + inferência)
#             pelo método Amostragem Casual Estratificada (ACE)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
paramEstatisticosACE = foreach(
  grp = grupos, .combine = 'rbind') %:% 
  foreach(
    niv = parcelas[,grp] %>% unique %>% 
      as.character %>% sort, .combine = 'rbind'
  ) %do% {
    inGroup = talhoes[,grp] == niv
    popSize = round(
      sum(talhoes[rightAges & !is.na(inGroup) & inGroup,]$AREA) / 
        parcelaAreaMedia)
    inGroup = parcelas[,grp] == niv
    tempPars = parcelas[inGroup, variaveisDeInteresse, drop=F]
    inventory = calcPars(tempPars, popSize)
    inventory$grupo = grp
    inventory$nivel = niv
    inventory$n     = nrow(tempPars)
    inventory$N     = popSize
    return(inventory)
  }
paramEstatisticosACE %<>% base::split(f=paramEstatisticosACE$grupo) %>%
  lapply(function(x) split(x, x$nivel))
globalparamEstatisticosACE = popFromStrata(paramEstatisticosACE)

# Função para cálculo da intensidade amostral recomendada
# (ha por parcela) da ACE que garante ~10% de erro (altere se desejar)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
erro <- 10
tamanhoIdealACE = function(y, g, Nh, errDesired=erro/100){
  vars = by(y, g, stats::var)
  Wh   = by(y, g, length) / length(y)
  Nh   = Nh[ names(Nh) %in% g ]
  B    = errDesired * mean(y)
  n    = sum( Nh^2 * vars / Wh ) / 
    ((B^2 * (sum(Nh)^2))/4 + sum(Nh * vars))
  return(n)
}

# Calcula o número possível de unidades amostrais (N) por estrato
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
parcPorEstrato <- round(                                           # Nh
  by(st_area(talhoesComGeo), talhoes$IDINV, sum) /
    (mean(parcelas$AREAPARCEL)), 0)
IA <- round(AreaTotal/tamanhoIdealACE(y = parcelas$VTCC,
                                      g = parcelas$IDINV, 
                                      Nh = parcPorEstrato), 0)

# Intensidade amostral usada por estrato
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
IAE <- round(by(st_area(talhoesComGeo)/10000, talhoes$IDINV, sum) /
               (parcelas %>% count(IDINV))[2])[1]
IAS <- paste0(" usada: 1 parc / ", as.character(IAE) , " ha.")

# Intensidade amostral que seria necessária para ACE
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
erroTexto <- as.character(erro)
IAI <- paste0("\n Necessárias p/ erro de ", erroTexto, 
              "%: 1 parc / ", IA, " ha.")

NotaDeRodape <- paste0(IAS, IAI)
globalparamEstatisticosACE[[grupos]] %>% t %>%  # transpõe o data frame
  kbl(caption = paste0("Amostragem Casual Estratificada (ACE)"),
      align = "r") %>%
  kable_classic(full_width = F) %>%
  footnote(general = NotaDeRodape,
           general_title = "Intensidade amostral",
           footnote_as_chunk = T)

# Gráfico de número de amostras necessárias por erro amostral aceitável
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
prjNome <- 'PRJ_Modelo'
dirNome <- paste0('C:/LiDAR/', prjNome)
dir.create(dirNome, showWarnings = F)
dirNome <- paste0(dirNome, '/GRAPH/')
dir.create(dirNome, showWarnings = F)
arqNome <- paste0(dirNome, prjNome, '_IntAmostral_ACSvsACE.png')

erro = 1:20
scsPlotn <- tamanhoIdealACS(parcelas$VTCC, N_ACS,
                            errDesired = erro/100)
cssPlotn <- tamanhoIdealACE(parcelas$VTCC,  parcelas$IDINV, 
                            parcPorEstrato, errDesired = erro/100)
# dsPlotn = dubleSamplePlotNumber(plotMetrics$PC_VOL_TOT,
#                                 plotMetrics$avgCrownHeight,
#                                 cropMetrics$avgCrownHeight, 300, erro/100)

png( arqNome, 30, 20, 'cm', res = 200)           # abre "impressão" PNG
# jpeg(arqNome, 30, 20, 'cm', res = 200)           # abre "impressão" JPG

plot( scsPlotn ~ erro, type='l', col='red', lwd=2, 
      ylim=c(0,200), ylab='Número de parcelas', xlab='Erro amostral (%)',
      axes=F)
lines(erro, cssPlotn, col='green', lwd=2)
# lines(erro, dsPlotn, col='blue', lwd=2)
box()
axis(1)
axis(2, at = c(25, 50, 75, 100, 125, 150, 175, 200))
abline(v=10, col='black', lwd=2, lty=2)
lines(c(0,15), rep(100, 2), col='black', lty=2, lwd=2)
# lines(c(0,5), rep(dsPlotn[5], 2), col='blue', lty=2, lwd=2)
# lines(c(0,5), rep(cssPlotn[5], 2), col='green', lty=2, lwd=2)
legend('topright', col=rainbow(3), lwd=2,
       legend = c('ACS', 'ACE'))
# legend = c('ACS', 'ACE', 'AD'))

dev.off()                                           # fecha "impressão"


# Salva resultados em planilha Excel
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
prjNome <- 'PRJ_Modelo'
dirNome <- paste0('C:/LiDAR/', prjNome)
dir.create(dirNome, showWarnings = F)
dirNome <- paste0(dirNome, '/RSLTS/')
dir.create(dirNome, showWarnings = F)

arqNome <- paste0(dirNome, prjNome, '_Results.xlsx')
export(list(ACS = paramEstatisticosACS,
            ACE37 = paramEstatisticosACE$IDINV$'3.7',
            ACE52 = paramEstatisticosACE$IDINV$'5.2'),
       file = arqNome)

# save.image(file='Modelo_inventario.RData')       # faz backup dos dados
# load('Modelo_inventario.RData')                       # recupera backup
#  ----

Referências sobre o pacote lidR para processamento de dados LiDAR:

e sobre os pacotes terra e sf para processamento de dados espaciais:

lidar/projetos/rcode_invconve.txt · Última modificação: 2024/03/23 20:17 por 127.0.0.1