0

I have a list of points and I want to calculate the distance between each of them to the nearest body of water larger than n=10 pixels. In other words, for each point, I want to return a number showing how far the nearest water is, but the body of water needs to be bigger than a minimum size. I think I can use the annual Global Surface Water dataset https://developers.google.com/earth-engine/datasets/catalog/JRC_GSW1_3_YearlyHistory#description as the source for bodies of water, where a pixel code greater than 2 means permanent water. Is this possible within Google Earth Engine and if so, any ideas on how to go about this?

As an example, I define the existence of water as a minimum pixel value >2 over a past 5-year period before a desired date:

# A sample point
point = ee.Geometry.Point([-121.771399, 40.330908],'EPSG:4269')

# Extract image collection in desired time period
collection = ee.ImageCollection('JRC/GSW1_3/YearlyHistory')\
                .filterDate(ee.Date('2019-08-27').advance(-5, 'year'), '2019-08-27') # To see if a pixel had water over the past 5 years

# Pixels that had permanent water (water_class>2) over past 5 years (by taking min of pixel values over 5-year collection)
water = collection.min().gt(2)

1 Answer 1

1

You can filter out smaller water bodies my masking based on connectedPixelCount(). Then you can create an image with distances to water using fastDistanceTransform(), and finally get the distance of a point by reduceRegion() on that distance image for that point. Something like this:

var point = ee.Geometry.Point([-121.771399, 40.330908], 'EPSG:4269')
var collection = ee.ImageCollection('JRC/GSW1_3/YearlyHistory')
  .filterDate(ee.Date('2019-08-27').advance(-5, 'year'), '2019-08-27')

var water = collection.min().gt(2)
  .selfMask() // Mask all pixels but the actual water pixels

var nominalScale = 30 // Scale of JRC/GSW1_3/YearlyHistory
var minPixels = 10 // What you specified in the question
var minArea = Math.pow(nominalScale, 2) * minPixels

var pixelCount = water.connectedPixelCount({maxSize: 100})
var area = pixelCount.multiply(ee.Image.pixelArea())
var largeWaterBodies = water.updateMask(area.gte(minArea))

var distance = largeWaterBodies
  .mask()
  .fastDistanceTransform({
    neighborhood: 1024
  })
  .multiply(ee.Image.pixelArea())
  .sqrt()
  .rename('waterDistance')

var distanceFromPoint = distance
  .reduceRegion({
    reducer: ee.Reducer.first(), 
    geometry: point, 
    scale: nominalScale
  })
  .getNumber('waterDistance')

https://code.earthengine.google.com/a903545c703bcac076c54de1649481cb

4
  • This solution works great. What does maxSize:100 do in connectedPixelCount? Also, am I right in thinking that neighborhood:1024 in the fastDistanceTransform sets the limits of where the search for water bodies will be carried out and therefore the maximum distance possible? What will be returned if no water exists within that neighborhood?
    – Mohamad
    Jul 23, 2021 at 15:48
  • 1
    maxSize inside connectedPixelCount() is the maximum number of pixels to try to count. The function will report that number even if there are more pixels. I added a pixelCount layer - inspect some pixels inside large water bodies - they'll report 100. Jul 23, 2021 at 16:05
  • 1
    I'm not completely clear about the semantics of fastDistanceTransform() outside the neighborhood. Here's a script you can experiment with: code.earthengine.google.com/c0a1b4897973530e90af672cb81b8d65 Jul 23, 2021 at 16:20
  • 1
    Inspect the image inside the "circle" - that's the 256 pixel boundary in this test script. There distance is OK. Outside, it gives a large value. Zoom out a lot, and there seems to be another limit at play. Jul 23, 2021 at 16:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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