第13节 对时间序列的影像进行非线性拟合:平滑处理
1 数据说明
LANDSAT/LC8_L1T_TOA
详细的说明可以看注释
2 结果展示
线性和非线性拟合
线性
非线性
滑动平均值
原始值
滑动平均值
3 详细代码
var l8toa = ee.ImageCollection("LANDSAT/LC8_L1T_TOA");
var roi = ee.Geometry.Point([-121.9353461265564, 37.56180984982223]);
Map.centerObject(roi, 10);
// This field contains UNIX time in milliseconds.
var timeField = 'system:time_start';
// Use this function to mask clouds in Landsat 8 imagery.
var maskClouds = function(image) {
var quality = image.select('BQA');
var cloud01 = quality.eq(61440);
var cloud02 = quality.eq(53248);
var cloud03 = quality.eq(28672);
var mask = cloud01.or(cloud02).or(cloud03).not();
return image.updateMask(mask);
};
// Use this function to add variables for NDVI, time and a constant
// to Landsat 8 imagery.
var addVariables = function(image) {
// Compute time in fractional years since the epoch.
var date = ee.Date(image.get(timeField));
var years = date.difference(ee.Date('1970-01-01'), 'year');
// Return the image with the added bands.
return image
// Add an NDVI band.
.addBands(image.normalizedDifference(['B5', 'B4']).rename('NDVI')).float()
// Add a time band.
.addBands(ee.Image(years).rename('t').float())
// Add a constant band.
.addBands(ee.Image.constant(1));
};
// Remove clouds, add variables and filter to the area of interest.
var filteredLandsat = l8toa
.filterBounds(roi)
.map(maskClouds)
.map(addVariables);
// Plot a time series of NDVI at a single location.
var l8Chart = ui.Chart.image.series(filteredLandsat.select('NDVI'), roi)
.setChartType('ScatterChart')
.setOptions({
title: 'Landsat 8 NDVI time series at ROI',
trendlines: {0: {
color: 'CC0000'
}},
lineWidth: 1,
pointSize: 3,
});
print(l8Chart);
// Linear trend ----------------------------------------------------------------
// List of the independent variable names
var independents = ee.List(['constant', 't']);
// Name of the dependent variable.
var dependent = ee.String('NDVI');
// Compute a linear trend. This will have two bands: 'residuals' and
// a 2x1 band called coefficients (columns are for dependent variables).
var trend = filteredLandsat.select(independents.add(dependent))
.reduce(ee.Reducer.linearRegression(independents.length(), 1));
// Map.addLayer(trend, {}, 'trend array image');
// Flatten the coefficients into a 2-band image
var coefficients = trend.select('coefficients')
.arrayProject([0])
.arrayFlatten([independents]);
// Compute a de-trended series.
var detrended = filteredLandsat.map(function(image) {
return image.select(dependent).subtract(
image.select(independents).multiply(coefficients).reduce('sum'))
.rename(dependent)
.copyProperties(image, [timeField]);
});
// Plot the detrended results.
var detrendedChart = ui.Chart.image.series(detrended, roi, null, 30)
.setOptions({
title: 'Detrended Landsat time series at ROI',
lineWidth: 1,
pointSize: 3,
});
print(detrendedChart);
// Harmonic trend ----------------------------------------------------------------
// Use these independent variables in the harmonic regression.
var harmonicIndependents = ee.List(['constant', 't', 'cos', 'sin']);
// Add harmonic terms as new image bands.
var harmonicLandsat = filteredLandsat.map(function(image) {
var timeRadians = image.select('t').multiply(2 * Math.PI);
return image
.addBands(timeRadians.cos().rename('cos'))
.addBands(timeRadians.sin().rename('sin'));
});
// The output of the regression reduction is a 4x1 array image.
var harmonicTrend = harmonicLandsat
.select(harmonicIndependents.add(dependent))
.reduce(ee.Reducer.linearRegression(harmonicIndependents.length(), 1));
// Turn the array image into a multi-band image of coefficients.
var harmonicTrendCoefficients = harmonicTrend.select('coefficients')
.arrayProject([0])
.arrayFlatten([harmonicIndependents]);
// Compute fitted values.
var fittedHarmonic = harmonicLandsat.map(function(image) {
return image.addBands(
image.select(harmonicIndependents)
.multiply(harmonicTrendCoefficients)
.reduce('sum')
.rename('fitted'));
});
// Plot the fitted model and the original data at the ROI.
print(ui.Chart.image.series(
fittedHarmonic.select(['fitted','NDVI']), roi, ee.Reducer.mean(), 30)
.setSeriesNames(['NDVI', 'fitted'])
.setOptions({
title: 'Harmonic model: original and fitted values',
lineWidth: 1,
pointSize: 3,
}));
// Compute phase and amplitude.
var phase = harmonicTrendCoefficients.select('cos').atan2(
harmonicTrendCoefficients.select('sin'));
var amplitude = harmonicTrendCoefficients.select('cos').hypot(
harmonicTrendCoefficients.select('sin'));
// Use the HSV to RGB transform to display phase and amplitude
var rgb = phase.unitScale(-Math.PI, Math.PI).addBands(
amplitude.multiply(2.5)).addBands(
ee.Image(1)).hsvToRgb();
Map.addLayer(rgb, {}, 'phase (hue), amplitude (saturation)');
// Autocovariance and autocorrelation ---------------------------------------------
// Function to get a lagged collection. Images that are within
// lagDays of image are stored in a List in the 'images' property.
var lag = function(leftCollection, rightCollection, lagDays) {
var filter = ee.Filter.and(
ee.Filter.maxDifference({
difference: 1000 * 60 * 60 * 24 * lagDays,
leftField: timeField,
rightField: timeField
}),
ee.Filter.greaterThan({
leftField: timeField,
rightField: timeField
}));
return ee.Join.saveAll({
matchesKey: 'images',
measureKey: 'delta_t',
ordering: timeField,
ascending: false, // Sort reverse chronologically
}).apply({
primary: leftCollection,
secondary: rightCollection,
condition: filter
});
};
// Lag the Landsat series to get the previous image.
// Note that the results vary when using detrended data
var lagged17 = lag(detrended, detrended, 17);
print(lagged17,'lagged17 ')
// Function to merge bands of a lagged collection. If a collection is
// lagged with itself, the band names will be appended with an '_' and
// suffixed by an index of the order in which they were added. Because
// the 'images' list is sorted reverse chronologically, band_1 is the t-1
// image when the band names collide.
var merge = function(image) {
// Function to be passed to iterate.
var merger = function(current, previous) {
return ee.Image(previous).addBands(current);
};
return ee.ImageCollection.fromImages(image.get('images')).iterate(merger, image);
};
// Merge the bands together.
var merged17 = ee.ImageCollection(lagged17.map(merge));
// Function to compute covariance over time. This will return
// a 2x2 array image. Pixels contains variance-covariance matrices.
var covariance = function(mergedCollection, band, lagBand) {
return mergedCollection.select([band, lagBand]).map(function(image) {
return image.toArray();
}).reduce(ee.Reducer.covariance(), 8);
};
// Compute covariance from the merged series.
var lagBand = dependent.cat('_1');
var covariance17 = ee.Image(covariance(merged17, dependent, lagBand));
// (Note that covariance accentuates agriculture)
Map.addLayer(covariance17.arrayGet([0, 1]), {}, 'covariance (lag = 17 days)');
// Compute correlation from a 2x2 covariance image.
var correlation = function(vcArrayImage) {
var covariance = ee.Image(vcArrayImage).arrayGet([0, 1]);
var sd0 = ee.Image(vcArrayImage).arrayGet([0, 0]).sqrt();
var sd1 = ee.Image(vcArrayImage).arrayGet([1, 1]).sqrt();
return covariance.divide(sd0).divide(sd1).rename('correlation');
};
// Correlation
var correlation17 = correlation(covariance17);
// (Not sure what this means)
Map.addLayer(correlation17, {min: -1, max: 1}, 'correlation (lag = 17 days)');
// Lag the Landsat series to get the previous image.
var lagged34 = lag(detrended, detrended, 34);
// Merge the bands together.
var merged34 = ee.ImageCollection(lagged34.map(merge))
.map(function(image) {
return image.set('laggedImages', ee.List(image.get('images')).length());
})
.filter(ee.Filter.gt('laggedImages', 1));
// Compute covariance from the merged series.
var covariance34 = ee.Image(covariance(merged34, dependent, dependent.cat('_2')));
Map.addLayer(covariance34.arrayGet([0, 1]), {}, 'covariance34');
// Cross-correlation ----------------------------------------------------------------
// Precipitation (covariate)
var chirps = ee.ImageCollection('UCSB-CHG/CHIRPS/PENTAD');
// Join the precipitation images from a pentad ago
var lag1PrecipNDVI = lag(filteredLandsat, chirps, 5);
print('lag1PrecipNDVI', lag1PrecipNDVI);
// Add the precipitation images as bands.
var merged1PrecipNDVI = ee.ImageCollection(lag1PrecipNDVI.map(merge));
print('merged1PrecipNDVI', merged1PrecipNDVI);
// Compute covariance.
var cov1PrecipNDVI = covariance(merged1PrecipNDVI, 'NDVI', 'precipitation');
Map.addLayer(cov1PrecipNDVI.arrayGet([0, 1]), {}, 'NDVI - PRECIP cov (lag = 5)');
// Correlation.
var corr1PrecipNDVI = correlation(cov1PrecipNDVI);
Map.addLayer(corr1PrecipNDVI, {min: -0.5, max: 0.5}, 'NDVI - PRECIP corr (lag = 5)');
// Advanced cross-correlation----------------------------------------------------
// Join the precipitation images from the previous month
var lag30PrecipNDVI = lag(filteredLandsat, chirps, 30);
print(lag30PrecipNDVI);
var sum30PrecipNDVI = ee.ImageCollection(lag30PrecipNDVI.map(function(image) {
var laggedImages = ee.ImageCollection.fromImages(image.get('images'));
return ee.Image(image).addBands(laggedImages.sum().rename('sum'));
}));
// Compute covariance.
var cov30PrecipNDVI = covariance(sum30PrecipNDVI, 'NDVI', 'sum');
Map.addLayer(cov1PrecipNDVI.arrayGet([0, 1]), {}, 'NDVI - sum cov (lag = 30)');
// Correlation.
var corr30PrecipNDVI = correlation(cov30PrecipNDVI);
Map.addLayer(corr30PrecipNDVI, {min: -0.5, max: 0.5}, 'NDVI - sum corr (lag = 30)');
// AR models --------------------------------------------------------------------------
// Lag the Landsat series to get the previous image.
var lagged34 = ee.ImageCollection(lag(filteredLandsat, filteredLandsat, 34));
// Merge the bands together.
var merged34 = lagged34.map(merge).map(function(image) {
return image.set('n', ee.List(image.get('images')).length());
}).filter(ee.Filter.gt('n', 1));
// These names are based on the default behavior of addBands(), which
// appends '_n' for the nth time the band name is replicated.
var arIndependents = ee.List(['constant', 'NDVI_1', 'NDVI_2']);
var ar2 = merged34
.select(arIndependents.add(dependent))
.reduce(ee.Reducer.linearRegression(arIndependents.length(), 1));
// Turn the array image into a multi-band image of coefficients.
var arCoefficients = ar2.select('coefficients')
.arrayProject([0])
.arrayFlatten([arIndependents]);
// Compute fitted values.
var fittedAR = merged34.map(function(image) {
return image.addBands(
image.expression('beta0 + beta1 * p1 + beta2 * p2', {
p1: image.select('NDVI_1'),
p2: image.select('NDVI_2'),
beta0: arCoefficients.select('constant'),
beta1: arCoefficients.select('NDVI_1'),
beta2: arCoefficients.select('NDVI_2')
}).rename('fitted'));
});
// Plot the fitted model and the original data at the ROI.
print(ui.Chart.image.series(
fittedAR.select(['fitted', 'NDVI']), roi, ee.Reducer.mean(), 30)
.setSeriesNames(['NDVI', 'fitted'])
.setOptions({
title: 'AR(2) model: original and fitted values',
lineWidth: 1,
pointSize: 3,
}));
// Forecasting
var fill = function(current, list) {
// Get the date of the last image in the list.
var latestDate = ee.Image(ee.List(list).get(-1)).date();
// Get the date of the current image being processed.
var currentDate = ee.Image(current).date();
// If those two dates are more than 16 days apart, there's
// a temporal gap in the sequence. To fill in the gap, compute
// the potential starting and ending dates of the gap.
var start = latestDate.advance(16, 'day').millis();
var end = currentDate.advance(-16, 'day').millis();
// Determine if the start and end dates are chronological.
var blankImages = ee.Algorithms.If({
// Watch out for this. Might need a tolerance here.
condition: start.lt(end),
// Make a sequence of dates to fill in with empty images.
trueCase: ee.List.sequence({
start: start,
end: end,
step: 1000 * 60 * 60 * 24 * 16
}).map(function(date) {
// Return a dummy image with a masked NDVI band and a date.
return ee.Image(0).mask(0).rename('NDVI').set({
'dummy': true,
'system:time_start': ee.Date(date).millis()
});
}),
// If there's no gap, return an empty list.
falseCase: []
});
// Add any dummy images and the current image to the list.
return ee.List(list).cat(blankImages).add(current);
};
// The first image is the starting image.
var first = filteredLandsat.first();
// The first image is duplicated in this list, so slice it off.
var filled = ee.List(filteredLandsat.iterate(fill, [first])).slice(1);
// Now, map a function over this list to do the prediction.
var indices = ee.List.sequence(5, filled.length().subtract(1));
// A function to forecast from the previous two images.
var forecast = function(current, list) {
var ndvi = ee.Image(current).select('NDVI');
// Get the t-1 and t-2 images.
var size = ee.List(list).size();
var image1 = ee.Image(ee.List(list).get(size.subtract(1)));
var image2 = ee.Image(ee.List(list).get(size.subtract(2)));
var predicted = ee.Image().expression('beta0 + beta1 * p1 + beta2 * p2', {
p1: image1.select('NDVI'),
p2: image2.select('NDVI'),
beta0: arCoefficients.select('constant'),
beta1: arCoefficients.select('NDVI_1'),
beta2: arCoefficients.select('NDVI_2')
}).rename('NDVI')
.set('system:time_start', current.get('system:time_start'));
// Replace the entire image if it's a dummy.
var replaced = ee.Algorithms.If({
condition: current.get('dummy'),
trueCase: predicted,
// Otherwise replace only masked pixels.
falseCase: current.addBands({
srcImg: ndvi.unmask().where(ndvi.mask().not(), predicted).rename('NDVI'),
overwrite: true
})
});
// Add the predicted image to the list.
return ee.List(list).add(replaced);
};
// Start at a point in the sequence with three consecutive real images.
var startList = filled.slice(4, 5);
// Iterate over the filled series to replace dummy images with predictions.
var modeled = ee.ImageCollection.fromImages(
ee.ImageCollection(filled).iterate(forecast, startList)).select('NDVI');
print(ui.Chart.image.series(
modeled, roi, ee.Reducer.mean(), 30)
.setSeriesNames(['NDVI'])
.setOptions({
title: 'forecast',
lineWidth: 1,
pointSize: 3,
}));
// Smoothing ---------------------------------------------------------------
var join = ee.Join.saveAll({
matchesKey: 'images'
});
var diffFilter = ee.Filter.maxDifference({
difference: 1000 * 60 * 60 * 24 * 17,
leftField: timeField,
rightField: timeField
});
var threeNeighborJoin = join.apply({
primary: modeled,
secondary: modeled,
condition: diffFilter
});
var smoothed = ee.ImageCollection(threeNeighborJoin.map(function(image) {
var collection = ee.ImageCollection.fromImages(image.get('images'));
return ee.Image(image).addBands(collection.mean().rename('mean'));
}));
// Note that there is still a discontinuity in the chart. While there may be
// two neighbors, the pixel may be masked at one or all of those times.
print(ui.Chart.image.series(
smoothed.select(['NDVI', 'mean']), roi, ee.Reducer.mean(), 30)
.setSeriesNames(['NDVI', 'mean'])
.setOptions({
title: 'smoothed',
lineWidth: 1,
pointSize: 3,
}));
激励自己,尽可能每周更新1-2篇,2020加油!!!
需要交流或者有项目合作可以加微信好友 (姓名+专业+学校)
微信号:comingboy0701
公众号:猿人充电站
Last updated