You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于已知年份计算SpatRaster的dNBR等火烧后光谱恢复指标?

基于火烧年份计算时序NBR恢复指标

数据背景

我有研究区(AOI)内38年的拟合NBR值SpatRaster(由LandTrendR导出),以及单场火灾的火烧年份:

# class       : SpatRaster 
# dimensions  : 967, 1193, 38  (nrow, ncol, nlyr)
# resolution  : 8.983153e-05, 8.983153e-05  (x, y)
# extent      : -115.5009, -115.3937, 51.15043, 51.2373  (xmin, xmax, ymin, ymax)
# coord. ref. : lon/lat WGS 84 (EPSG:4326) 
# source      : NBR_FTV_fit_neg_stack.tif 
# names       : 1984, 1985, 1986, 1987, 1988, 1989, ...

year_of_fire <- 2014

需求说明

需要编写通用函数,无需手动传入火烧前后年份,仅基于year_of_fire对SpatRaster图层索引,计算以下火烧后光谱恢复指标:

  • dNBR:公式为 dNBR = NBR_pre - NBR_post(默认取火烧前1年NBR作为pre,火烧后1年作为post,可按需调整)
  • R80P(80%恢复率):公式为 R80P = max(火烧后第4/5年NBR) / (火烧前3年NBR平均值 * 0.8)
  • 函数需支持批量处理数十场火灾

解决方案

核心思路

利用terra包的图层名称(年份)匹配year_of_fire,提取对应时序窗口的图层,再逐像元计算指标。无需依赖tapp,直接通过图层索引和原生函数实现更直观。

1. 辅助工具函数:提取指定年份窗口的图层

先编写辅助函数,快速提取火烧前后目标时序的NBR图层:

library(terra)

extract_nbr_window <- function(nbr_stack, fire_year, pre_years = -3:-1, post_years = 1:5) {
  # 获取所有图层的年份
  layer_years <- as.integer(names(nbr_stack))
  # 提取火烧前/后窗口的图层索引
  pre_idx <- which(layer_years %in% (fire_year + pre_years))
  post_idx <- which(layer_years %in% (fire_year + post_years))
  
  # 返回对应的SpatRaster子集
  list(
    pre_nbr = nbr_stack[[pre_idx]],
    post_nbr = nbr_stack[[post_idx]]
  )
}

2. 计算dNBR

calc_dnbr <- function(nbr_stack, fire_year) {
  layer_years <- as.integer(names(nbr_stack))
  # 匹配火烧前1年和后1年的图层索引
  pre_idx <- which(layer_years == fire_year - 1)
  post_idx <- which(layer_years == fire_year + 1)
  
  # 校验年份是否存在
  if(length(pre_idx) == 0 || length(post_idx) == 0) {
    stop("火烧前后目标年份不在NBR堆栈中")
  }
  
  # 计算dNBR并命名
  dnbr <- nbr_stack[[pre_idx]] - nbr_stack[[post_idx]]
  names(dnbr) <- paste0("dNBR_", fire_year)
  return(dnbr)
}

# 调用示例
dnbr_result <- calc_dnbr(nbr_stack = your_nbr_raster, fire_year = 2014)

3. 计算R80P

calc_r80p <- function(nbr_stack, fire_year) {
  layer_years <- as.integer(names(nbr_stack))
  # 匹配火烧前3年和后4-5年的图层索引
  pre_idx <- which(layer_years %in% (fire_year - 3):(fire_year - 1))
  post_idx <- which(layer_years %in% (fire_year + 4):(fire_year + 5))
  
  # 校验年份窗口完整性
  if(length(pre_idx) < 3 || length(post_idx) < 2) {
    stop("火烧前后目标年份窗口不完整")
  }
  
  # 分步计算指标
  pre_mean <- mean(nbr_stack[[pre_idx]])
  post_max <- max(nbr_stack[[post_idx]])
  r80p <- post_max / (pre_mean * 0.8)
  
  names(r80p) <- paste0("R80P_", fire_year)
  return(r80p)
}

# 调用示例
r80p_result <- calc_r80p(nbr_stack = your_nbr_raster, fire_year = 2014)

4. 批量处理多场火灾

遍历火灾年份列表,批量计算并合并结果:

# 假设有多个火灾年份
fire_years <- c(2010, 2014, 2018)

# 批量计算dNBR
batch_dnbr <- lapply(fire_years, function(y) calc_dnbr(your_nbr_raster, y))
batch_dnbr <- rast(batch_dnbr) # 合并为多图层SpatRaster

# 批量计算R80P同理
batch_r80p <- lapply(fire_years, function(y) calc_r80p(your_nbr_raster, y))
batch_r80p <- rast(batch_r80p)

替代方案(针对像元级火烧年份栅格)

如果year_of_fire是每个像元对应不同火烧年份的SpatRaster,可使用ifel逐像元计算:

calc_dnbr_per_pixel <- function(nbr_stack, fire_year_raster) {
  layer_years <- as.integer(names(nbr_stack))
  dnbr <- rast(fire_year_raster) # 创建空结果栅格
  
  # 遍历所有唯一火烧年份
  unique_fire_years <- unique(values(fire_year_raster))
  unique_fire_years <- unique_fire_years[!is.na(unique_fire_years)]
  
  for(y in unique_fire_years) {
    pre_idx <- which(layer_years == y - 1)
    post_idx <- which(layer_years == y + 1)
    if(length(pre_idx) == 0 || length(post_idx) == 0) next
    
    # 对该火烧年份的像元赋值dNBR
    dnbr <- ifel(fire_year_raster == y, nbr_stack[[pre_idx]] - nbr_stack[[post_idx]], dnbr)
  }
  
  names(dnbr) <- "dNBR_per_pixel"
  return(dnbr)
}

内容的提问来源于stack exchange,提问作者user217532

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 03:12:35