Python ee.Date() Examples

The following are 30 code examples of ee.Date(). You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may also want to check out all available functions/classes of the module ee , or try the search function .
Example #1
Source File: set_image_properties.py    From qgis-earthengine-examples with MIT License 7 votes vote down vote up
def addDate(image):
    # parse date stored in 'system:index'
    date = ee.Date(image.get('system:index'))

    # format date, see http:#www.joda.org/joda-time/apidocs/org/joda/time/format/DateTimeFormat.html
    str = date.format('YYYY-mm-dd')

    return image.set({'Date': str})


# point = ee.Geometry.Point(-122.262, 37.8719)
# start = ee.Date('2014-06-01')
# finish = ee.Date('2014-10-01')

# filteredCollection = ee.ImageCollection('LANDSAT/LC08/C01/T1') \
#     .filterBounds(point) \
#     .filterDate(start, finish) \
#     .sort('CLOUD_COVER', True) 
Example #2
Source File: algorithms.py    From gee_tools with MIT License 6 votes vote down vote up
def unmask_slc_off(image, optical_bands='B.+'):
        """ Unmask pixels that were affected by scl-off  error in Landsat 7

        Expects a Landsat 7 image and it is meant to be used before any other
        masking, otherwise this could affect the previous mask.

        The parameter `optical_bands` must be used if band were renamed. By
        default it is `B.+` which means that optical bands are those starting
        with B: B1 (blue), B2 (green), B3 (red), B4 (nir), B5 (swir),
        B6 (thermal), B7 (swir2).
        """
        idate = image.date()
        slcoff = ee.Date('2003-05-31')
        condition = idate.difference(slcoff, 'days').gte(0)

        def compute(i):
            mask = i.mask()
            reduced = mask.select(optical_bands).reduce('sum')
            slc_off = reduced.eq(0)
            unmasked = i.unmask()
            newmask = mask.where(slc_off, 1)
            return unmasked.updateMask(newmask)

        return ee.Image(ee.Algorithms.If(condition, compute(image), image)) 
Example #3
Source File: atmospheric.py    From ee-atmcorr-timeseries with Apache License 2.0 6 votes vote down vote up
def round_month(date):
    """
    round date to closest month
    """
    # start of THIS month
    m1 = date.fromYMD(date.get('year'),date.get('month'),ee.Number(1))
    
    # start of NEXT month
    m2 = m1.advance(1,'month')
      
    # difference from date
    d1 = ee.Number(date.difference(m1,'day')).abs()
    d2 = ee.Number(date.difference(m2,'day')).abs()
    
    # return closest start of month
    return ee.Date(ee.Algorithms.If(d2.gt(d1),m1,m2)) 
Example #4
Source File: test_c_image.py    From openet-ssebop-beta with Apache License 2.0 6 votes vote down vote up
def toa_image(red=0.1, nir=0.9, bt=305):
    """Construct a fake Landsat 8 TOA image with renamed bands"""
    mask_img = ee.Image(f'{COLL_ID}/{SCENE_ID}').select(['B3']).multiply(0)
    return ee.Image([mask_img.add(red), mask_img.add(nir), mask_img.add(bt)]) \
        .rename(['red', 'nir', 'tir'])\
        .set({
            'system:time_start': SCENE_TIME,
            'k1_constant': ee.Number(607.76),
            'k2_constant': ee.Number(1260.56),
        })
    # return ee.Image.constant([red, nir, bt])\
    #     .rename(['red', 'nir', 'lst']) \
    #     .set({
    #         'system:time_start': ee.Date(SCENE_DATE).millis(),
    #         'k1_constant': ee.Number(607.76),
    #         'k2_constant': ee.Number(1260.56),
    #     }) 
Example #5
Source File: ee-atmcorr-coefficients-timeseries.py    From ee-atmcorr-timeseries with Apache License 2.0 6 votes vote down vote up
def atm_corr_image(imageInfo: dict) -> dict:
    """Retrieves atmospheric params from image.

    imageInfo is a dictionary created from an ee.Image object
    """
    atmParams = {}
    # Python uses seconds, EE uses milliseconds:
    scene_date = datetime.datetime.utcfromtimestamp(imageInfo['system:time_start']/1000)
    dt1 = ee.Date(str(scene_date).rsplit(sep=' ')[0])

    atmParams['doy'] = scene_date.timetuple().tm_yday
    atmParams['solar_z'] = imageInfo['MEAN_SOLAR_ZENITH_ANGLE']
    atmParams['h2o'] = Atmospheric.water(geom, dt1).getInfo()
    atmParams['o3'] = Atmospheric.ozone(geom, dt1).getInfo()
    atmParams['aot'] = Atmospheric.aerosol(geom, dt1).getInfo()

    return atmParams 
Example #6
Source File: scores.py    From geebap with GNU General Public License v3.0 6 votes vote down vote up
def map(self, collection, **kwargs):
        """ Map function to use in BAP

        :param year: the analysing year. Must match the year of the bap
        :type year: int
        """
        range_out = self.range_out
        year = kwargs.get('year')
        date_range = self.season.add_year(year)
        doy = ee.Date(season_module.SeasonDate(self.best_doy).add_year(year))
        doy2 = ee.Date(season_module.SeasonDate(self.best_doy).add_year(year-1))
        condition = date_range.contains(doy)
        best = ee.Number(ee.Algorithms.If(condition, doy, doy2))

        return self.apply(collection, best_doy=best, name=self.name,
                          output_min=range_out[0], output_max=range_out[1],
                          function=self.function, stretch=self.stretch) 
Example #7
Source File: test_c_image.py    From openet-ssebop-beta with Apache License 2.0 6 votes vote down vote up
def test_Image_tcorr_scene_source(tcorr_source, tmax_source, scene_id,
                                  expected, tol=0.000001):
    """Test getting Tcorr value and index for a single date at a real point"""
    scene_date = datetime.datetime.strptime(scene_id.split('_')[-1], '%Y%m%d') \
        .strftime('%Y-%m-%d')
    input_image = ee.Image.constant(1).set({
        'system:index': scene_id,
        'system:time_start': ee.Date(scene_date).millis()
    })
    tcorr_img = ssebop.Image(
        input_image, tcorr_source=tcorr_source,
        tmax_source=tmax_source, tmax_resample='nearest').tcorr

    # Tcorr images are constant images and need to be queried at a point
    tcorr = utils.point_image_value(tcorr_img, SCENE_POINT)
    index = utils.getinfo(tcorr_img.get('tcorr_index'))
    assert abs(tcorr['tcorr'] - expected[0]) <= tol
    assert index == expected[1]


# Note, this is calling .tcorr_stats(), which isn't tested till the end.
# I'm not sure if it would make more sense to move this to the end or maybe
#   move the .tcorr_stats test further up, since they are independent of most
#   of the other functions. 
Example #8
Source File: season.py    From geebap with GNU General Public License v3.0 6 votes vote down vote up
def add_year(self, year):
        year = ee.Number(year)
        if self.over_end:
            start_year = year.subtract(1)
        else:
            start_year = ee.Number(year)
        end_year = ee.Number(year)

        sday = self.start.day
        eday = self.end.day

        # look for feb 29h in non leap
        if not is_leap(year):
            if self.start.month == 2 and sday == 29:
                sday = 28
            if self.end.month == 2 and eday == 29:
                eday = 28

        start = ee.Date.fromYMD(start_year, self.start.month, sday)
        end = ee.Date.fromYMD(end_year, self.end.month, eday)
        daterange = ee.DateRange(ee.Date(start), ee.Date(end))
        return daterange 
Example #9
Source File: dispatcher.py    From gee_tools with MIT License 6 votes vote down vote up
def dispatch(eeobject):
    """ General dispatcher """
    if belongToEE(eeobject):
        # DISPATCH!!
        if isinstance(eeobject, (ee.Image,)):
            return dispatchImage(eeobject)
        elif isinstance(eeobject, (ee.Date,)):
            return dispatchDate(eeobject)
        elif isinstance(eeobject, (ee.DateRange,)):
            return dispatchDaterange(eeobject)
        # ADD MORE ABOVE ME!
        else:
            info = eeobject.getInfo()
            return info
    else:
        info = str(eeobject)
        return info 
Example #10
Source File: utils.py    From openet-ssebop-beta with Apache License 2.0 6 votes vote down vote up
def date_to_time_0utc(date):
    """Get the 0 UTC time_start for a date

    Parameters
    ----------
    date : ee.Date

    Returns
    -------
    ee.Number

    """
    return ee.Date.fromYMD(date.get('year'), date.get('month'),
                           date.get('day')).millis()
    # Extra operations are needed since update() does not set milliseconds to 0.
    # return date.update(hour=0, minute=0, second=0).millis()\
    #     .divide(1000).floor().multiply(1000) 
Example #11
Source File: bap.py    From geebap with GNU General Public License v3.0 6 votes vote down vote up
def make_proxy(self, image, collection, year):
        """ Make a proxy collection """

        size = collection.size()

        # unmask all bands
        unmasked = image.unmask()

        proxy_date = ee.Date('{}-01-01'.format(year))

        bands = image.bandNames()
        empty = tools.image.empty(0, bands)
        proxy = unmasked.where(unmasked, empty)

        proxy = proxy.set('system:time_start', proxy_date.millis())

        proxy_col = ee.ImageCollection.fromImages([proxy])

        return ee.ImageCollection(ee.Algorithms.If(size.gt(0),
                                                   collection,
                                                   proxy_col)) 
Example #12
Source File: date.py    From gee_tools with MIT License 6 votes vote down vote up
def fromDOY(doy, year):
    """ Creat a ee.Date given a Day of Year and a Year """
    def less10(doy):
        doy = doy.toInt()
        return ee.String('00').cat(ee.Number(doy).format())

    def less100(doy):
        doy = doy.toInt()
        return ee.String('0').cat(ee.Number(doy).format())

    doy = ee.Number(doy).add(1).toInt()
    year_str = ee.Number(year).format()
    doy_str = ee.Algorithms.If(doy.lt(10), less10(doy),
                               ee.String(ee.Algorithms.If(doy.lt(100), less100(doy), doy.format())))
    s = ee.String(doy_str).cat(year_str)

    return ee.Date.parse('DDDyyyy', s) 
Example #13
Source File: _test_date.py    From gee_tools with MIT License 6 votes vote down vote up
def test_daterange_list():
    start_date = ee.Date('2010-01-01')
    end_date = ee.Date('2015-01-01')
    unit = 'year'
    interval = 1

    expected = ee.List([
        ee.DateRange('2010-01-01', '2011-01-01'),
        ee.DateRange('2011-01-01', '2012-01-01'),
        ee.DateRange('2012-01-01', '2013-01-01'),
        ee.DateRange('2013-01-01', '2014-01-01'),
        ee.DateRange('2014-01-01', '2015-01-01')
    ])

    daterange_list = tools.date.daterangeList(start_date, end_date,
                                              interval, unit)

    assert expected == daterange_list 
Example #14
Source File: lake_measure.py    From CrisisMappingToolkit with Apache License 2.0 5 votes vote down vote up
def get_image_collection(bounds, start_date, end_date):
    '''Retrieve Landsat 5 imagery for the selected location and dates'''
    # ee_bounds = apply(ee.Geometry.Rectangle, bounds)
    # ee_points = map(ee.Geometry.Point, [(bounds[0], bounds[1]), (bounds[0], bounds[3]),
    #                 (bounds[2], bounds[1]), (bounds[2], bounds[3])])
    global possibleSensors
    l5s = ee.ImageCollection(ee.Algorithms.If(possibleSensors.contains('L5'),getCollection('L5',bounds,start_date, end_date),getCollection('L5',bounds,ee.Date('1000-01-01'),ee.Date('1001-01-01'))))
    #l7s = ee.ImageCollection(ee.Algorithms.If(possibleSensors.contains('L7'),getCollection('L7',bounds,start_date, end_date),getCollection('L7',bounds,ee.Date('1000-01-01'),ee.Date('1001-01-01'))))
    l8s = ee.ImageCollection(ee.Algorithms.If(possibleSensors.contains('L8'),getCollection('L8',bounds,start_date, end_date),getCollection('L8',bounds,ee.Date('1000-01-01'),ee.Date('1001-01-01'))))
    ls = ee.ImageCollection(l5s.merge(l8s))
    # Clips image to rectangle around buffer. Thought this would free-up memory by reducing image size, but it doesn't
    # seem too :(
    # rect = bounds.bounds().getInfo()
    # ls = ls.map(lambda img: img.clip(rect))
    return ls 
Example #15
Source File: test_b_landsat.py    From openet-ssebop-beta with Apache License 2.0 5 votes vote down vote up
def toa_image(red=0.1, nir=0.9, bt=305):
    """Construct a fake Landsat 8 TOA image with renamed bands"""
    return ee.Image.constant([red, nir, bt])\
        .rename(['red', 'nir', 'tir']) \
        .set({
            # 'system:time_start': ee.Date(SCENE_DATE).millis(),
            'k1_constant': ee.Number(607.76),
            'k2_constant': ee.Number(1260.56),
        }) 
Example #16
Source File: modis_lake_measure.py    From CrisisMappingToolkit with Apache License 2.0 5 votes vote down vote up
def lineToDict(line):
        '''Extract the information from a single line in the log file in to a dictionary object'''

        MAX_ALGS = 15  # Used for sanity check
        NUM_HEADER_VALS = 2  # Date, MODIS
        ELEMENTS_PER_ALGORITHM = 4  # (alg name, precision, recall, eval_res)
        thisDict = dict()

        parts = line.split(',')
        numAlgs = (len(parts) - NUM_HEADER_VALS) / ELEMENTS_PER_ALGORITHM  # Date, MODIS, (alg name, precision, recall, eval_res)...
        if numAlgs > MAX_ALGS:  # Error checking
            print line
            raise Exception('Error: Too many algorithms found!')
        thisDict['date'] = parts[0]
        thisDict['satellite'] = parts[1]
        for i in range(numAlgs):  # Loop through each algorithm
            startIndex = i*ELEMENTS_PER_ALGORITHM + NUM_HEADER_VALS
            algName = parts[startIndex].strip()
            if (parts[startIndex+1].strip() == 'NA'):  # Check if this was logged as a failure
                thisDict[algName] = False
            else:  # Get the successful log results
                precision = float(parts[startIndex+1])
                recall = float(parts[startIndex+2])
                evalRes = float(parts[startIndex+3])
                thisDict[algName] = (precision, recall, evalRes)  # Store the pair of results for the algorithm
        return thisDict 
Example #17
Source File: test_b_landsat.py    From openet-ssebop-beta with Apache License 2.0 5 votes vote down vote up
def sr_image(red=1000, nir=9000, bt=305):
    """Construct a fake Landsat 8 TOA image with renamed bands"""
    return ee.Image.constant([red, nir, bt])\
        .rename(['red', 'nir', 'tir']) \
        .set({
            # 'system:time_start': ee.Date(SCENE_DATE).millis(),
            'k1_constant': ee.Number(607.76),
            'k2_constant': ee.Number(1260.56),
        }) 
Example #18
Source File: date_test.py    From aqua-monitor with GNU Lesser General Public License v3.0 5 votes vote down vote up
def testDate(self):
    """Verifies date constructors."""

    datefunc = ee.ApiFunction.lookup('Date')

    d1 = ee.Date('2000-01-01')
    d2 = ee.Date(946684800000)
    d3 = ee.Date(datetime.datetime(2000, 1, 1))
    d4 = ee.Date(d3)
    dates = [d1, d2, d3, d4]

    for d in dates:
      self.assertTrue(isinstance(d, ee.Date))
      self.assertEquals(datefunc, d.func)

    self.assertEquals(d1.args, {'value': '2000-01-01'})
    for d in dates[1:]:
      self.assertEquals(d.args['value'], 946684800000)

    d5 = ee.Date(ee.CustomFunction.variable('Date', 'foo'))
    self.assertTrue(isinstance(d5, ee.Date))
    self.assertTrue(d5.isVariable())
    self.assertEquals('foo', d5.varName)

    # A non-date variable.
    v = ee.CustomFunction.variable('Number', 'bar')
    d6 = ee.Date(v)
    self.assertTrue(isinstance(d6, ee.Date))
    self.assertFalse(d6.isVariable())
    self.assertEquals(datefunc, d6.func)
    self.assertEquals({'value': v}, d6.args)

    # A non-date ComputedObject, promotion and casting.
    obj = ee.ApiFunction.call_('DateRange', 1, 2)
    d7 = ee.Date(obj)
    self.assertTrue(isinstance(d7, ee.Date))
    self.assertEquals(datefunc, d7.func)
    self.assertEquals({'value': obj}, d7.args) 
Example #19
Source File: test_a_utils.py    From openet-ssebop-beta with Apache License 2.0 5 votes vote down vote up
def test_date_to_time_0utc(input, expected):
    input_img = ee.Date(input)
    assert utils.getinfo(utils.date_to_time_0utc(input_img)) == expected 
Example #20
Source File: test_c_image.py    From openet-ssebop-beta with Apache License 2.0 5 votes vote down vote up
def test_Image_dt_source_calculated(dt_source, elev, date, xy, expected, tol=0.001):
    """Test getting calculated dT values for a single date at a real point"""
    m = default_image_obj(dt_source=dt_source, elev_source=elev)
    # Start/end date are needed to filter the source collection
    m._start_date = ee.Date.parse('yyyy-MM-dd', date)
    m._end_date = ee.Date.parse('yyyy-MM-dd', date).advance(1, 'day')
    # DOY is needed in dT calculation
    m._doy = ee.Date.parse('yyyy-MM-dd', date).getRelative('day', 'year')\
        .int().add(1)
    output = utils.point_image_value(ee.Image(m.dt), xy)
    assert abs(output['dt'] - expected) <= tol 
Example #21
Source File: test_c_image.py    From openet-ssebop-beta with Apache License 2.0 5 votes vote down vote up
def test_Image_tmax_fallback(tmax_source, xy, expected, tol=0.001):
    """Test getting Tmax median value when daily doesn't exist

    To test this, move the test date into the future
    Tmax collections are filtered based on start_date and end_date
    """
    m = default_image_obj(tmax_source=tmax_source)
    m._start_date = ee.Date.fromYMD(2099, 7, 1)
    m._end_date = ee.Date.fromYMD(2099, 7, 2)
    output = utils.point_image_value(ee.Image(m.tmax), xy)
    assert abs(output['tmax'] - expected) <= tol 
Example #22
Source File: test_c_image.py    From openet-ssebop-beta with Apache License 2.0 5 votes vote down vote up
def test_Image_tcorr_scene_month(expected=[0.9700734316155846, 1], tol=0.000001):
    """Test getting monthly Tcorr from composite when daily is missing"""
    # Setting start date to well before beginning of daily Tcorr images
    input_image = ee.Image.constant(1).set({
        'system:index': 'LC08_042035_20150713',
        'system:time_start': ee.Date('1980-07-13').millis()})
    m = ssebop.Image(
        input_image, tcorr_source='SCENE', tmax_source='DAYMET_MEDIAN_V2',
        tmax_resample='nearest')
    tcorr_img = m.tcorr
    tcorr = utils.point_image_value(tcorr_img, SCENE_POINT)
    index = utils.getinfo(tcorr_img.get('tcorr_index'))
    assert abs(tcorr['tcorr'] - expected[0]) <= tol
    assert index == expected[1] 
Example #23
Source File: test_c_image.py    From openet-ssebop-beta with Apache License 2.0 5 votes vote down vote up
def test_Image_tcorr_scene_annual(expected=[0.9762643456613403, 2], tol=0.000001):
    """Test getting annual Tcorr from composite when monthly/daily are missing"""
    input_image = ee.Image.constant(1).set({
        'system:index': 'LC08_042035_20150713',
        'system:time_start': ee.Date('1980-07-13').millis()})
    m = ssebop.Image(
        input_image, tcorr_source='SCENE', tmax_source='DAYMET_MEDIAN_V2',
        tmax_resample='nearest')
    m._month = ee.Number(9999)
    tcorr_img = m.tcorr
    tcorr = utils.point_image_value(tcorr_img, SCENE_POINT)
    index = utils.getinfo(tcorr_img.get('tcorr_index'))
    assert abs(tcorr['tcorr'] - expected[0]) <= tol
    assert index == expected[1]


# CGM - I'm not quite sure how to test this condition
# def test_Image_tcorr_scene_default(expected=[0.978, 3], tol=0.0001):
#     """Test getting default Tcorr from composite"""
#     input_image = ee.Image.constant(1).set({
#         'system:index': 'LC08_042035_20150713',
#         'system:time_start': ee.Date('1980-07-13').millis()})
#     m = ssebop.Image(input_image, tcorr_source='SCENE',
#                      tmax_source='DAYMET_MEDIAN_V2')
#     m._month = ee.Number(9999)
#     tcorr_img = m.tcorr
#     tcorr = utils.point_image_value(tcorr_img, SCENE_POINT)
#     index = utils.getinfo(tcorr_img.get('tcorr_index'))
#     assert abs(tcorr['tcorr'] - expected[0]) <= tol
#     assert index['index'] == expected[1] 
Example #24
Source File: dispatcher.py    From gee_tools with MIT License 5 votes vote down vote up
def dispatchDate(date):
    """ Dispatch a ee.Date """
    info = date.format().getInfo()

    return info 
Example #25
Source File: date.py    From gee_tools with MIT License 5 votes vote down vote up
def toDatetime(date):
    """ convert a `ee.Date` into a `datetime` object """
    formatted = date.format('yyyy,MM,dd,HH,mm,ss').getInfo()
    args = formatted.split(',')
    intargs = [int(arg) for arg in args]
    return datetime(*intargs) 
Example #26
Source File: date.py    From gee_tools with MIT License 5 votes vote down vote up
def daterangeIntervals(start_date, end_date, interval=1, unit='month',
                       date_range=(1, 1), date_range_unit='day',
                       direction='backward'):
    """ Make Date Ranges in equal intervals

    :param interval: the amount of the interval in `unit`
    :param unit: the unit of the interval
    :param date_range: the amount of `date_range_unit` to go
        backwards (item 0) and forward (item 1)
    :param date_range_unit: the unit for date_range
    :param direction: the direction to advance
    """
    start_date = ee.Date(start_date)
    end_date = ee.Date(end_date)
    amplitude = end_date.difference(start_date, unit).round()
    intervals = amplitude.divide(interval).toInt()
    proxy = ee.List.sequence(0, intervals)

    if direction == 'forward':
        dates = proxy.map(
            lambda i: start_date.advance(
                ee.Number(i).multiply(interval), unit))
    else:
        dates = proxy.map(
            lambda i: end_date.advance(
                ee.Number(i).multiply(-interval), unit))

    def make_drange(date):
        date = ee.Date(date)
        return ee.DateRange(
            date.advance(-date_range[0], date_range_unit),
            date.advance(date_range[1], date_range_unit))

    return dates.map(lambda d: make_drange(d)) 
Example #27
Source File: date.py    From gee_tools with MIT License 5 votes vote down vote up
def unitSinceEpoch(date, unit='day'):
    """ Return the number of units since the epoch (1970-1-1)

    :param date: the date
    :type date: ee.Date
    :param unit: one of 'year', 'month' 'week', 'day', 'hour', 'minute',
        or 'second'
    :return: the corresponding units from the epoch
    :rtype: ee.Number
    """
    epoch = ee.Date(EE_EPOCH.isoformat())
    return date.difference(epoch, unit).toInt() 
Example #28
Source File: cdd_simple.py    From coded with MIT License 5 votes vote down vote up
def makeVariables_gv(image):
  """ Make variables for GV regression model """
  year = ee.Image(image.date().difference(ee.Date('1970-01-01'), 'year')) 
  season = year.multiply(2 * np.pi)
  return image.select().addBands(ee.Image(1)).addBands(
    season.sin().rename(['sin'])).addBands(
    season.cos().rename(['cos'])).addBands(
    image.select(['band_0'])).toFloat() 
Example #29
Source File: array_transformations.py    From qgis-earthengine-examples with MIT License 5 votes vote down vote up
def makeVariables(image):
  # Compute time of the image in fractional years relative to the Epoch.
  year = ee.Image(image.date().difference(ee.Date('1970-01-01'), 'year'))
  # Compute the season in radians, one cycle per year.
  season = year.multiply(2 * math.pi)
  # Return an image of the predictors followed by the response.
  return image.select() \
    .addBands(ee.Image(1)) \
    .addBands(year.rename('t')) \
    .addBands(season.sin().rename('sin')) \
    .addBands(season.cos().rename('cos')) \
    .addBands(image.normalizedDifference().rename('NDVI')) \
    .toFloat()

# Load a Landsat 5 image collection. 
Example #30
Source File: linear_regression.py    From qgis-earthengine-examples with MIT License 5 votes vote down vote up
def addBand(image):
    date = ee.Date(image.get('system:time_start'))
    yearOffset = date.difference(ee.Date(start), 'year')
    ndvi = image.normalizedDifference(['B4', 'B3'])
    return ee.Image(1).addBands(yearOffset).addBands(ndvi).toDouble()