lidar:projetos:rcode_invcomlidar
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Inventário com dados LiDAR na Fazenda Modelo ~~~~~~~~~~~~~~~~~~~~~~~~
#
# Autor: Luiz Carlos Estraviz Rodriguez
# Departamento de Ciências Florestais
# ESALQ/USP - 02/Dez/2023
#
# 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 pastas locais
# C:/LiDAR/PRJ_Modelo/NUVENS/ como pasta para os dados LidAR
# C:/GitRepo/PRJ_Modelo/ sendo GitRepo a pasta onde ficam os
# projetos sincronizados no github
#
# Linguagem de programação:
# R (v 4.3)
# RStudio (v. 2023.09.1 Build 494)
# Pacote lidR* (v 4.0.3 March 11th, 2023) Jean Romain Roussel
#
# Bibliografia complementar:
# https://github.com/r-lidar/lidR/wiki/Area-based-approach-from-A-to-Z
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rm(list=ls(all=TRUE)) # Limpa memória
gc()
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 1. Carrega pacotes, organiza pastas e define funções locais
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ----
if(!require(tidyverse)) # Para melhor manipulação de dados e funções
install.packages("tidyverse")
library(tidyverse)
if(!require(sf)) # Para manipulação de shapes e outros tipos
install.packages("sf") # de dados espacializados
library(sf)
if(!require(tidyterra)) # Package para processar mapas raster
install.packages("tidyterra")
library(tidyterra)
if(!require(terra)) # Package para processar mapas raster
install.packages("terra")
library(terra)
if(!require(stars)) # Package que permite rasterização
install.packages("stars")
library(stars)
if(!require(tools)) # Package para manipular nomes de aquivos
install.packages("tools")
library(tools)
if(!require(RColorBrewer)) # Package com mais paletas de cores
install.packages("RColorBrewer")
library(RColorBrewer)
if(!require(progress)) # Package com mais paletas de cores
install.packages("progress")
library(progress)
if(!require(reshape2)) # Usado na função que gera gráficos
install.packages("reshape2") # mais caprichados de correlação
library(reshape2)
if(!require(mapview)) # Package para incluir mapas de fundo
install.packages("mapview")
library(mapview)
# devtools::install_github("Jean-Romain/rlas", dependencies=TRUE)
# devtools::install_github("Jean-Romain/lidR")
if(!require(lidR)) # PARA MANPULAÇÃO DE DADOS LiDAR
install.packages("lidR") # Se preferir instalar a versão mais recente
library(lidR) # e tiver o package devtools instalado, rode
# alternativamente as duas linhas acima
if(!require(RCSF)) # Pacote complementar do lidR e necessário
install.packages("RCSF") # rodar a função classify_ground()
library(RCSF)
if(!require(future)) # Package que permite ao lidR rodar usando
install.packages("future") # processamento paralelo de dados
library(future)
cores <- as.integer(parallel::detectCores() - 1)
plan(multisession, workers = cores)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define pastas e local de trabalho
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
prjNam <- "PRJ_Modelo" # Nomeia o projeto
dirProjet <- str_c('C:/GitRepo/',prjNam) # Define diretório de trabalho
if (!dir.exists(dirProjet)) { # Cria diretório caso não exista
dir.create('C:/GitRepo/', showWarnings = F)
dir.create(dirProjet, showWarnings = F)
}
setwd(dirProjet) # Define pasta default de trabalho
datDir <- str_c('C:/LiDAR/', prjNam) # Define raiz da pasta de dados
if (!dir.exists(datDir)) { # Cria diretórios caso não existam
dir.create('C:/LiDAR/', showWarnings = F)
dir.create(datDir, showWarnings = F)
}
lidDir <- str_c(datDir, '/NUVENS/') # Cria sub-pasta p/ dados LiDAR
if (!dir.exists(lidDir)) {
dir.create(lidDir, showWarnings = F)
}
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Este bloco cria funções que permitem produzir gráficos esteticamente
# caprichados para apresentar os índices de correlação de Pearson
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Parâmetros dos gráficos
txt.size = 6
thm.size = (14/5) * txt.size
Tema <- theme(
axis.line = element_line(size = .5, colour = "black"),
axis.text.x = element_text(angle = 45, size = thm.size, vjust = 1, hjust = 1),
axis.title.y = element_blank(),
axis.text.y = element_text(angle = 45, size = thm.size),
axis.ticks = element_blank(),
legend.justification = c(1, 0),
legend.position = c(1, 0.5),
legend.text = element_text(size = 12, colour="black"),
legend.title = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
panel.grid.major = element_line(colour = "grey"),
title = element_text(size = thm.size, colour="black", face="bold"),
plot.background = element_rect(colour = "black", fill=NA, size=1)
)
# Função para reordenamento da cormat (correlation matrix)
reorder_cormat <- function(cormat){
# Use correlation between variables as distance
dd <- as.dist((1-cormat)/2)
hc <- hclust(dd)
cormat <-cormat[hc$order, hc$order]
}
# Função para definição do triângulo superior da cormat
get_upper_tri <- function(cormat){
cormat[lower.tri(cormat)]<- NA
return(cormat)
}
# Função para embelazamento da cormat
graphCormat <- function(cormat){
# Resets font size for correlation matrix graph
txt.size = 6
thm.size = (14/5) * txt.size
upper_tri <- get_upper_tri(cormat)
# Melt the correlation matrix
melted_cormat <- melt(upper_tri, na.rm = TRUE)
p <- ggplot(melted_cormat, aes(Var2, Var1, fill = value))+
geom_tile(color = "white")+
scale_fill_gradient2(low = "blue", high = "red", mid = "white",
midpoint = 0, limit = c(-1,1), space = "Lab",
name="Pearson\nCorrelation") +
coord_fixed() +
geom_text(aes(Var2, Var1, label = value), color = "black", size = 0.7*txt.size) +
theme(
axis.line = element_line(size = .5, colour = "black"),
axis.title.x = element_blank(),
axis.text.x = element_text(angle = 45, size = thm.size, vjust = 1, hjust = 1),
axis.title.y = element_blank(),
axis.text.y = element_text(angle = 45, size = thm.size),
axis.ticks = element_blank(),
legend.direction = "horizontal",
legend.justification = c(1, 0),
legend.position = c(0.6, 0.7),
legend.text = element_text(size = 12, colour="black"),
panel.border = element_blank(),
panel.background = element_blank(),
panel.grid.major = element_line(colour = "grey"),
title = element_text(size = thm.size, colour="black", face="bold"),
plot.background = element_rect(colour = "black", fill=NA, size=1)
) +
guides(fill = guide_colorbar(barwidth = 7, barheight = 1,
title.position = "top", title.hjust = 0.5))
return(p)
}
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Estimativas. e estatísticas de qualidade da inferência, resultantes
# da amostragem dupla (double sampling)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dsStats = function(y, x, xLarge, alpha=.05){
n = length(y)
beta = ( sum(y*x, na.rm=T) - ( sum(x, na.rm=T)*sum(y, na.rm=T) / n )) / ( sum(x^2, na.rm=T) - (sum(x, na.rm=T)^2 / n) )
rho = cor(y,x)
N = length(xLarge)
ydsr = mean(y, na.rm=T) + beta * ( mean(xLarge, na.rm=T) - mean(x, na.rm=T) )
vardsr = (var(y, na.rm=T)/n)*(1 - (rho^2)*(N-n)/N)
stderr = sqrt(vardsr)
ci = calcCI(stderr, n, alpha)
out = c(media = ydsr, var = vardsr, dp = stderr, ic = ci, erro = 100*ci/ydsr, n=n, rho=rho)
return(out)
}
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Cálculo do número ideal de unidades amostrais em double sampling, ou
# seja, da intensidade amostral necessária para gerar o erro admissível
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dsNumberOfPlots = function(y, x, xLarge, Cpg = 300,
errDesired = .05, alpha = .05){
rho = cor(x,y)
a = var(y) * (1 - rho^2)
b = var(y) * rho^2
B = errDesired * mean(y)
qt = qt(1 - alpha/2, length(y)-1)
nG = (sqrt( a*b*Cpg ) + b) / (B^2 / qt^2)
nP = (sqrt( a*b/Cpg ) + a) / (B^2 / qt^2)
return(nP)
}
# ----
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 2. Leitura de dados
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ----
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define o nome do arquivo com os dados espacializados disponíveis para
# a área de estudo, faz o download e salva esses dados na devida pasta
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gitOnde <-
"https://github.com/FlorestaR/dados/blob/main/5_LIDARF/Modelo/CLOUDS/"
gitNome <-c("L0004-C0005.laz", "L0004-C0006.laz",
"L0005-C0005.laz", "L0005-C0006.laz", # arquivos LiDAR
"L0006-C0005.laz", "L0006-C0006.laz")
anoData <- c("A13") # Lê apenas os dados LiDAR de 2013
# anoData <- c("A13", "A14") # Se quiser baixar os dois conjuntos
# Download dos tiles
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
options(timeout=1000) # Reset timeout oferecendo mais tempo de download
for (ano in anoData) { # guarda na sub-pasta de dados LiDAR por ano
dirLAZ <- str_c(lidDir, ano)
dir.create(dirLAZ, showWarnings = T)
for (nome in gitNome){ # acrescenta "?raw=true" no fim do URL
gitFile <- str_c(gitOnde, ano, "/", ano, nome, "?raw=true")
localFl <- str_c(dirLAZ, "/", nome)
if(!file.exists(localFl)) # garante download binário (wb)
download.file(gitFile, mode="wb", destfile = localFl)
}
}
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define o nome do arquivo com os dados espacializados disponíveis para
# a área de estudo, faz o download e salva esses dados na devida pasta
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gitOnde <-
"https://github.com/FlorestaR/dados/blob/main/5_LIDARF/Modelo/SHAPES"
gitNome <- "fazmodelo.zip" # shapes da fazenda modelo
gitArqv <- file.path(gitOnde, gitNome) %>% str_c("?raw=true")
tmpd <- tempdir(check = TRUE) # diretório temporário
zipf <- file.path(tmpd, "shapes.zip") # arquivo temporário
# Download e unzip do coonteúdo do arquivo zipado
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
options(timeout=1000)
if(!file.exists(zipf)) {
download.file(gitArqv, mode="wb", destfile = zipf)
}
shpDir <- str_c(dirProjet, '/SHAPES') # Define diretório para os shapes
if (!dir.exists(shpDir)) { # Cria diretório caso não exista
dir.create(shpDir, showWarnings = F)
}
unzip(zipf, exdir = shpDir) # unzipa shps e guarda na pasta de shapes
unlink(zipf) # deleta o arquivo zipado
# ----
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 3. Processamento da variável auxiliar LiDAR e estudo da correlação
# das métricas LiDAR com os parâmetros de interesse
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ----
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Leitura do catálogo de dados LiDAR (2013 ou 2014)
# Exibe conteúdo do catálogo e confere se os mapas de tiles são iguais
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dirLAZ <- str_c(lidDir, anoData[1]) # Define local das nuvens LiDAR
ctg_orig <- readLAScatalog(dirLAZ) # LEITURA DAS NUVENS DE PONTOS LiDAR
opt_select(ctg_orig) <- "xyzic" # Só atributos xyz intensid. e classif.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Lê shape e atributos dos talhoes
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
shpArq <- str_c(shpDir, "/Modelo_talhoes.shp") # shape de talhões
tal_shp <- st_read(shpArq)
st_is_valid(tal_shp) # confere se os elementos do shape estão OK
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Os tiles de dados originais abrangem uma área maior do que a
# necessária. Para reduzir a informação LiDAR que será usada, uma
# alternativa é usar os limites dos talhões mais um buffer de 10m
# como "tesoura" para clipar as nuvens.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dirTal <- str_c(dirLAZ, '/TALHOES') # Pasta p/ nuvens por talhao
if (!dir.exists(dirTal)) {
dir.create(dirTal, showWarnings = F)
}
opt_chunk_buffer(ctg_orig) <- 10 # 10 m buffer
opt_output_files(ctg_orig) <- str_c(dirTal, "/{SUBTALHAO}")
opt_laz_compression(ctg_orig) <- TRUE # Mantém formato LAZ
ctg_talh = clip_roi(ctg_orig, tal_shp) # Usa shape como "tesoura"
# Para plotar a nuvem LiDAR de um dos talhões no catálogo ctg_tal,
# basta ler o respectivo arquivo salvo pela função clip_roi(),
# como no exemplo abaixo:
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# las <- readLAS(ctg_tal$filename[3])
# plot(las)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Classifica pontos de solo (este passo pode ser bem lento)
# ... espere até que o sinal "stop" desapareça na janela de "log"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dirNoN <- str_c(dirTal, '/NoNORM') # Nuvens c/ ground não normalizadas
if (!dir.exists(dirNoN)) {
dir.create(dirNoN, showWarnings = F)
}
opt_output_files(ctg_talh) <- str_c(dirNoN, "/{*}_g")
ctg_gNoN <- classify_ground(ctg_talh, csf())
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Normaliza as nuvens de pontos dos talhões (igualmente lento)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dirNrm <- str_c(dirTal, '/SiNORM') # Pasta p/ nuvens normalizadas
if (!dir.exists(dirNrm)) {
dir.create(dirNrm, showWarnings = F)
}
opt_output_files(ctg_gNoN ) <- str_c(dirNrm, "/{*}SiN")
ctg_gSiN <- normalize_height(ctg_gNoN, tin())
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Retile dos talhões normalizados para geração de um conjunto de núvens
# mais apropriado para a correta clipagem das parcelas
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dirTNrm <- str_c(dirNrm, '/TILES') # Pasta p/ tiles normalizados
if (!dir.exists(dirTNrm)) {
dir.create(dirTNrm, showWarnings = F)
}
# ctg_gSiN <- readLAScatalog(dirTNrm)
opt_output_files(ctg_gSiN) <- # Onde guardar as nuvens normalizadas
str_c(dirTNrm, "/FzMod_{XLEFT}_{YBOTTOM}") # renomeadas com coords
opt_chunk_buffer(ctg_gSiN) <- 0 # sem buffers ao redor de cada tile
opt_chunk_size(ctg_gSiN) <- 300 # em tiles de 300 m
opt_laz_compression(ctg_gSiN) <- TRUE # Mantém formato LAZ
ctg_tile <- catalog_retile(ctg_gSiN) # e executa
rm(ctg_gNoN, ctg_gSiN, ctg_orig, ctg_talh) # limpa da memória catálogos
# Lê shape e atributos das parcelas p/ acessar parâmetros de interesse
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
shpPar <- str_c(shpDir, "/Modelo_parcentr.shp") # shape de parcelas
par_shp <- st_read(shpPar)
st_is_valid(par_shp) # confere se os elementos do shape estão OK
dirParc <- str_c(dirNrm, '/PARCELAS') # Pasta p/ parcelas clipadas
if (!dir.exists(dirParc)) {
dir.create(dirParc, showWarnings = F)
}
# Exibe localização dos tiles, talhoes e parcelas
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
plot(ctg_tile, mapview = TRUE, map.type = "Esri.WorldImagery")
plot(ctg_tile)
plot(tal_shp, add = TRUE, col="black")
plot(par_shp, add = TRUE, col="white")
# Clipa e salva as núvens das parcelas, e calcula
# métricas para cada "voxel" clipado com o shape de parcelas no tiles
# normalizados usando a função .stdmetrics_z de métricas genéricas
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
opt_output_files(ctg_tile) <- # Onde guardar as nuvens das parcelas
str_c(dirParc, "/P_{NUMPARCELA}") # renomeadas pelo respectivo número
opt_select(ctg_tile) <- "xyz" # Carrega na memória apenas coordenadas
opt_filter(ctg_tile) <- "-drop_z_below 0" # Ignora pontos com z < 0
D <- plot_metrics(ctg_tile, .stdmetrics_z, par_shp, radius = 11.28)
# Escolhe um subgrupo de métricas e dados para estudo da correlação
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
X <- tibble(D) %>% select(VTCC, MHDOM, IDINV, zmean,
zq45, zq75, zq95, zpcum2, zpcum4, zpcum6,
pzabovezmean, pzabove2)
# Prepara o gráfico para análise das correlações
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cormat <- X %>% cor()
cormat <- round(cor(cormat), 2) %>% reorder_cormat()
p <- graphCormat(cormat)
grfDir <- str_c(dirProjet, '/RESULTADOS/') # Define pasta p/ resultados
if (!dir.exists(grfDir)) { # Cria pasta, caso não exista
dir.create(grfDir, showWarnings = F)
}
fileGrf <- str_c(grfDir, "MatrizDeCorrelacoes.jpg") # Arq para matriz
tituGrf <- "Matriz de Correlações de Pearson" # Define um título
# Abre "impressora" para gerar e salvar o gráfico no formato JPEG
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
jpeg(fileGrf, width = 1000, height = 1000, units = "px", quality = 200)
plot(p + labs(title=tituGrf))
dev.off() # Fecha a "impressora"
# Análise de regressão por quadrados mínimos ordinários (OLS) para
# determinação do modelo de predição. Testando: VTCC ~f(zq90,IDINV)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
m <- lm(VTCC ~ zq95 + IDINV, data = X) # Análise de Regressão Linear
summary(m) # Mostra os resultados da regressão
plot(X$VTCC, predict(m)) # Gráfico de observado vs predito
abline(0,1)
# ----
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 4. Criação de mapas raster com as métricas mais promissoras
# É possível processar a partir deste bloco, desde que já tenham
# sido processadas as linhas 1 a 229 (Bloco 1.)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ----
prjNam <- "PRJ_Modelo" # Nome do projeto
dirRaizNuvens <- str_c('C:/LiDAR/',prjNam, '/NUVENS') # Raiz das nuvens
dirDadosLiDAR <- str_c(dirRaizNuvens, '/A13/TALHOES/SiNORM')
# Lê catálogo de com as núvens por talhão normalizadas
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ctg_taln <- readLAScatalog(dirDadosLiDAR)
nNuvens <- length(ctg_taln$filename) # Número de núvens no catálogo
# Criação de mapas raster formato tif para as métricas de interesse
# Atribua à variável "interesse" uma das métricas calculadas pela
# função padrão ".stdmetrics_z", dentre as várias: zmax, zmean,
# zsd, zskew, zkurt, zentropy, pzabovezmean, pzabove2, zq5, zq10,
# zq15, zq20, zq25, zq30, zq35, zq40, zq45, zq50, zq55, zq60, zq65,
# zq70, zq75, zq80, zq85, zq90, zq95, zpcum1, zpcum2, zpcum3, zpcum4,
# zpcum5, zpcum6, zpcum7, zpcum8, zpcum9
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
interesse <- "zq95"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Criação de mapas raster para a métrica de interesse (PIXEL QUADRADO)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dirDadosRaster <- str_c(dirDadosLiDAR, '/RSTR_qua/') # Quadrados
if (!dir.exists(dirDadosRaster)) {
dir.create(dirDadosRaster, showWarnings = F)
}
pb <- progress_bar$new(total = 2*nNuvens) # Reset da barra de progresso
for (i in 1:nNuvens) {
nuvem <- ctg_taln$filename[i]
talhao <- ctg_taln$filename[i] %>% basename() %>% file_path_sans_ext()
las <- readLAS(nuvem, select = "xyz",
filter = "-drop_z_below 2") # lê apenas pontos > 2m
pb$tick() # Avança barra de progresso
inteRaster <- (las %>% # Cria objeto raster da métrica de interesse
pixel_metrics(.stdmetrics_z, res = 20))[[interesse]]
rstNome <- str_c(dirDadosRaster, talhao,'_sqr_', interesse, '.tif')
writeRaster(inteRaster, rstNome, overwrite=TRUE) # Salva arquivo tif
pb$tick() # Avança barra de progresso
rstNome <- str_c(dirDadosRaster, talhao,'_sqr_', interesse, '.png')
# Cria parâmetros para melhorar a legenda do gráfico raster
minL = round(minmax(inteRaster)[1])-1 # Menor valor no raster
maxL = round(minmax(inteRaster)[2])+1 # Maior valor no raster
brkL = round((maxL - minL) / 5) # Legenda com cinco breaks
intL = c(minL + brkL, minL + 2*brkL, minL + 3*brkL, minL + 4*brkL)
limL = c(minL, maxL)
titulo <- str_c('Talhão: ', talhao, ' | Métrica: ', interesse,
' | Mín: ', minL, ' | Máx: ', maxL)
p <- ggplot() + geom_spatraster(data = inteRaster, aes()) +
guides(fill = guide_legend(reverse=F)) +
coord_sf(datum=st_crs(31983)) + # Gráfico numa certa projeção
scale_fill_whitebox_c(palette = "atlas", direction = -1,
breaks = intL, limits = limL)+ggtitle(titulo)
png(rstNome, 30, 20, 'cm', res = 200) # Abre "impressão"
print(p)
dev.off() # Fecha "impressão" do aquivo
}
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# !!! Método alternativo:
# Criação de mapas raster para a métrica de interesse (PIXEL HEXAGONAL)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# dirDadosRaster <- str_c(dirDadosLiDAR, '/RSTR_hex/') # Hexagonais
# if (!dir.exists(dirDadosRaster)) {
# dir.create(dirDadosRaster, showWarnings = F)
# }
#
# pb <- progress_bar$new(total = 2*nNuvens) # Reset da barra de progresso
# for (i in 1:nNuvens) {
# nuvem <- ctg_taln$filename[i]
# talhao <- ctg_taln$filename[i] %>% basename() %>% file_path_sans_ext()
#
# las <- readLAS(nuvem, select = "xyz",
# filter = "-drop_z_below 2") # lê apenas pontos > 2m
#
# pb$tick() # Avança barra de progresso
# inteRaster <- las %>%
# hexagon_metrics(.stdmetrics_z, area = 400) %>%
# select(contains(c(interesse, "geometry"))) %>%
# st_rasterize()
#
# rstNome <- str_c(dirDadosRaster, talhao,'_hex_', interesse, '.tif')
# write_stars(inteRaster, rstNome)
#
# pb$tick() # Avança barra de progresso
# rstNome <- str_c(dirDadosRaster, talhao,'_hex_', interesse, '.png')
# titulo <- str_c('Talhão: ', talhao, ' / Métrica: ', interesse)
# inteRaster <- as(inteRaster, "SpatRaster")
# # Cria parâmetros para melhorar a legenda do gráfico raster
# minL = round(minmax(inteRaster)[1])-1 # Menor valor no raster
# maxL = round(minmax(inteRaster)[2])+1 # Maior valor no raster
# brkL = round((maxL - minL) / 5) # Legenda com cinco breaks
# intL = c(minL + brkL, minL + 2*brkL, minL + 3*brkL, minL + 4*brkL)
# limL = c(minL, maxL)
# titulo <- str_c('Talhão: ', talhao, ' | Métrica: ', interesse,
# ' | Mín: ', minL, ' | Máx: ', maxL)
# p <- ggplot() + geom_spatraster(data = inteRaster, aes()) +
# guides(fill = guide_legend(reverse=F)) +
# coord_sf(datum=st_crs(31983)) + # Gráfico numa certa projeção
# scale_fill_whitebox_c(palette = "atlas", direction = -1,
# breaks = intL, limits = limL)+ggtitle(titulo)
# png(rstNome, 30, 20, 'cm', res = 200) # Abre "impressão"
# print(p)
# dev.off() # Fecha "impressão" do aquivo
# }
# Cálculo de mapas raster como função dos mapas rasters de métricas
# criados nos passos anteriores. Por exemplo, MAPA DE VOLUME
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ctg_taln <- readLAScatalog(dirDadosLiDAR)
nNuvens <- length(ctg_taln$filename) # Número de núvens no catálogo
# Criação de mapas raster formato tif para as métricas de interesse
# Atribua à variável "interesse" uma das métricas calculadas pela
# função .stdmetrics_z: zmax, zmean, zsd, zskew, zkurt, zentropy,
# pzabovezmean, pzabove2, zq5, zq10, zq15, zq20, zq25, zq30, zq35,
# zq40, zq45, zq50, zq55, zq60, zq65, zq70, zq75, zq80, zq85, zq90,
# zq95, zpcum1, zpcum2, zpcum3, zpcum4, zpcum5, zpcum6, zpcum7,
# zpcum8, zpcum9
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
interesse <- "zq95"
# Criação de lista de mapas raster criados nos passos anteriores
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
listaRasters <- list()
for (i in 1:nNuvens) {
nuvem <- ctg_taln$filename[i]
talhao <- ctg_taln$filename[i] %>% basename() %>% file_path_sans_ext()
rstNome <- str_c(dirDadosRaster, talhao,'_sqr_', interesse, '.tif')
listaRasters[[i]] <- rast(rstNome)
}
# Junta os rasters para gerar um mapa único com a métrica de interesse
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rastersJuntos <- do.call(merge, listaRasters)
# Cria gráfico caprichado da métrica de interesse com todos os rasters
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
minL = round(minmax(rastersJuntos)[1])-1 # Menor valor no raster
maxL = round(minmax(rastersJuntos)[2])+1 # Maior valor no raster
brkL = round((maxL - minL) / 5) # Legenda com cinco breaks
intL = c(minL + brkL, minL + 2*brkL, minL + 3*brkL, minL + 4*brkL)
limL = c(minL, maxL)
titulo <- str_c('Métrica: ', names(rastersJuntos),
' | Mín: ', minL, ' | Máx: ', maxL)
p <- ggplot() + geom_spatraster(data = rastersJuntos, aes()) +
guides(fill = guide_legend(reverse=F)) +
coord_sf(datum=st_crs(31983)) + # Gráfico numa certa projeção
scale_fill_whitebox_c(palette = "atlas", direction = -1,
breaks = intL, limits = limL)+ggtitle(titulo)
rstNome <- str_c(dirDadosRaster, 'talhoes_', names(rastersJuntos), '.png')
png(rstNome, 30, 20, 'cm', res = 200) # Abre "impressão"
print(p)
dev.off() # Fecha "impressão" do aquivo
# Lê o arquivo shape com os limites e atributos dos talhoes
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dirShape <- str_c('C:/GitRepo/',prjNam, '/SHAPES') # Pasta com shapes
arqShape <- str_c(dirShape, '/Modelo_talhoes.shp') # Nome do shape
# Cria estrutura de dados (data frame) com os atributos dos talhoes
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
poligonos <- vect(arqShape)
# Plota mapa de rasters juntados sobreposto com os limites dos talhões
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
plot(rastersJuntos)
plot(poligonos, add = TRUE, col = "white")
# ----
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 5 . Cria um novo raster com as estimativas de VOLUME para produção
# do mapa de VTCC. A esrimativa será obtida a partir do modelo
# ajustado na fase anterior:
# VTCC = -444.142 + 24.63 zq95 + 20.133 IDINV
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ----
# A lista poligonos$SUBTALHAO contém os nomes dos talhões da Fazenda
# Modelo: "302c" "301a" "301d" "302a". Criação de um vetor com as
# correspondentes idades na mesma ordem, para inclusão no objeto
# com informações sobres os poligonos
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Idades <- as.numeric(c(5.2, 3.7, 3.7, 5.2))
poligonos$IDINV <- Idades
valoresAtrib <- poligonos$IDINV
# Cria um novo raster com a idade do respectivo polígono atribuída ao
# respectivo pixel sobreposto
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rastersIdade <- rasterize(poligonos, rastersJuntos,
field=valoresAtrib, ext=ext(rastersJuntos),
res=res(rastersJuntos)) %>% rename(IDINV = layer)
# Junta rasters, mantendo os respecivos valores em layers separados,
# renomeia as colunas e assim, agora rastersJuntos tem as varíaveis
# necessárias para estimar o parâmetro de interesse, uma em cada layer.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rastersFinal <- c(rastersJuntos, rastersIdade)
# Usa a equação ajustada (final do bloco 3 deste script) para estimar
# o parâmetro de interesse
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rastersVolume <- -444.142 + 24.630 * rastersFinal["zq95"] +
20.133 * rastersFinal["IDINV"]
rastersVolume <- rastersVolume %>% rename(VTCC = zq95)
# Junta layer VTCC como novo layer no raster final
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rastersFinal = c(rastersFinal, rastersVolume)
# Cria novo raster de volumes mudando para zero valores negativos
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
vtcc <- ifel(rastersFinal[["VTCC"]] < 0, 0, rastersFinal)[["VTCC"]]
minmax(vtcc)[1]
minmax(vtcc)[2]
# Apaga rasters intermediários
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rm(inteRaster, rastersIdade, rastersJuntos, rastersVolume)
# /////////////////////////////////////////////////////////////////////
# MAPA DE ESTIMATIVAS DE VOLUME TOTAL COM CASCA (VTCC)
# /////////////////////////////////////////////////////////////////////
minL = round(minmax(vtcc)[1]) # Menor valor no raster
maxL = round(minmax(vtcc)[2]) # Maior valor no raster
brkL = round((maxL - minL) / 6) # Legenda com cinco breaks
intL = c(minL+brkL, minL+2*brkL, minL+3*brkL, minL+4*brkL, minL+5*brkL)
limL = c(minL, maxL)
titulo <- str_c('Mapa de estimativas de VTCC | Mín: ', minL,
' | Máx: ', maxL)
p <- ggplot() + geom_spatraster(data = vtcc, aes()) +
guides(fill = guide_legend(reverse=F)) +
coord_sf(datum=st_crs(31983)) + # Gráfico numa certa projeção
scale_fill_whitebox_c(palette = "atlas", direction = -1,
breaks = intL, limits = limL)+ggtitle(titulo)
rstNome <- str_c(dirDadosRaster, 'talhoes_VTCC.png')
png(rstNome, 30, 20, 'cm', res = 200) # Abre "impressão"
print(p)
dev.off() # Fecha "impressão" do aquivo
# ----
# /////////////////////////////////////////////////////////////////////
# TENTE AGORA CRIAR A TABELA COM A ESTIMATIVA DE VOLUME CALCULADA
# PELA AMOSTRAGEM DUPLA (SEMELHANTE À QUE FIZEMOS PARA ACS E ACE)
# /////////////////////////////////////////////////////////////////////
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Este script foi atualizado em Dezembro/2023 depois de instalar,
# na seguinte sequência, as mais recentes versões do pacote lidR*:
#
# devtools::install_github("Jean-Romain/rlas", dependencies=TRUE)
# devtools::install_github("Jean-Romain/lidR")
#
# * lidR latest development version
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ----
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_invcomlidar.txt · Última modificação: por 127.0.0.1
