2

I want to filter my ImageCollection for my analysis with specif time ranges. I have an Image Collection with annual max NDVI images ranging from 1982 - 2015 and a conflict FeatureCollection for a period from 1989 - 2015. I'm calculating slopes with a linearFit function before and after a conflict date. Now I want to introduce some conditions for the time ranges before and after the conflict.

  1. The slopes (NDVI trends) need to be calculated for at least 6 years before and 6 years after the conflict date (conflictDate) (to be able to calculate an accurate slope for the vegetation)
  2. The time range needs to be the same, e.g 7 years before and 7 after the conflict (but min 6 years)
  3. The time range should be as long as possbile (the maximum number of years). Because the longer the time range the more images used and the more accurate the slope. So basically it means if the conflict date is 1997. Then the beforeSlope time range is from 1982 to 1996 (15 years) and the afterSlope time range is from 1997 to 2011 (15 years).
//Dates for filtering the GIMMS NDVI Image Collection
var startDate = '1982-01-01'
var endDate = '2015-12-16'


//Loading and filtering (by date) the UCDP conflict data (CSV)  1989 - 2015
var conflicts = ee.FeatureCollection('users/welp1992/Conflicts25_Date')
  .filterMetadata('date_start', 'greater_than', '1988-01-01')//min 6 years, but as long as possible
  .filterMetadata('date_start', 'less_than', '2010-01-01')


//filter image GIMMS by date and band and add time 't' as a band  
var timeSeries = ee.ImageCollection('users/welp1992/GIMMS_Max_Annual_NDVI')
    .filterDate(startDate, endDate)
    .select(['b1'])
    .map(function (image) {
      return image.addBands(
        ee.Image(image.getNumber('system:time_start')) //Time past in millis. since 1970 for the composite (15 days) start period
          .subtract(ee.Date(startDate).millis())
          .divide(1000*3600) // year (*24*365 = days)
          .int() //casts the input value to a signed 32-bit integer
          .rename('t')
      )
    })

print(timeSeries,'time');



//Call function for adding slopes to conflicts feature  collection
var conflictsWithSlopes = conflicts.map(addSlopes)
print(conflictsWithSlopes.limit(500)) //(5815 conflicts)
print('slope change stats', conflictsWithSlopes.aggregate_stats('slopeChange'))
print('histogram', conflictsWithSlopes.aggregate_histogram('slopeChange'))
// Other aggregate_*() functions...

//Function to add slopes (3) to feature collection, while calling getSlope function
function addSlopes(conflict) {
  var conflictDate = conflict.getString('date_start')
  var beforeSlope = getSlope(timeSeries, conflict, startDate, conflictDate)
  var afterSlope = getSlope(timeSeries, conflict, conflictDate, endDate)
  return conflict
    .set('beforeSlope', beforeSlope)
    .set('afterSlope', afterSlope)
    .set('slopeChange', afterSlope.subtract(beforeSlope))
}

//Function to calculate slopes using linearFit and reduce GIMMS to intersections with the conflict geometry
function getSlope(timeSeries, conflict, startDate, endDate) {
  return timeSeries
    .select(['t', 'b1'])
    .filterDate(startDate, endDate)
    .reduce(ee.Reducer.linearFit()) //try other reducers ee.Reducer.linearRegression()
    .select('scale')
    .reduceRegion({ 
      reducer: ee.Reducer.mean(), //different reducers possible (first, min, max, mean)
      geometry: conflict.geometry(),
      scale: 8000
    })
    .getNumber('scale')
}

I'm not sure where to add these conditions. But I think in the getSlope function (bottom) I should introduce them. Somehow the 't' (as a time range) needs to be filtered so that the 'beforeSlope' time range equals the afterSlope time range. And the max. possible time range.

Code: https://code.earthengine.google.com/046eaa48b06f647f53f061583d00a0b3 Asset NDVI and Conflict: https://code.earthengine.google.com/?asset=users/welp1992/NDVI_Annual_Max_81_2015 https://code.earthengine.google.com/?asset=users/welp1992/Conflicts25_Date

2

You could just filter the conflict featureCollection to 6 years after the NDVI start date and 6 years before the end date. That should guarantee that all conflicts have a sufficient amount of data for the before and after slope.

Otherwise you could also count how many features are before and after the conflict. Afterwards you could filter on this data how many NDVI years should at least be before and after the conflict.

function addSlopes(conflict) {
  var conflictDate = conflict.getString('date_start')
  var beforeSlope = getSlope(timeSeries, conflict, startDate, conflictDate)
  var afterSlope = getSlope(timeSeries, conflict, conflictDate, endDate)
  var beforeCount = timeSeries.filterDate(startDate, conflictDate)
                              .count()
  var afterCount = timeSeries.filterDate(conflictDate, endDate)
                              .count()
  return conflict
    .set('beforeSlope', beforeSlope)
    .set('afterSlope', afterSlope)
    .set('slopeChange', afterSlope.subtract(beforeSlope))
    .set('beforeCount', beforeCount)
    .set('afterCount', afterCount)
}

EDIT:

Here's the code how you would get the same number of years before and after the conflict. It works with getting the min of the years before and after and using .advance() to set the appropriate time series.

I didn't test it, but it should work, if it doesn't work you can tell me what went wrong.

//Function to add slopes (3) to feature collection, while calling getSlope function
function addSlopes(conflict) {
  var conflictDate = ee.Date(conflict.getString('date_start'))
  
  // Get the minimum number of years
  var beforeCount = timeSeries.filterDate(startDate, conflictDate)
                              .size()
  var afterCount = timeSeries.filterDate(confilctDate, endDate)
                              .size()
  var minCount = afterCount.min(beforeCount)
  
  // Use .advance() to set the same number of years before and after
  var start = conflictDate.advance(minCount.multiply(-1), "year")
  var end = conflictDate.advance(minCount, "year")
  
  var beforeSlope = getSlope(timeSeries, conflict, start, conflictDate)
  var afterSlope = getSlope(timeSeries, conflict, conflictDate, end)
  
  return conflict
    .set('beforeSlope', beforeSlope)
    .set('afterSlope', afterSlope)
    .set('slopeChange', afterSlope.subtract(beforeSlope))
    .set('slopeYears', minCount)
}
7
  • Yes the first suggestion I have in my code already (as a first simple solution). But for statistics it's better to have the same periods for before and after the conflict. For the second suggestion. The features are distributed unevenly, so the count based on them would probably result in different amount of features and therefore years. But you mean that I set for a example a minimum number of years (6) and then just have the same amount of features before and after? How would I put in the minimum number of years in the code?
    – Mathias
    Jul 6 '20 at 6:59
  • Ah now I understand more clearly. I edited my answer to show how this could be done. If the answer works, you can accept it. If not, tell me what went wrong.
    – JonasV
    Jul 6 '20 at 8:54
  • Thank you for your help. Why exactly is the minCount.multiply(-1), it turns the minCount negative right? The .advance() argument seems to not work because the date is stored in a different format: Date.advance, argument 'delta': Invalid type. Expected type: Float. Actual type: Image<[b1, t]>.
    – Mathias
    Jul 6 '20 at 12:25
  • Yes it turns it negative. That's to go backwards from the conflict date. For the error you probably only habe to cast minCount to an ee.Number and then to float. I will have a look later. Or you can also try. It might just work with ee.Number(minCount).float()
    – JonasV
    Jul 6 '20 at 16:33
  • I tried to cast with var minCount = afterCount.min(beforeCount) var MinCount = ee.Number(minCount).float() and then changed the following minCount to MinCount and now it says float is an invalid type: Number.float, argument 'input': Invalid type. Expected type: Number. Actual type: Image<[b1, t]>.
    – Mathias
    Jul 7 '20 at 7:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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