getDownloadURL()
is a client-side function that is intended for quick, interactive download of a sample region. Say for instance your app includes a very complex algorithm applied to high resolution data and a user requests a global download - this task would not be able to run interactively in the browser, which is why the limitation is in place.
A data service app is best build using a custom app, where a user can log into their Earth Engine account and export data as an asset to their own account or Google Cloud Storage.
Since 1196
should give the millis for 2022-04-11
, it seems like you want to exclude leap days altogether. Is this the case?
If you exclude leap days, it's quite simple:
function toMillis(day) {
var dayOffset = ee.Date('2019-01-01')
.difference(ee.Date('1970-01-01'), 'days')
.subtract(1)
return day.add(dayOffset)
.multiply(1000) // Second to millis
.multiply(3600) // Hour to second
.multiply(24) // Day to hour
}
Otherwise, it's tricky. Since you need to do date manipulations on an image, you cannot rely on ee.Date
, so you have to manually deal with the date logic, which is notoriously easy to get wrong. Anyway, below is my stab at implementing something like this. I'm sure there are problems with the implementation, it certainly it has some assumptions that causes this to fail if your date is too far in the future (2070s).
function toMillis(day) {
var referenceYear = ee.Number(2019)
// Could be different due to leap years.
var approximateYear = day.subtract(1).divide(365).floor().add(referenceYear)
var leapYears = ee.Image(ee.Array( // Array image with leap years
ee.List.sequence(referenceYear, 2070)
.map(function isLeapYear(year) {
year = ee.Number(year)
return ee.Date.fromYMD(year.add(1), 1, 1).advance(-1, 'day') // Last of year
.getRelative('day', 'year').gt(364) // Got the extra day
.multiply(year) // Return the actual year if a leap year or 0
})
.filter(ee.Filter.neq('item', 0))
))
var numberOfLeapYears = leapYears
.arrayMask(leapYears.lte(approximateYear)) // Mask out leap years after the year of the pixel
.arrayLength(0) // Results in an image where the pixel value represents the number of leap years
var leapYearAdjustedDay = day.add(numberOfLeapYears)
var year = leapYearAdjustedDay.subtract(1).divide(365).floor().add(referenceYear)
var isLeapYear = leapYears.arrayMask(leapYears.eq(year))
.arrayLength(0)
var januaryFirstDay = year.subtract(referenceYear).multiply(365).add(numberOfLeapYears).add(1).subtract(isLeapYear)
var dayOfYear = leapYearAdjustedDay.subtract(januaryFirstDay).add(1)
var isBeforeLeapDay = dayOfYear.lte(31 + 29)
var timeOfYearAdjustment = isLeapYear.and(isBeforeLeapDay) // 1 if before leap day on leap year, otherwise 0
var finalDay = leapYearAdjustedDay
.subtract(timeOfYearAdjustment)
var dayOffset = ee.Date.fromYMD(referenceYear, 1, 1)
.difference(ee.Date('1970-01-01'), 'days')
.subtract(1)
return finalDay.add(dayOffset)
.multiply(1000) // Second to millis
.multiply(3600) // Hour to second
.multiply(24) // Day to hour
}
testDate(toMillis(ee.Image(1)), '2019-01-01')
testDate(toMillis(ee.Image(2)), '2019-01-02')
testDate(toMillis(ee.Image(3)), '2019-01-03')
testDate(toMillis(ee.Image(365)), '2019-12-31')
testDate(toMillis(ee.Image(366)), '2020-01-01')
testDate(toMillis(ee.Image(423)), '2020-02-27')
testDate(toMillis(ee.Image(424)), '2020-02-28')
testDate(toMillis(ee.Image(425)), '2020-03-01')
testDate(toMillis(ee.Image(426)), '2020-03-02')
testDate(toMillis(ee.Image(730)), '2020-12-31')
testDate(toMillis(ee.Image(731)), '2021-01-01')
testDate(toMillis(ee.Image(732)), '2021-01-02')
testDate(toMillis(ee.Image(1196)), '2022-04-11')
function testDate(millisImage, expectedDate) {
var millis = millisImage
.reduceRegion(ee.Reducer.first(), ee.Geometry.Point([0, 0]), 1)
.values()
.getNumber(0)
var date = ee.Date(millis).format('yyyy-MM-dd')
var valid = millis.eq(ee.Date(expectedDate).millis())
print(
ee.Algorithms.If(valid,
'Success: ' + expectedDate,
ee.String('Failed: Expected ' + expectedDate + ' was ').cat(date)
)
)
}
https://code.earthengine.google.com/16a23b3554e90c869c999de09e2fde90
Best Answer
Okay, so here is a way that should work. Probably not the fastest or cleanest, but the advantage is that you don't have to implement messy date logic with leap years or leap seconds yourself.
I'm basically building a lookup table for a desired time period and remapping the values in the image.