1

I am attempting to calculate tree canopy coverage statistics (count, min, max, mean, median) on a census block level using the GLCF: Landsat Tree Cover Continuous Fields data set. I ran the script and discovered for some census blocks it will return all statistics, for others it will return only the mean and median, while yet other census blocks it won't return any of them. The code below references three census blocks that illustrate this behavior.

  • What causes this behavior?
  • Are there any changes I can make to guarantee all statistics are calculated?

_

var censusBlockIds = ['050534701002010',    // Returns all statistics
                      '051250105031020',    // Returns only mean and median statistics
                      '051250106006047'];   // Returns no statistics

// Get census blocks for analysis
var censusBlocks = ee.FeatureCollection("TIGER/2010/Blocks")
                     .filter(ee.Filter.inList('blockid10', censusBlockIds));


// Get 2010 Landsat Tree Cover Continuous Fields
var treeCanopyCover = ee.ImageCollection('GLCF/GLS_TCC')
                        .filter(ee.Filter.date('2010-01-01', '2010-12-31'))
                        .filterBounds(censusBlocks)
                        .select('tree_canopy_cover')
                        .max();

// Function to calculates statistics and add block id
function censusBlockTreeCoverage(censusBlock) {
  return  calcTreeCoverage(censusBlock.geometry())
    .set('blockid10', censusBlock.get('blockid10'));
}

// Function to calculate tree canopy coverage statistics for census block   
function calcTreeCoverage(censusBlockGeometry) {

  // Count pixels census block intersects
  var countDict = treeCanopyCover.reduceRegion({
    reducer: ee.Reducer.count(),
    geometry: censusBlockGeometry,
    scale: 30,
    maxPixels: 1e9,
    bestEffort: true
  });

  // Calculate minimum tree canopy cover for census block
  var minDict = treeCanopyCover.reduceRegion({
    reducer: ee.Reducer.min(),
    geometry: censusBlockGeometry,
    scale: 30,
    maxPixels: 1e9,
    bestEffort: true
  });  

  // Calculate maximum tree canopy cover for census block
  var maxDict = treeCanopyCover.reduceRegion({
    reducer: ee.Reducer.max(),
    geometry: censusBlockGeometry,
    scale: 30,
    maxPixels: 1e9,
    bestEffort: true
  });

  // Calculate mean tree canopy cover for census block
  var meanDict = treeCanopyCover.reduceRegion({
    reducer: ee.Reducer.mean(),
    geometry: censusBlockGeometry,
    scale: 30,
    maxPixels: 1e9,
    bestEffort: true
  }); 

  // Calculate median tree canopy cover for census block
  var medianDict = treeCanopyCover.reduceRegion({
    reducer: ee.Reducer.median(),
    geometry: censusBlockGeometry,
    scale: 30,
    maxPixels: 1e9,
    bestEffort: true
  });   

  // Return new geometry-less feature with calculations
  return ee.Feature(null)
    .set('treeCoverageCount', countDict.get('tree_canopy_cover'))
    .set('treeCoverageMin', minDict.get('tree_canopy_cover'))
    .set('treeCoverageMax', maxDict.get('tree_canopy_cover'))
    .set('treeCoverageMean', meanDict.get('tree_canopy_cover'))
    .set('treeCoverageMedian', medianDict.get('tree_canopy_cover'));
}

// Define visualization parameters
var treeCanopyCoverVis = {
  min: 0.0,
  max: 100.0,
  palette: ['ffffff', 'afce56', '5f9c00', '0e6a00', '003800'],
};

// Calculate statistics for each census block
var censusBlockStatistics = censusBlocks.map(censusBlockTreeCoverage);
print(censusBlockStatistics);

// Add layers to map for visual confirmation
Map.addLayer(treeCanopyCover, treeCanopyCoverVis, 'Tree Canopy Cover');
Map.addLayer(censusBlocks, {}, 'Census Blocks');
Map.centerObject(censusBlocks);   

Script Print Results

1

You do not get results for feature '0052be214b0ba9dd06e' as all the pixels in that region are masked. You can check that easily by adding the features to the map and inspecting the three features zooming in on them.

I do not see why you are performing reduce regions multiple times. You can easily combine the reducers and apply reduceRegion only a single time per feature. In that way your reducers work faster and will return values for the regions in which pixels are not masked. As in your original script, the geometry is then not retained:

// Function to calculate tree canopy coverage statistics for census block   
function calcTreeCoverage(censusBlockGeometry) {
// combine the reducers
  var reducers = ee.Reducer.count().combine(ee.Reducer.mean(), "", true).combine(ee.Reducer.min(), "", true)
              .combine(ee.Reducer.max(), "", true).combine(ee.Reducer.median(), "", true);
  // Calculate median tree canopy cover for census block
  var allDict = treeCanopyCover.reduceRegion({
    reducer: reducers,
    geometry: censusBlockGeometry,
    scale: 30,
    maxPixels: 1e9,
    bestEffort: true
  });
  // Return new geometry-less feature with calculations
  return ee.Feature(null).setMulti(allDict);
}

Way more easier, you could apply reduceRegions at once on your image. Then, the geometry is retained, which you somehow didn't do in your example script, but properties are added as 'null' values if all pixels inside the feature are masked:

// combine the reducers
var reducers = ee.Reducer.count().combine(ee.Reducer.mean(), "", true).combine(ee.Reducer.min(), "", true)
              .combine(ee.Reducer.max(), "", true).combine(ee.Reducer.median(), "", true);
// apply reduce regions at once
var reduceRegions = treeCanopyCover.reduceRegions(censusBlocks, reducers, 30);

Here is the link to the full script: script

As for your last question: ask only one question a time. Please open a new question for that question and someone can provide a specific solution for that question.

3
  • Thanks for the reply. Prior to your answer I didn't know you could combine reducers in the way you've suggested. I will move the second question to a new post. In the case of census blocks where there were mean/median calculations provided, do you know why it might not provide the other calculations? – Brian Dec 19 '18 at 15:57
  • I think it is related to the very small regions for which the error occurred. It only comprised of small parts of 8 pixels. But for such kind of questions you should ask them on the help forum of GEE to developpers. Or you could try the reduceRegion on multiple small and large regions and see if it makes a difference. – Kuik Dec 19 '18 at 16:06
  • Thanks again for your help. Since implementing your approach of combined reducers just a little while ago, I noticed that GEE now returns the missing statistics for those census blocks where only mean/median were being returned before. Perhaps it's a factor of reducing the region multiple times that caused that behavior. – Brian Dec 19 '18 at 16:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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