Mapping Center-pivot Irrigation using Google Earth Engine

Over 22 million hectares (ha) of U.S. croplands are irrigated. Irrigation is an intensified agricultural land use that increases crop yields and the practice affects water and energy cycles at, above, and below the land surface. Until recently, there has been a scarcity of geospatially detailed information about irrigation that is comprehensive, consistent, and timely to support studies tying agricultural land use change to aquifer water use and other factors. (Abstract from Brown and Pervez 2014).

Many efforts have been put forth to map the irrigated land across the states. The Moderate Resolution Imaging Spectroradiometer (MODIS) Irrigated Agriculture Dataset for the U.S. (MIrAD-US) is one of its first kind. Check the MIrAD product on Earth Engine App. Learn more about MIrAD here. You can download 250 m MIrAD products in ScienceBase here.

In addition, Xie et. al 2019 mapped the irrigated cropland extent across the conterminous United States at 30 m resolution using a semi-automatic training approach on Google Earth Engine. Their work can be found in Earth Engine App here.

Let’s jump into code. (This code is obtained and modified from Google Earth Engine user guide.)

// Center-pivot Irrigation Detector.
// Finds circles that are 500m in radius.
Map.setCenter(-106.06, 37.71, 12);

// A nice NDVI palette.
var palette = [
  'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718',
  '74A901', '66A000', '529400', '3E8601', '207401', '056201',
  '004C00', '023B01', '012E01', '011D01', '011301'];

// Just display the image with the palette.
var image = ee.Image('LANDSAT/LC08/C01/T1_TOA/LC08_034034_20170608');
var ndvi = image.normalizedDifference(['B5','B4']);

Map.addLayer(ndvi, {min: 0, max: 1, palette: palette}, 'Landsat NDVI');

// Find the difference between convolution with circles and squares.
// This difference, in theory, will be strongest at the center of
// circles in the image. This region is filled with circular farms
// with radii on the order of 500m.
var farmSize = 500;  // Radius of a farm, in meters.
var circleKernel =, 'meters');
var squareKernel = ee.Kernel.square(farmSize, 'meters');
var circles = ndvi.convolve(circleKernel);
var squares = ndvi.convolve(squareKernel);
var diff = circles.subtract(squares);

// Scale by 100 and find the best fitting pixel in each neighborhood.
var diff = diff.abs().multiply(100).toByte();
var max = diff.focal_max({radius: farmSize * 1.8, units: 'meters'});
// If a pixel isn't the local max, set it to 0.
var local = diff.where(diff.neq(max), 0);
var thresh =;

// Here, we highlight the maximum differences as "Kernel Peaks"
// and draw them in red.
var peaks = thresh.focal_max({kernel: circleKernel});
Map.addLayer(peaks.updateMask(peaks), {palette: 'FF3737'}, 'Kernel Peaks');

// Detect the edges of the features.  Discard the edges with lower intensity.
var canny = ee.Algorithms.CannyEdgeDetector(ndvi, 0);
canny =;

// Create a "ring" kernel from two circular kernels.
var inner = - 20, 'meters', false, -1);
var outer = + 20, 'meters', false, 1);
var ring = outer.add(inner, true);

// Highlight the places where the feature edges best match the circle kernel.
var centers = canny.convolve(ring).gt(0.5).focal_max({kernel: circleKernel});
Map.addLayer(centers.updateMask(centers), {palette: '4285FF'}, 'Ring centers');

Leave a Reply

%d bloggers like this: