text: # coding=utf-8
  """ Module holding tools for ee.ImageCollections """
  import ee
  import ee.data
  
  if not ee.data._initialized:
      ee.Initialize()
  
  
  def fill_with_last(collection):
      """ Fill masked values of each image pixel with the last available
      value
  
      :param self: the self that holds the images that will be filled
      :type self: ee.ImageCollection
      :rtype: ee.ImageCollection
      """
  
      new = collection.sort('system:time_start', True)
      collist = new.toList(new.size())
      first = ee.Image(collist.get(0)).unmask()
      rest = collist.slice(1)
  
      def wrap(img, ini):
          ini = ee.List(ini)
          img = ee.Image(img)
          last = ee.Image(ini.get(-1))
          mask = img.mask().Not()
          last_masked = last.updateMask(mask)
          last2add = last_masked.unmask()
          img2add = img.unmask()
          added = img2add.add(last2add) \
              .set('system:index', ee.String(img.id()))
  
          props = img.propertyNames()
          condition = props.contains('system:time_start')
  
          final = ee.Image(ee.Algorithms.If(condition,
                                            added.set('system:time_start',
                                                      img.date().millis()),
                                            added))
  
          return ini.add(final.copyProperties(img))
  
      newcol = ee.List(rest.iterate(wrap, ee.List([first])))
      return ee.ImageCollection.fromImages(newcol)
  
  
  def get_values(collection, geometry, reducer=ee.Reducer.mean(), scale=None,
                 id='system:index', properties=None, side='server'):
      """ Return all values of all bands of an image collection in the
          specified geometry
  
      :param geometry: Point from where to get the info
      :type geometry: ee.Geometry
      :param scale: The scale to use in the reducer. It defaults to 10 due
          to the minimum scale available in EE (Sentinel 10m)
      :type scale: int
      :param id: image property that will be the key in the result dict
      :type id: str
      :param properties: image properties that will be added to the resulting
          dict
      :type properties: list
      :param side: 'server' or 'client' side
      :type side: str
      :return: Values of all bands in the ponit
      :rtype: dict
      """
      if not scale:
          # scale = minscale(ee.Image(self.first()))
          scale = 1
      else:
          scale = int(scale)
  
      propid = ee.Image(collection.first()).get(id).getInfo()
      def transform(eeobject):
          try: # Py2
              isstr = isinstance(propid, (str, unicode))
          except: # Py3
              isstr = isinstance(propid, (str))
  
          if isinstance(propid, (int, float)):
              return ee.Number(eeobject).format()
          elif isstr:
              return ee.String(eeobject)
          else:
              msg = 'property must be a number or string, found {}'
              raise ValueError(msg.format(type(propid)))
  
  
      if not properties:
          properties = []
      properties = ee.List(properties)
  
      def listval(img, it):
          theid = ee.String(transform(img.get(id)))
          values = img.reduceRegion(reducer, geometry, scale)
          values = ee.Dictionary(values)
          img_props = img.propertyNames()
  
          def add_properties(prop, ini):
              ini = ee.Dictionary(ini)
              condition = img_props.contains(prop)
              def true():
                  value = img.get(prop)
                  return ini.set(prop, value)
              # value = img.get(prop)
              # return ini.set(prop, value)
              return ee.Algorithms.If(condition, true(), ini)
  
          with_prop = ee.Dictionary(properties.iterate(add_properties, values))
          return ee.Dictionary(it).set(theid, with_prop)
  
      result = collection.iterate(listval, ee.Dictionary({}))
      result = ee.Dictionary(result)
  
      if side == 'server':
          return result
      elif side == 'client':
          return result.getInfo()
      else:
          raise ValueError("side parameter must be 'server' or 'client'")
  