Yield monitor data collected from sensors onboard of modern combines offer a wealth of spatial information crucial for precision agriculture. By analyzing these data, farmers and agronomists can create and delineate field management zones based on varying crop yield levels. This process involves importing, cleaning, and aggregating yield observations across a field to identify areas that exhibit similar yield levels.
These management zones can then be used to apply specific agronomic practices, such as variable rate seeding, fertilization, or irrigation, which are adjusted to the specific needs of each zone. By segmenting fields based on yield data, this approach allows for more efficient use of resources, optimizing crop production, and more sustainable farming practices by reducing unnecessary inputs in lower-yielding areas.
In this exercise we will use a yield monitor data to implement a series of geospatial operations to remove outliers, clip the observations to the field boundaries, and cluster the resulting yield map.
Go to https://geojson.io/ and export a field boundary for the field. Just in case, the boundary is also available in the “datasets/shapefiles/Mortimers” folder
# Import modulesimport numpy as npimport geopandas as gpdimport matplotlib.pyplot as pltfrom shapely.geometry import boxfrom scipy.interpolate import griddata
# Read the filegdf = gpd.read_file("../datasets/spatial/Mortimers/Soybeans.shp")gdf.head(3)
GrowerName
FieldName
CrpZnName
Load
Variety
LoadVar
MachineID
TimeStamp
Date
Time
Yield
Flow
Moisture
Duration
Distance
Width
Area
geometry
0
Knopf Farms
Mortimer's
Soybeans
17/09/30-18:37:10
NaN
17/09/30-18:37:10
222486
2017-09-30 18:38:56
2017-09-30
18:38:56
61.45
2.7965
13.0
1.0
21.8504
480.0
0.001672
POINT (-97.43268 38.71256)
1
Knopf Farms
Mortimer's
Soybeans
17/09/30-18:37:10
NaN
17/09/30-18:37:10
222486
2017-09-30 18:38:57
2017-09-30
18:38:57
49.74
2.1531
13.0
1.0
20.7874
480.0
0.001591
POINT (-97.43269 38.71255)
2
Knopf Farms
Mortimer's
Soybeans
17/09/30-18:37:10
NaN
17/09/30-18:37:10
222486
2017-09-30 18:38:58
2017-09-30
18:38:58
42.12
1.8202
13.0
1.0
20.7480
480.0
0.001588
POINT (-97.43269 38.71255)
# Add a speed columngdf['Speed'] = gdf['Distance']/gdf['Duration']/12*0.68# in/s to mph
# Inspect data in tabular formatgdf[['Yield','Moisture','Speed']].describe()
# Define boxes of the fieldxmin, ymin, xmax, ymax = bnd.to_crs(epsg=epsg_utm).total_bounds# Define box sizexdelta =20# metersydelta =20# meters# Create an empty numpy arraygrid = np.array([])# Iterate to create each boxfor x in np.arange(xmin,xmax,xdelta):for y in np.arange(ymin,ymax,ydelta): cell = box(x, y, x+xdelta, y+ydelta) grid = np.append(grid, cell)# Conver the grid to a GeoDataFramegdf_grid = gpd.GeoDataFrame(grid, columns=['geometry'], crs=epsg_utm)# Get centroids of each cellgdf_grid['centroids'] = gdf_grid['geometry'].centroidgdf_grid.head()
# Create plot of field boundary and gridfig, ax = plt.subplots(1,1)bnd.plot(ax=ax, facecolor='w', edgecolor='r')gdf_grid.plot(ax=ax, facecolor='None', edgecolor='k')ax.ticklabel_format(useOffset=False)plt.show()
# Clip cells to field boundarygdf_grid = gpd.clip(gdf_grid, bnd['geometry'].iloc[0])gdf_grid.reset_index(drop=True, inplace=True)
# Create plot of field boundary and gridfig, ax = plt.subplots(1,1)bnd.plot(ax=ax, facecolor='w', edgecolor='r')gdf_grid.plot(ax=ax, facecolor='None', edgecolor='k')ax.ticklabel_format(useOffset=False)plt.show()
# Iterate over each cell to compute the median yieldgrid_yield = []for k,row in gdf_grid.iterrows(): idx_within_cell = gdf['geometry'].within(row['geometry']) yield_value = gdf.loc[idx_within_cell, 'Yield'].median() grid_yield.append(yield_value)# Append new column to the gdf_grid gdf_grid['yield'] = grid_yieldgdf_grid.head()
geometry
centroids
yield
0
POLYGON ((-97.43229 38.71095, -97.43229 38.710...
POINT (-97.43217 38.71086)
NaN
1
POLYGON ((-97.43252 38.71095, -97.43229 38.710...
POINT (-97.43240 38.71086)
NaN
2
POLYGON ((-97.43229 38.71095, -97.43229 38.710...
POINT (-97.43217 38.71104)
NaN
3
POLYGON ((-97.43229 38.71095, -97.43252 38.710...
POINT (-97.43240 38.71104)
36.565
4
POLYGON ((-97.43251 38.71113, -97.43251 38.711...
POINT (-97.43240 38.71122)
53.040
# Create plot of field boundary and gridfig, ax = plt.subplots(1,1)bnd.plot(ax=ax, facecolor='w', edgecolor='k')gdf_grid.plot(ax=ax, column='yield', edgecolor='k', cmap='RdYlGn')ax.ticklabel_format(useOffset=False)plt.show()