0

I have a FeatureCollection with points loaded from an asset (shapefile). Since the system:index is empty, gee assigns a weird index to each feature (e.g. 000000000001, 0000000000a).Which causes problems when I export my Timeseries CSV file as I want it to show the Sample_ID found in my asset (shapefile) Sample ID in the asset like the picture below rather than the GEE assigned feature ID. Link to code https://code.earthengine.google.com/?noload=1&scriptPath=users%2Fmpalasimbarashe%2FProject1%3AKNDVI%2FTableWIth_Null

Image of Asset ID

/// IMPORTING THE FEATURE COLLECTION ///
var points = points.map(function(feature){
  return ee.Feature(feature.geometry(), {'id': feature.id()})
})
Map.addLayer(points,{color:'green'},'Root Locations')
Map.centerObject(points,10)
print(points)

/// FILTERING THE SENTINEL IMAGE COLLECTION ////
function maskCloudAndShadows(image){
  var cloudProb = image.select('MSK_CLDPRB');
  var snowProb = image.select('MSK_SNWPRB');
  var cloud = cloudProb.lt(5);
  var snow = snowProb.lt(5);
  var scl = image.select('SCL');
  var shadow = scl.eq(3); //3 = cloud shadow
  var cirrus = scl.eq(10); // 10 = cirrus
  
  var mask = (cloud.and(snow)).and(cirrus.neq(1)).and(shadow.neq(1));
  return image.updateMask(mask);
}

/// DATE RANGE FOR THE TIME SERIES ///
var startDate =  new Date ('2020-10-01')
var endDate =  new Date ('2021-05-30')



var addKNDVI = function(image){
  
  var RED = image.select('B4');
  var NIR = image.select('B8');
  
  /// COMPUTE D2 A RENAME TO d2 ///
  var D2 = NIR.subtract(RED).pow(2)
    .select([0],['d2']);
 
  /// GAMMA DEFINED AS 1/sigma^2
  var gamma = ee.Number(4e6).multiply(-2.0);

/// COMPUTE KERNEL (k) AND KNDVI ///

  var k = D2.divide(gamma).exp();
  var kndvi = ee.Image.constant(1)
    .subtract(k).divide(ee.Image.constant(1).add(k))
    .select([0],['kndvi']).clip(points);
    
     return image.addBands(ee.Image([kndvi]));
 
};

/// IMPORTING THE IMAGE COLLECTION

var collection = ee.ImageCollection('COPERNICUS/S2_SR')
                   .filterDate(startDate, endDate)
                   .map(maskCloudAndShadows)
                   .map(addKNDVI)
                   .filter(ee.Filter.bounds(points))
                   
/// VIEWING THE MAX IN THE COMPOSITE ////
 var vizParams = { bands: ['B4','B3','B2'] , min: 0, max: 2000}
 Map.addLayer(collection.max(),vizParams, 'collection')

var testPoint = ee.Feature(points.first())

//Map.centerObject(testPoint, 10)

/// TIMESERIES CHART FOR SINGLE LOCATION ///
var chart = ui.Chart.image.series({
    imageCollection: collection.select('kndvi'),
    region: testPoint.geometry()
    }).setOptions({
      interpolateNulls: true,
      lineWidth: 1,
      pointSize: 3,
      title: 'KNDVI over Time at a Single Location',
      vAxis: {title: 'KNDVI'},
      hAxis: {title: 'Date', format: 'YYYY-MMM', gridlines: {count: 12}}
    })
print(chart)

//// TIMESERIES CHART FOR MULTIPLE LOCATIONS ////
var chart = ui.Chart.image.seriesByRegion({
    imageCollection: collection.select('kndvi'),
    regions: points,
    reducer: ee.Reducer.mean()
})
print(chart)

/// HANDLING MASKED PIXELS ///
var triplets = collection.map(function(image) {
  return image.select('kndvi').reduceRegions({
    collection: points, 
    reducer: ee.Reducer.mean().setOutputs(['kndvi']), 
    scale: 10,
  })// reduceRegion doesn't return any output if the image doesn't intersect
    // with the point or if the image is masked out due to cloud
    // If there was no ndvi value found, we set the ndvi to a NoData value -9999
    .map(function(feature) {
    var kndvi = ee.List([feature.get('kndvi'), -9999])
      .reduce(ee.Reducer.firstNonNull())
    return feature.set({'kndvi': kndvi, 'imageID': image.id()})
    })
  }).flatten();
  
var format = function(table, rowId, colId) {
  var rows = table.distinct(rowId); 
  var joined = ee.Join.saveAll('matches').apply({
    primary: rows, 
    secondary: table, 
    condition: ee.Filter.equals({
      leftField: rowId, 
      rightField: rowId
    })
  });
         
  return joined.map(function(row) {
      var values = ee.List(row.get('matches'))
        .map(function(feature) {
          feature = ee.Feature(feature);
          return [feature.get(colId), feature.get('kndvi')];
        });
      return row.select([rowId]).set(ee.Dictionary(values.flatten()));
    });
};
var sentinelResults = format(triplets, 'id', 'imageID');

/// GRANULE OVERLAPS ///
var merge = function(table, rowId) {
  return table.map(function(feature) {
    var id = feature.get(rowId)
    var allKeys = feature.toDictionary().keys().remove(rowId)
    var substrKeys = ee.List(allKeys.map(function(val) { 
        return ee.String(val).slice(0,8)}
        ))
    var uniqueKeys = substrKeys.distinct()
    var pairs = uniqueKeys.map(function(key) {
      var matches = feature.toDictionary().select(allKeys.filter(ee.Filter.stringContains('item', key))).values()
      var val = matches.reduce(ee.Reducer.max())
      return [key, val]
    })
    return feature.select([rowId]).set(ee.Dictionary(pairs.flatten()))
  })
}
var sentinelMerged = merge(sentinelResults, 'id');


//// EXPORTING MULTIPLE LOCATION TIMESERIES ///
Export.table.toDrive({
    collection: sentinelMerged,
    description: 'Multiple_Locations_KNDVI_time_series',
    folder: 'earthengine',
    fileNamePrefix: 'Kndvi_time_series_multiple',
    fileFormat: 'CSV'
})

1 Answer 1

0

You can do that by using a generic function to remove this "weird" index property and, afterward, setting your desired id that identifies each feature. Code snippet looks as follows. Complete code is here.

// Generic Function to remove a property from a feature
var removeProperty = function(feat, property) {
  var properties = feat.propertyNames();
  var selectProperties = properties.filter(ee.Filter.neq('item', property));
  return feat.select(selectProperties);
};

// remove property id in each feature
sentinelMerged = sentinelMerged.map(function(feat) {
  feat = removeProperty(feat, 'id');
  return feat.set('id', ee.Feature(feat).id());
});

After running complete code in GEE code editor, I ran the task and CSV obtained file has corresponding id in desired way. It can be observed in following picture.

enter image description here

Editing Note:

For pairing 'id' and 'Sample_ID' you first need to create a dictionary at the beginning of the code as follows:

/// IMPORTING THE FEATURE COLLECTION ///
var points = points.map(function(feature){
  var sample_id = feature.get('Sample_ID');
  return ee.Feature(feature.geometry(), {'id': feature.id()})
    .set('Sample_ID', sample_id);
});

Map.addLayer(points,{color:'green'},'Root Locations');
Map.centerObject(points,10);
print(points);

var points_list = points.toList(points.size());

var keys = points_list.map(function (ele) {

  return ee.Feature(ele).get('id');
  
});

var values = points_list.map(function (ele) {

  return ee.Feature(ele).get('Sample_ID');
  
});

var dict = ee.Dictionary.fromLists(keys, values);

Afterward, you set 'Sample_ID' property in merge function as in following code snippet:

/// GRANULE OVERLAPS ///
var merge = function(table, rowId) {
  return table.map(function(feature) {
    var id = feature.get(rowId)
    var allKeys = feature.toDictionary().keys().remove(rowId)
    var substrKeys = ee.List(allKeys.map(function(val) { 
        return ee.String(val).slice(0,8)}
        ))
    var uniqueKeys = substrKeys.distinct()
    var pairs = uniqueKeys.map(function(key) {
      var matches = feature.toDictionary().select(allKeys.filter(ee.Filter.stringContains('item', key))).values()
      var val = matches.reduce(ee.Reducer.max())
      return [key, val]
    })
    
    var sample_id = dict.get(ee.Feature(feature).get('id'));
    
    return feature.select([rowId]).set(ee.Dictionary(pairs.flatten()))
                                  .set('Sample_ID', sample_id);
  });
};

Complete code can be accessed here.

2
  • Yes is there any way that the code can utilise the ID found on the asset, for example, the one in the picture I have now put above so that when the CSV is exported it uses the Sample_ID string?
    – Simba
    Commented Jul 19, 2022 at 8:02
  • 1
    Yes, there is a way. Please see my Editing Note.
    – xunilk
    Commented Jul 19, 2022 at 14:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.