Apart from the problems @kuik points out, I'm wondering if you're calculating SVI correctly. I've never done it myself, but look at this article:
The SVI is a z-score deviation from the mean in units of the standard
deviation, calculated from the NDVI or EVI values for each pixel
location of a composite period for each year during a given reference
period. The equation below shows the general calculation of the SVI
It seems like you should calculate your mean and standard deviation by pixel over time, not over your region. Here's my take on this, which might not be correct, but reflects my understanding of the above article:
var region = ee.Geometry.Polygon([
[-2.2535247523754443, 5.881894109545312],
[-0.31993100237544425, 5.881894109545312],
[-0.31993100237544425, 9.669268383910472],
[-2.2535247523754443, 5.881894109545312]
])
var startDate = ee.Date('2000-01-01')
var endDate = ee.Date('2020-01-01')
var collection = ee.ImageCollection("MODIS/006/MOD13Q1")
.filterDate(startDate, endDate)
.filterBounds(region)
.map(maskClouds)
.select(['EVI'])
var deltaDays = 7
var statsCollection = ee.ImageCollection(
ee.List.sequence(0, 365, deltaDays).map(calculateStats)
)
var sviCollection = collection.map(toSVI)
print(sviCollection)
Map.centerObject(region, 9)
var first = sviCollection
.first()
.clip(region)
var sviVis = {
min: -2,
max: 2,
palette: ["ffffff", "ce7e45", "df923d", "f1b555", "fcd163", "99b718", "74a901", "66a000", "529400", "3e8601", "207401", "056201", "004c00", "023b01", "012e01", "011d01", "011301"]
}
Map.addLayer(first, sviVis, 'first')
function calculateStats(startDOY) {
var endDOY = ee.Number(startDOY).add(deltaDays)
return collection
.filter(ee.Filter.calendarRange(startDOY, endDOY, 'day_of_year'))
.reduce(ee.Reducer.stdDev().combine(ee.Reducer.mean(), null, true))
.set('startDOY', startDOY)
.set('endDOY', endDOY)
}
function toSVI(image) {
var doy = image.date().getRelative('day', 'year')
var stats = statsCollection
.filter(ee.Filter.lte('startDOY', doy))
.filter(ee.Filter.gt('endDOY', doy))
.first()
return image
.expression(
'(evi - mean) / stdDev', {
evi: image,
mean: stats.select('EVI_mean'),
stdDev: stats.select('EVI_stdDev')
})
.rename('svi')
.copyProperties(image, image.propertyNames())
}
function maskClouds(image) {
var qa = image.select('DetailedQA')
// https://developers.google.com/earth-engine/datasets/catalog/MODIS_006_MOD13Q1
var quality = bitwiseExtract(qa, 0, 1)
var usefulness = bitwiseExtract(qa, 2, 5)
var aerosolQuantity = bitwiseExtract(qa, 6, 7)
var adjacentCloudDetected = bitwiseExtract(qa, 8)
var atmosphereBrdfCorrection = bitwiseExtract(qa, 9)
var mixedClouds = bitwiseExtract(qa, 10)
var landWaterMask = bitwiseExtract(qa, 11, 13)
var possibleSnowIce = bitwiseExtract(qa, 14)
var possibleShadow = bitwiseExtract(qa, 15)
var mask = quality.lte(1) // VI produced, but check other QA
.and(usefulness.lte(2)) // Lower quality
.and(aerosolQuantity.lte(2)) // Intermediate
.and(adjacentCloudDetected.eq(0)) // No
.and(mixedClouds.eq(0)) // No
.and(possibleSnowIce.eq(0)) // No
.and(possibleShadow.eq(0)) // No
return image.updateMask(mask)
}
function bitwiseExtract(value, fromBit, toBit) {
if (toBit === undefined)
toBit = fromBit
var maskSize = ee.Number(1).add(toBit).subtract(fromBit)
var mask = ee.Number(1).leftShift(maskSize).subtract(1)
return value.rightShift(fromBit).bitwiseAnd(mask)
}
https://code.earthengine.google.com/1e552b88ad6f27f12acf58adcc01b8ba