I've extracted a ton of pixel-value data for numerous study sites, across numerous days. I couldn't figure out how to do a precise extraction, so I just settled on extracting from all sites on ALL days at once. However, my goal is to compare against on-site measurements that were taken on certain days at each study site, so most of my extracted data won't match and is thus useless. I've gone through numerous iterations of this by now before always hitting dead-ends, but figured I'd give this one last attempt before just settling on manually filtering the CSV export outside of GEE.

So, my GEE approach was to try and filter the otherwise final featureCollection by means of an algorithm.if, which tries to check each entry of the feature collection, and confirm if it matches exactly to an entry in a separate dictionary variable that lists when and where the on-site sampling happened.

Sample of the erroneous function in question:

var targetsDict = ee.Dictionary({
'Brevard+Forest+2013-9-3': ee.Geometry.Point(-80.7987,28.3573),
'Citrus+Spivey+2013-7-1': ee.Geometry.Point(-82.3034,28.8322),
// etc...
'Lake+Unity+2013-9-3': ee.Geometry.Point(-81.8796,28.8743),
'Lake+Winona+2013-4-12': ee.Geometry.Point(-81.7696,28.5482)

// Function for checking if every feature in the feature collection has a match somewhere in the target list.
// If it does match, do nothing. If it doesn't match and is thus unwanted data, change band 1 value to -9999 to later filter out
var targetMatcher = function(feature) {
  // First pull the properties from the Feature such that it matches the name from the targetsDict
  var sampleName = feature.get("county") + "+" + feature.get("name") + "+" + feature.get("year") + 
                    "-" + feature.get("month") + "-" + feature.get("day");
  // Then create a copy of the point feature by again reading the properties of the Feature
  var sampleLocation = ee.Geometry.Point(feature.get("long"),feature.get("lat"));
    // Confirm if there's a match between a target site and the feature class entry - lookup the name, then check the location
    // I've tested this comparison method on a separate script, and it did seem to work fine there at least
    ( targetsDict.getGeometry( sampleName ).distance( {'right': sampleLocation, 'maxError' : 1 } ).abs().lte( 0.01 ) )), 
    // No idea though if just setting correct option to do nothing will work:
      (""), (feature.set("B1", -9999)))
  return feature;

// Map the function over the post-cloud-filtered FeatureCollection.
var targetFiltered = cloudFiltered.map(targetMatcher);

// Filter out data that didn't pass the target-list check, AKA had it's band1 value set to -9999
// This SHOULD rule be ruling out a LOT of data...
var fullyFiltered = outputData.filter(ee.Filter.gte("B1", -999)); 
print("This next featureCollection SHOULD be smaller");

Full script: https://code.earthengine.google.com/d789ba1c1310c248b73c5393f0eec9e3 Line 129 on is where this sample comes from.

I'm brand new to all of this so I'm probably doing a lot wrong here. Basically, my idea was to just change the band1 value to -9999 if it fails the algorithm.if check, and then filter out any entries afterwards that have that low band 1 value. The dictionary check itself should work since I did test it here at least: https://code.earthengine.google.com/5daa191a96def560bf960a7d43a743b2 However, the final featureCollection is the same size as the one before it (should be WAY smaller), and doesn't appear to have changed any values. So, I've messed something up somewhere.

Any ideas what I've done wrong, or what a better approach might be? Any advice welcome.

  • Noticed an error in the code - I was feeding the wrong variable into the final filtering step at line 205. I've fixed that in the code link above, but the targetMatcher function still isn't shrinking the featureClass at all, so I'm still stuck.
    – randomTask
    Jul 20, 2021 at 23:59

1 Answer 1


ReduceRegion returns a dictionary with null values if there is no intersecting pixels. You can't set null values to a feature that you are trying to build. So you have to work around that and specify your output values. Is this giving the behaviour you are expecting:

//////////////////// Pixel extractions of ALL coordinates for EACH day in question: ////////////////////
var outputData = pt_collection.map(function(feat){
  // only map over images that intersect with the selected feature.
  var filterCollect = sitesCollection.filterBounds(ee.Feature(feat).geometry());
  return filterCollect.map(function(img){
    var nameSplit = (ee.String(feat.get('name'))).split(' - '); // Split up the county + name + type designations
    var dateSplit = (ee.String(img.get('Date'))).split('-'); // Split up the date into year, month, and day
    var dictionaryOutput = img.reduceRegion({ // the dictionary will be null if there is no intersecting pixel
      geometry: feat.geometry(),
      scale: 30,
      reducer: ee.Reducer.mean()})
    dictionaryOutput = dictionaryOutput.map(function(key, val){return ee.List([val, -999]).reduce(ee.Reducer.firstNonNull())});
    return ee.Feature(null,dictionaryOutput).set({ // set up additional data columns for spreadsheet display purposes
        "water type": nameSplit.get(2),
        county: nameSplit.get(0),
        name: nameSplit.get(1),
        lat: feat.geometry().coordinates().get(1),
        long: feat.geometry().coordinates().get(0),
        //date: img.get('Date'); // Do we want it broken up or whole?
        year: dateSplit.get(0),
        month: dateSplit.get(1),
        day: dateSplit.get(2)
}).flatten(); // Ensures we're only getting one spreadsheet table, and not collection of divided up tables
print(outputData.limit(50), 'the output size: ', outputData.size());

// Filter out data that didn't pass the cloud/shadow filter, and thus had no pixel to extract from
var cloudFiltered = outputData.filter(ee.Filter.eq("B1", -999)); // Unsure this is best method. May need upper limit too since some values seem oddly high.


  • Hi, thanks for the advice! I noticed your method is instead keeping the -999 data and filtering out the good ones, but I was able to fix that by changing line 128 to "neq" instead of "eq". But, this still leaves me where I already was, since what I had before still got that same result (removing cloud-covered points). The part of this program I'm stumped on is the targetMatcher function at line 187 - it should return a smaller featureClass by comparing each feature to the dictionary to see if it matches (keep) or not (discard), but it's not doing anything.
    – randomTask
    Jul 20, 2021 at 23:58

Your Answer

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

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