GEE code to map CH4 emmissions using Sentinel 1 and 2

// ============================================================
// EXPORT CATEGORIZED CH4 MAPS – CLEAR SYMBOLOGY
// DRY and WET are saved as integer classes (0,1,2,3)

// code developed by ERic Ariel Salas, Ph.D.
// ============================================================

var region = ee.Geometry.Rectangle([123.2, 9.5, 124.2, 11.2]); (change this for your geometry)

function maskClouds(img) {
var scl = img.select(‘SCL’);
var mask = scl.neq(3).and(scl.neq(8)).and(scl.neq(9)).and(scl.neq(10));
return img.updateMask(mask).select([‘B2′,’B3′,’B4′,’B8′,’B11′,’B12’]).divide(10000);
}

function processSeason(startDate, endDate, seasonName, threshold) {
print(‘Processing:’, seasonName, startDate, ‘to’, endDate);

var s2 = ee.ImageCollection(‘COPERNICUS/S2_SR’)
.filterBounds(region)
.filterDate(startDate, endDate)
.filter(ee.Filter.lt(‘CLOUDY_PIXEL_PERCENTAGE’, 20))
.map(maskClouds)
.median()
.clip(region);

var s1 = ee.ImageCollection(‘COPERNICUS/S1_GRD’)
.filterBounds(region)
.filterDate(startDate, endDate)
.filter(ee.Filter.listContains(‘transmitterReceiverPolarisation’, ‘VH’))
.filter(ee.Filter.eq(‘instrumentMode’, ‘IW’))
.select(‘VH’)
.median()
.clip(region);

var ndvi = s2.normalizedDifference([‘B8’, ‘B4’]).rename(‘NDVI’);
var lswi = s2.normalizedDifference([‘B8’, ‘B11’]).rename(‘LSWI’);
var features = s2.addBands(ndvi).addBands(lswi).addBands(s1.rename(‘VH’));

var pts = ee.FeatureCollection.randomPoints({region: region, points: 300, seed: 42, maxError: 10});
var sampled = features.sampleRegions({collection: pts, scale: 10, geometries: true});

function label(f) {
var n = ee.Number(f.get(‘NDVI’));
var l = ee.Number(f.get(‘LSWI’));
var v = ee.Number(f.get(‘VH’));

var primary = l.gt(n.subtract(threshold)).and(v.gt(-22)).and(v.lt(-10));
var fallback = n.gt(0.35);
var rice = primary.or(fallback);

return f.set('class', ee.Number(rice));

}

var data = sampled.map(label).filter(ee.Filter.notNull([‘class’]));

var riceCount = data.filter(ee.Filter.eq(‘class’, 1)).size().getInfo();
var nonCount = data.filter(ee.Filter.eq(‘class’, 0)).size().getInfo();

print(seasonName + ‘ – Rice points:’, riceCount, ‘| Non-rice:’, nonCount);

if (riceCount < 10) {
print(‘WARNING: Too few rice points for’, seasonName);
return null;
}

var withRandom = data.randomColumn(‘random’, 42);
var train = withRandom.filter(‘random < 0.7’);
var valid = withRandom.filter(‘random >= 0.7’);

var rf = ee.Classifier.smileRandomForest(50, 1, 42).train({
features: train,
classProperty: ‘class’,
inputProperties: features.bandNames()
});

var classified = features.select(features.bandNames()).classify(rf);

var riceMask = classified.eq(1);
var pixelAreaHa = 0.01;
var efPerHa = 300;
var ch4PerPixel = riceMask.multiply(efPerHa * pixelAreaHa).rename(‘CH4’);

// ============================================================
// CATEGORIZE CH4 VALUES INTO DISCRETE CLASSES
// Classes:
// 0 = No emission (0 kg CH4)
// 1 = Low emission (0.1 – 1.0 kg CH4)
// 2 = Medium emission (1.1 – 2.0 kg CH4)
// 3 = High emission (2.1 – 3.0 kg CH4)
// ============================================================
var ch4Categorized = ee.Image(0)
.where(ch4PerPixel.gt(0).and(ch4PerPixel.lte(1.0)), 1)
.where(ch4PerPixel.gt(1.0).and(ch4PerPixel.lte(2.0)), 2)
.where(ch4PerPixel.gt(2.0), 3)
.rename(‘CH4_CLASS’)
.toInt();

// ============================================================
// EXPORT CATEGORIZED CH4 MAP
// ============================================================
if (seasonName === ‘DRY’) {
Export.image.toDrive({
image: ch4Categorized,
description: ‘DRY_CH4_Categorized_2024’,
folder: ‘GEE_Exports’,
fileNamePrefix: ‘DRY_CH4_Categorized_2024’,
region: region,
scale: 10,
maxPixels: 1e10
});
}

if (seasonName === ‘WET’) {
Export.image.toDrive({
image: ch4Categorized,
description: ‘WET_CH4_Categorized_2024’,
folder: ‘GEE_Exports’,
fileNamePrefix: ‘WET_CH4_Categorized_2024’,
region: region,
scale: 10,
maxPixels: 1e10
});
}

// ============================================================
// ALSO EXPORT RAW (CONTINUOUS) CH4 MAP FOR REFERENCE
// ============================================================
if (seasonName === ‘DRY’) {
Export.image.toDrive({
image: ch4PerPixel,
description: ‘DRY_CH4_Raw_2024’,
folder: ‘GEE_Exports’,
fileNamePrefix: ‘DRY_CH4_Raw_2024’,
region: region,
scale: 10,
maxPixels: 1e10
});
}

if (seasonName === ‘WET’) {
Export.image.toDrive({
image: ch4PerPixel,
description: ‘WET_CH4_Raw_2024’,
folder: ‘GEE_Exports’,
fileNamePrefix: ‘WET_CH4_Raw_2024’,
region: region,
scale: 10,
maxPixels: 1e10
});
}

// ============================================================
// ADD PREVIEW LAYER (low-resolution)
// ============================================================
var ch4Preview = ch4Categorized.reduceResolution({
reducer: ee.Reducer.mode(),
maxPixels: 1024
}).reproject({
crs: ch4PerPixel.projection(),
scale: 500
});

if (seasonName === ‘DRY’) {
Map.addLayer(ch4Preview, {
min: 0, max: 3,
palette: [‘white’, ‘yellow’, ‘orange’, ‘red’]
}, ‘DRY CH4 CATEGORIZED’);
} else {
Map.addLayer(ch4Preview, {
min: 0, max: 3,
palette: [‘white’, ‘lightblue’, ‘blue’, ‘darkblue’]
}, ‘WET CH4 CATEGORIZED’);
}

print(seasonName + ‘ ✅ Categorized export + Preview added’);

return {
season: seasonName,
ricePoints: riceCount
};
}

var threshold = -0.15;

print(‘========================================’);
print(‘STARTING CATEGORIZED EXPORTS’);
print(‘========================================’);

var dry = processSeason(‘2024-01-01’, ‘2024-05-31’, ‘DRY’, threshold);
var wet = processSeason(‘2024-06-01’, ‘2024-11-30’, ‘WET’, threshold);

print(‘========================================’);
print(‘📌 EXPORT TASKS STARTED:’);
print(‘========================================’);
print(‘📂 Folder: GEE_Exports’);
print(‘📄 Files:’);
print(‘ ✅ DRY_CH4_Categorized_2024.tif (0,1,2,3 classes)’);
print(‘ ✅ DRY_CH4_Raw_2024.tif (continuous values)’);
print(‘ ✅ WET_CH4_Categorized_2024.tif (0,1,2,3 classes)’);
print(‘ ✅ WET_CH4_Raw_2024.tif (continuous values)’);
print(‘========================================’);
print(‘📌 HOW TO SYMBOLIZE IN QGIS/SMAP:’);
print(‘ Use the Categorized file and assign colors to:’);
print(‘ 0 = White (No rice)’);
print(‘ 1 = Yellow (Low emission)’);
print(‘ 2 = Orange (Medium emission)’);
print(‘ 3 = Red (High emission)’);
print(‘========================================’);

Leave a Reply

Your email address will not be published. Required fields are marked *

15 − 11 =

This site uses Akismet to reduce spam. Learn how your comment data is processed.