Google Earth Engine – Sample Regions Excluding Masked Bands

attribute-joinsgoogle-earth-enginemaskingsamplepointsvalue-extraction

I have a set of points from around the world and I want to extract the pixel values from a set of images, giving information about the local environment, for each point. Then export the set of values as a .csv table.

My approach was to merge the images into a single multi-band image, then use SampleRegions to extract the values.

The problem is that if one of the images has a mask at the point location, the point is not included in the final results. So only points with no masked pixels are included in the output.
I would like to include all the points, even if some of the bands have masked pixels.

//1. Set points: 
var fc_points = ee.FeatureCollection([
  ee.Feature(ee.Geometry.Point([-17.5558, 65.8674]),    {ID: '1'}),
  ee.Feature(ee.Geometry.Point(-7.85187, 54.15745),     {ID: '2'}),   
  ee.Feature(ee.Geometry.Point(177.469394, -38.968075), {ID: '3'}),
  ee.Feature(ee.Geometry.Point(-78.93655, -2.2895),     {ID: '4'}),     
  ee.Feature(ee.Geometry.Point(6.11699, 61.51734),      {ID: '5'}), 
          ]);

//2. Make multiband image 
var koppen = ee.Image("users/fsn1995/Global_19862010_KG_5m")
koppen = koppen.updateMask(koppen.lte(30)); // Koppen-Geiger climate class
var mean_precipitation = ee.Image("WORLDCLIM/V1/BIO").select('bio12') // World Climate: mean annual precipitation 
var Cop_dataset = ee.Image("COPERNICUS/Landcover/100m/Proba-V-C3/Global/2019").select('discrete_classification'); //Copernicus Land cover 
var landCover = ee.Image('COPERNICUS/CORINE/V20/100m/2012').select('landcover'); //CORINE landcover

// rename bands
    var climate = koppen.rename('KG_climate').toFloat();
    var precipitation = mean_precipitation.rename('WC_mean_precipitation_mmyr');
    var landcover_Copernicus = Cop_dataset.rename('GLC_Copernicus').toFloat();
    var landcover_CORINE = landCover.rename('LC_CORINE').toFloat();

// combine into single image
var merged_image = climate
      .addBands(precipitation)
      .addBands(landcover_Copernicus)
      .addBands(landcover_CORINE)


// Sample pixel values for each point, using SampleRegions: (requires: Image, points)
var sampled = merged_image.sampleRegions({
  collection: fc_points, // is a feature collection
  properties: ['ID'], // List of column names of sample points inside feature collection   (not compulsory).
  scale:30  // spatial resolution of band or image
});

print(sampled,'Sample_Region');


Export.table.toDrive({
  collection: sampled,
  description: 'sample_example_pt',
  fileFormat: 'CSV'
});

I tried to convert masked values to -99 by adding .unmask(-99) to each of the original images. But that didn't help.

My next idea would be to sample each layer separately, then join the results based on the ID property.

Any suggestions?

Best Answer

I think that with 'sampleRegions' method is not possible to solve this issue. We need to use 'sample' method when mapping point feature collection as follows:

//1. Set points: 
var fc_points = ee.FeatureCollection([
  ee.Feature(ee.Geometry.Point([-17.5558, 65.8674]),    {ID: '1'}),
  ee.Feature(ee.Geometry.Point(-7.85187, 54.15745),     {ID: '2'}),   
  ee.Feature(ee.Geometry.Point(177.469394, -38.968075), {ID: '3'}),
  ee.Feature(ee.Geometry.Point(-78.93655, -2.2895),     {ID: '4'}),     
  ee.Feature(ee.Geometry.Point(6.11699, 61.51734),      {ID: '5'}), 
          ]);

//2. Make multiband image 
var koppen = ee.Image("users/fsn1995/Global_19862010_KG_5m")
koppen = koppen.updateMask(koppen.lte(30)); // Koppen-Geiger climate class
var mean_precipitation = ee.Image("WORLDCLIM/V1/BIO").select('bio12') // World Climate: mean annual precipitation 
var Cop_dataset = ee.Image("COPERNICUS/Landcover/100m/Proba-V-C3/Global/2019").select('discrete_classification'); //Copernicus Land cover 
var landCover = ee.Image('COPERNICUS/CORINE/V20/100m/2012').select('landcover'); //CORINE landcover

// rename bands
var climate = koppen.rename('KG_climate').toFloat();
var precipitation = mean_precipitation.rename('WC_mean_precipitation_mmyr');
var landcover_Copernicus = Cop_dataset.rename('GLC_Copernicus').toFloat();
var landcover_CORINE = landCover.rename('LC_CORINE').toFloat();

// combine into single image
var merged_image = climate
      .addBands(precipitation)
      .addBands(landcover_Copernicus)
      .addBands(landcover_CORINE)

// Sample pixel values for each point, using SampleRegions: (requires: Image, points)
var sampled = merged_image.sampleRegions({
  collection: fc_points, // is a feature collection
  properties: ['ID'], // List of column names of sample points inside feature collection   (not compulsory).
  scale:30  // spatial resolution of band or image
});

print("Sample_Region", sampled);

var sampled2 = fc_points.toList(fc_points.size()).map(function (feat) {
  
  return ee.Algorithms.If(ee.FeatureCollection(merged_image.sample(ee.Feature(feat).geometry(), 30)).first(),
                          ee.FeatureCollection(merged_image.sample(ee.Feature(feat).geometry(), 30)).first(), 
                          ee.Feature(feat).set({'GLC_Copernicus': 'null',
                                                'KG_climate': 'null',
                                                'LC_CORINE': 'null',
                                                'WC_mean_precipitation_mmyr': 'null'
                          }));
  
});

print("sampled2", sampled2);

Export.table.toDrive({
  collection: ee.FeatureCollection(sampled2),
  description: 'sample_example_pt',
  fileFormat: 'CSV'
});

After running above script in GEE code editor, the task produces a CSV file with these characteristics:

enter image description here

Masked pixels properties are printed as null but, you can use -99; as it was above proposed.