GEE17: 基于Theil-Sen Median斜率估计和Mann-Kendall趋势分析方法分析四川省2022年NDVI变化情况

Theil-Sen Median + Mann-Kendall

    • [1. Theil-Sen Median + Mann-Kendall 原理](#1. Theil-Sen Median + Mann-Kendall 原理)
      • [1.1 Theil-Sen Median](#1.1 Theil-Sen Median)
      • [1.2 Mann-Kendall](#1.2 Mann-Kendall)
    • [2. GEE code](#2. GEE code)

1. Theil-Sen Median + Mann-Kendall 原理

1.1 Theil-Sen Median

  Theil-Sen Median 方法又称为Sen斜率估计,是一种稳健的非参数统计的趋势计算方法。该方法计算效率高,对于测量误差和利群数据不敏感,适用于长时间序列数据的趋势分析。其计算公式为:

  式中,n为研究图层数,即n=22; ρ为趋势度,当ρ<0时,表示NDVI随时间呈下降趋势,当ρ>0时,表示NDVI随时间呈上升趋势。

1.2 Mann-Kendall

  Mann-Kendall(MK) 检验是一种非参数的时间序列趋势性检验方法,其不需要测量值服从正太分布,不受缺失值和异常值的影响,适用于长时间序列数据的趋势显著检验。检验公式:

  式中,Q为检验统计量;Z为标准化后的检验统计量;Var(Q)为方差,在给定的α显著性水平下,若Z<Za/2,则表示通过了相应置信度的显著性检验。

  方差计算公式:

2. GEE code

javascript 复制代码
var imageCollection = ee.ImageCollection("MODIS/006/MOD13Q1");
var table = ee.FeatureCollection("users/cduthes1991/boundry/China_province_2019");
var roi = table.filter(ee.Filter.eq('provinces','sichuan'));
Map.centerObject(roi,5.8)

var styling = {color:"red",fillColor:"00000000"}
Map.addLayer(roi.style(styling),{},"geometry")


var imgCol = ee.ImageCollection("MODIS/006/MOD13Q1")
                        .filterDate('2022-1-1','2022-12-31')
                        .filterBounds(roi)
                        .select('NDVI')
                        .map(function(image){
                          var imgsub = image;
                          return image.clip(roi)
                        });
var palette = ['red', 'yellow', 'green'];

// Theil-Sen Median斜率估计 + Mann-Kendall趋势分析
// 做一个筛选器,筛选出右边比左边小的部分
var afterFilter = ee.Filter.lessThan({
    leftField: 'system:index',
    rightField: 'system:index'
});

// 做一个连接,让primary中所有元素和secondary做对比
// 将secondary中的year大于当前primary元素的加入到primary的after属性中
var joined = ee.ImageCollection(ee.Join.saveAll('after').apply({
    primary: imgCol,
    secondary: imgCol,
    condition: afterFilter
})).aside(print);


// 1、实现Theil-Sen Median斜率估计
var sen_slop = function (i, j) {  // i和j均为图像
    return ee.Image(j).subtract(i)
        .divide(ee.Image(j).date().difference(ee.Image(i).date(), 'year'))
        .rename('slope')
        .float();
};

// 两个map实现双重循环,先取出当前ndvi(current)的after属性(也就是大于当前的日期),
// 然后利用sen_slop函数计算current和after的sen斜率
var sen = ee.ImageCollection(joined.map(function (current) {
    var afterCollection = ee.ImageCollection.fromImages(current.get("after"))
    return afterCollection.map(function (after) {
        return ee.Image(sen_slop(current, after));
    })
}).flatten());


var sensSlope = sen.reduce(ee.Reducer.median(), 2); // Set parallelScale.
Map.addLayer(sensSlope, {palette: palette}, 'sensSlope');

// 将sensSlope中的像素值进行修改,将正值改为2,负值改为1;
// 以便图像可视化、遥感图像分类或不同区域或特征的标记
var classify = sensSlope.where(sensSlope.gt(0), 2)  // 将原图像中所有正值的像素修改为2
    .where(sensSlope.lt(0), 1)                      // 将原图像中所有负值的像素修改为1
Map.addLayer(classify, { min: 1, max: 2, palette: ['red', 'green'] }, 'Image classify to 0 and 1')

// 图像导出
Export.image.toDrive({
        image:sensSlope,
        description:'Sen_Median',
		   region:roi,    
        scale:250,
        maxPixels:1e13,
        folder:'LUCC'      
      })


// 2、实现Mann-Kendall趋势分析
// 构建符号函数sgn
var sign = function (i, j) {
    return ee.Image(j).neq(i)
        .multiply(ee.Image(j).subtract(i).clamp(-1, 1)).int();
};

// 符号函数求和
var kendall = ee.ImageCollection(joined.map(function (current) {
    var afterCollection = ee.ImageCollection.fromImages(current.get('after'));
    return afterCollection.map(function (image) {
        return ee.Image(sign(current, image));
    });
	// Set parallelScale to avoid User memory limit exceeded.
}).flatten()
).sum();

Map.addLayer(kendall, {min:-200, max:200, palette: palette}, 'kendall');   // 根据需要拉伸


// Values that are in a group (ties). Set all else to zero.
// 找到那些至少在两个图像对中像素值相等的位置,并将这些位置的像素值保留,其他位置的像素值设为0
// 个人认为基本所有像元都能保留,像素值为0的像元很少
var groups = imgCol.map(function (i) {
    var matches = imgCol.map(function (j) {
        return i.eq(j);
    }).sum();                // 基于布尔类型的图像生成一个新的图像,其中像素值表示该像元上像素值相等图层数量
    return i.multiply(matches.gt(1)); // 创建一个新图像,只保留那些在至少两个图像对中像素值相等的位置,并将其他位置的像素值设为0。
});


// Compute tie group sizes in a sequence. The first group is discarded.
var group = function (array) {
    var length = array.arrayLength(0);     // 获取array的长度,通常是数组的第一个维度(行数或索引)的长度
    
	// Array of indices. These are 1-indexed.
	// 将包含一个多维数组,其中每个元素的值表示其在数组中的索引
	// 所有元素初始化为1,使用一系列数组操作来生成一个长度为length的一维数组,每个元素的值都是1。
    var indices = ee.Image([1])
        .arrayRepeat(0, length)           // 将现有数组在指定维度上复制多次,即第0维(行数或索引)上重复length次
        .arrayAccum(0, ee.Reducer.sum())  // 对数组中的值进行累积计算,
        .toArray(1);
	
    var sorted = array.arraySort();   // 升序排序
    var left = sorted.arraySlice(0, 1);
    var right = sorted.arraySlice(0, 0, -1);
    // Indices of the end of runs.
    var mask = left.neq(right)
        // Always keep the last index, the end of the sequence.
        .arrayCat(ee.Image(ee.Array([[1]])), 0);

    var runIndices = indices.arrayMask(mask);
    // Subtract the indices to get run lengths.

    var groupSizes = runIndices.arraySlice(0, 1)
        .subtract(runIndices.arraySlice(0, 0, -1));
    return groupSizes;

};

// var(s)计算
var factors = function (image) {
    return image.expression('b() * (b() - 1) * (b() * 2 + 5)');
};


var groupSizes = group(groups.toArray());

var groupFactors = factors(groupSizes);

var groupFactorSum = groupFactors.arrayReduce('sum', [0])
    .arrayGet([0, 0]);

var count = joined.count();

var kendallVariance = factors(count)
    .subtract(groupFactorSum)
    .divide(18)
    .float()
    .rename('kendallVariance');

//Map.addLayer(kendallVariance, {}, 'kendallVariance');


// Compute Z-statistics.
var zero = kendall.multiply(kendall.eq(0));
var pos = kendall.multiply(kendall.gt(0)).subtract(1);
var neg = kendall.multiply(kendall.lt(0)).add(1);

// Z values
var z = zero
    .add(pos.divide(kendallVariance.sqrt()))
    .add(neg.divide(kendallVariance.sqrt()))
    .rename('z');
//Map.addLayer(z, {min: -2, max: 2}, 'z');


var qs = z.where(z.lte(1.65).and(sensSlope.lt(0)), -1)
    .where(z.gt(1.65).and(z.lte(1.96)).and(sensSlope.lt(0)), -2)
    .where(z.gt(1.96).and(z.lte(2.58)).and(sensSlope.lt(0)), -3)
    .where(z.gt(2.58).and(sensSlope.lt(0)), -4)
    .where(z.lte(1.65).and(sensSlope.lt(0)), 1)
    .where(z.gt(1.65).and(z.lte(1.96)).and(sensSlope.gt(0)), 2)
    .where(z.gt(1.96).and(z.lte(2.58)).and(sensSlope.gt(0)), 3)
    .where(z.gt(2.58).and(sensSlope.gt(0)), 4)

// 红色、绿色、蓝色、黄色、青色、品红色
var palette_qs = ['FF0000', '00FF00', '0000FF', 'FFFF00', '00FFFF', 'FF00FF'];
Map.addLayer(qs, { min: -1, max: 4, palette: palette_qs}, 'QS')


// 图像导出
Export.image.toDrive({
    image: qs,
    description: 'QS_Values',
	region: roi,
    scale: 250,
    maxPixels: 1e13,
	folder: 'LUCC'
})

结果展示:

1、sensSlope斜率估计图层:绿色表示斜率增加,红色表示斜率降低

2、二值图像分类:

3、Kendall趋势检验:主要展示哪些像元发生了变化,颜色越深,数值越大,则变化越大

4、Z统计值图:

5、NDVI趋势变化图:通过Z统计值分类得到

相关推荐
火眼金睛记单词1 小时前
阅读理解核心词汇:破解英语阅读难题的利器
经验分享·学习
EatFan1 小时前
React 项目踩坑实录:useEffect 闭包陷阱、Context 性能陷阱与 React 18 升级避坑排查手册
前端·javascript·react.js·react·react hooks·useeffect·闭包陷阱
不要打扰7562 小时前
《房地产经纪操作实务》重点和难点是什么?用哪个题库好
经验分享·职场和发展
OpenDataLab2 小时前
Agent 如何免费接入多个文献数据库?一个好用的工具分享
经验分享
小李不想当小白4 小时前
USART串口协议(STM32标准库学习笔记)
经验分享·笔记·stm32·单片机·嵌入式硬件·学习
w2sfot5 小时前
【JS加密在线】jsjiami.online(让JavaScript 更安全!)
开发语言·javascript·安全
VX130715441225 小时前
测绘资质延续申请申报材料常见退件问题及解答
搜索引擎·arcgis·全文检索
非科班Java出身GISer8 小时前
ArcGIS JS 基础教程(41):Popup 弹出窗
arcgis·arcgis js 弹出框·arcgis js 气泡框·arcgis js 信息框·arcgis 弹出框·arcgis 气泡框·arcgis 信息框
2601_967212728 小时前
智能供电轨道系统供应商技术筛选维度与选型指南
大数据·经验分享
weixin_433417678 小时前
开车去佘山去爬佘山攻略
经验分享