Google Earth Engine – Combining Properties of Two FeatureCollections with Same Features

attribute-joinsgoogle-earth-engine

This is throwing me for a loop, yet it seems so simple. I have two FeatureCollections that have the same features (but different properties), as they were generated by reducing over the same ImageCollection. I would like to combine the properties of these two FeatureCollections into a single FeatureCollection for export.

Here is a dummy script I put together–I'm using the Python API and my actual use case is quite involved, so I'm just showing this simple case.

I have tried an inner join (returns 4 features rather than just two), and a merge() (also return 4 features instead of two)–both are shown in the example code. Neither produces the expected output: a FeatureCollection with two features (defined by the image ids) and two properties ('date', and 'npix').

I am guessing that one likely answer is to combine the mapped functions into a single function and return a feature that contains all the properties I want to combine, but my workflow is quite involved at this point and it would be much simpler if I could just join the FeatureCollections.

Here is a code snippet to illustrate where I am stuck:

var start_date = '2010-01-01';
var end_date = '2019-01-01';
var scale = 30;

var feat = test_rois.first()

// Filter sensor collection by date and region
L8 = L8.filterDate(start_date, end_date).filterBounds(feat.geometry());
L8 = ee.ImageCollection(L8.toList(2)) // only keep two images for testing

// Dummy function for getting image dates as features
var dateadd = function(image){
  var date = image.date().format('YYYY-MM-dd');
  return ee.Feature(null, {'date':date})
}

// Dummy function for computing total number of pixels within polygon
var n_total = function(img) {
  var npix = img.select('B1').reduceRegion({
  reducer: ee.Reducer.count(),
  geometry: feat.geometry(),
  scale: scale,
  });
  return ee.Feature(null, {'npix':npix.get('B1')})
};

// Map over the ImageCollection
var date_fc = L8.map(dateadd);
var npix_fc = L8.map(n_total);

// Look at results
print('Date Featurecollection', date_fc)
print('Npix Featurecollection', npix_fc)

// Attempt a join
var toyFilter = ee.Filter.equals({
  leftField: 'id',
  rightField: 'id',
});

var innerJoin = ee.Join.inner('primary', 'secondary');
var toyJoin = innerJoin.apply(date_fc, npix_fc, toyFilter);
print('Inner join toy example:', toyJoin);

// Attempt a merge
var merged = ee.FeatureCollection(date_fc).merge(npix_fc)
print('Merged:', merged)

Best Answer

The inner join looks for properties in your features but id is not set in properties. So what we can do is add a new property id to the features.

// Dummy function for getting image dates as features
var dateadd = function(image){
  var date = image.date().format('YYYY-MM-dd');
  return ee.Feature(null, {'date':date,'id':image.id()})
}

// Dummy function for computing total number of pixels within polygon
var n_total = function(img) {
  var npix = img.select('B1').reduceRegion({
  reducer: ee.Reducer.count(),
  geometry: feat.geometry(),
  scale: scale,
  });
  return ee.Feature(null, {'npix':npix.get('B1'),'id':img.id()})
};

After you do join it will give you a feature with two matching features based on your condition, which in this case is 'id', in two properties that you defined. So you can clean the result of join by taking one feature, say the one in primary, and copy over the properties of feature from secondary.

function cleanJoin(feature){
  return ee.Feature(feature.get('primary')).copyProperties(feature.get('secondary'));
}
print(toyJoin.map(cleanJoin));

Edit: Or as @RodrigoE.Principe mentioned below, you can use system:index to access your feature ids so all you'd have to change is the filter that is used on join to

// Attempt a join
var toyFilter = ee.Filter.equals({
  leftField: 'system:index',
  rightField: 'system:index',
});
Related Question