Growing Food in Pixels

Cameron Kruse
Earth Genome
Published in
8 min readSep 13, 2023

--

Mapping crops across the US using Mapbox’s new raster features

The Plotline, Earth Genome’s food and climate project, recently published an interactive map visualizing crops that grow across the United States at 10 by 10 meter resolution. This data is generated by the USDA using algorithms running on remote sensing imagery. Although it’s used widely for analysis by agricultural and academic researchers, it has been difficult to use this dataset in interactive maps due to its size. Mapbox recently released raster imagery styling in their new GL JS beta, making it easy to manipulate raster data in the front end. Our team took the opportunity to experiment with these new features in this tool to explore crop data.

To help others learn more about these new features and inspire new uses of these raster capabilities, we are sharing how we created this map. We’ll start with a little background on raster data, then explain the new features, and demonstrate how we applied them to crop data.

Along the way we also created an interactive tool demonstrating how we style climate stress data, that rapidly illustrates the power of raster color visualization on the front end.

Methods to interpret raster data

In a mapping context, raster data refers to data that utilizes a grid-like structure of cells or pixels to encode information about the Earth’s surface. Each pixel contains a value representing a specific attribute, such as elevation, temperature, land cover, satellite imagery intensity, or in our case crop type. This format is particularly suited for continuous and spatially varying data, allowing for detailed analysis and visualization of phenomena like topography, climate patterns, and urban sprawl. Raster data can be contrasted with vector data, but we won’t get into that in this post; Check out this blogpost from Carto if you’re interested in learning more about how these data types compare.

To better understand how to work with raster data, it’s helpful to look at three typical data patterns in rasters: categorical data, continuous, and multi-band continuous.

Categorical raster data

Categorical raster data is typically encoded in a single band with values that correspond to discrete attributes or categories. The crop data visualized in the map above is categorical data with each pixel value corresponding to a crop type. The raw data will look like a grayscale image that in and of itself is not really that informative. However, each gray pixel has a value that corresponds with a category. Typically this data gets styled using a palette where each value corresponds to a different color. In the example below, each square is a pixel. This hypothetical data has values between 0–9.

Continuous raster data

Continuous raster data is also typically a single ban and has values that correspond to the intensity of a specific variable. This could be something like elevation, carbon emissions, soil moisture, etc. This data is typically styled using a color scheme that communicates the message behind the intensity. For instance, if you’re styling soil moisture, the wettest values may be the darkest red and the driest the lightest red indicating where droughts are most intense. In the example below the illustration shows two ways continuous raster data can be styled. In the top example the data is styled with one color indicating intensity in the second example, it uses a color ramp of green-yellow-red to illustrate intensity.

Check out this example that styles heat stress and drought stress on agriculture to see what this type of data looks like.

Multi band continuous raster data

Multi band continuous raster data is perhaps the most common type as it’s how satellite imagery is portrayed on maps. Multi band data is the same type of data as single band continuous, but is typically represented as a blend of channels, such as a blend of red, green and blue channels. Note that remote sensing data can also include bands outside of the visible. This data can be styled in numerous ways depending on what you are trying to show. A typical way is to blend red, green, and blue bands into “natural colored” imagery, but other blends can be used to highlight a variety of insights such as vegetation or moisture. Check out this post for more info on common ways bands from remote sensing data are combined. In the diagram below, we illustrate how red, green and blue bands are combined to create a natural color image.

New Mapbox Raster Features

In GL JS 3 Mapbox released three new raster features: raster-color, raster-value, and raster-color-mix. Here’s how the documentation defines these new features:

  • raster-color Defines a color map by which to colorize a raster layer, parameterized by the raster-value expression and evaluated at 1024 uniformly spaced steps over the range specified by raster-color-range
  • raster-value Returns the raster value of a pixel computed via raster-color-mix. Can only be used in the raster-color property.
  • raster-color-mix When raster-color is active, it specifies the combination of source RGB channels used to compute the raster value. Computed using the equation mix.r * src.r + mix.g * src.g + mix.b * src.b + mix.a. The first three components specify the mix of source red, green, and blue channels, respectively. The fourth component serves as a constant offset and is *not* multiplied by source alpha. Source alpha is instead carried through and applied as opacity to the colorized result. Default value corresponds to RGB luminosity.

I found these settings difficult to understand until I tried them out in the examples below.

Rendering categorical raster data

Creating Tileset

For raster datasets that are large in size, it’s best to create a tileset and host this yourself in the cloud via an AWS s3 bucket or something similar. There are many ways to create raster tilesets, but we used GDAL, specifically gdal2tiles.

To tile categorical raster data, it’s important to carefully choose your resampling method. Resampling determines the value of a pixel when zoomed out at a resolution where each individual pixel in the source data can not be rendered. For categorical data either near or mode resampling is the right choice. The default method for gdal2tiles is average, which, in categorical data, will change the category of the pixels. We used mode resampling for our crop map as we wanted to highlight which crops were most prevalent in any given region. In the map you can change the resampling method yourself and see the differences.

The other parameter you should think about is your max and min zooms. For every zoom level you go up you increase the number of tiles by a factor of 4. With our pixels at a 10m x 10m level we didn’t gain much by going above 12. Here’s a tool that can help estimate how many tiles a geographic area would be at any particular zoom level.

Here’s the exact tiling script we used:

gdal2tiles.py -z 0–12 -r mode -w none <input-data-file.tif> <output-tile-directory/>

Once the tiles were finished, we hosted these in an s3 bucket.

Styling on a Mapbox map

To get started, add a map to the page, and create an on load event. Within the on load event, add a source that points to your raster tiles.

map.addSource("raster-source", {
"type": "raster",
"tiles": ["<url-to-tiles>/{z}/{x}/{y}.png"],
"tileSize": 256,
"scheme": "tms",
// Set your max and min zooms to your tiled max and min.
// In the layer styling you can choose when the layer itself is visible.
"minzoom": 1,
"maxzoom": 12,
})

Once you’ve added the source add the layer with styling parameters.

map.addLayer({
"id": option,
"type": "raster",
"source": 'raster-source',
// This is the max and min zoom where the layer is visible
// The data will stop increasing resolution at the max zoom
//specified for the source
"minzoom": 1,
"maxzoom": 22,
"layout": {"visibility": "visible"},
"paint": {
"raster-resampling": "nearest",
"raster-fade-duration": 0,
"raster-color": allCropsPaint,
"raster-color-mix": [255, 0, 0, 0],
"raster-color-range": [0, 255],
"raster-opacity": 1
}
},
// The layer the raster layer should be inserted above
"admin-1-boundary-bg")

Here are a few notes on what the raster styling features are doing:

  • raster-resampling needs to be set to nearest for categorical data. We already specified this when creating our tileset, but GL JS will also resample pixels when rendering the tiles.
  • raster-color points to a variable that defines the color of each crop category (1 = Corn, 2 = Cotton, 3 = Rice, 4 = Sorghum, 5 = Soybeans, 6 = Sunflower, 10 = Peanuts). Ours looks like this:
const allCropsPaint = [
["match", ["linear"], ["raster-value"],
0, "rgba(0, 0, 0, 0)",
1, "rgba(255, 212, 0, 1)",
2, "rgba(255, 38, 38, 1)",
3, "rgba(0, 169, 230, 1)",
4, "rgba(255, 158, 15, 1)",
5, "rgba(38, 115, 0, 1)",
6, "rgba(255, 255, 0, 1)",
7, "rgba(0, 0, 0, 0)",
8, "rgba(0, 0, 0, 0)",
9, "rgba(0, 0, 0, 0)",
10, "rgba(112, 168, 0, 1)",
// There are 245 more values
]
  • raster-color-mix is designed to control how multiple bands of data are mixed. Since we only have one band, and those values are between 0 and 255 our variable should be ”raster-color-mix”: [255, 0, 0, 0]. This value will be multiplied against the value defined in raster-color .
  • raster-color-range is the total range of values we want to style. This should be equal to your max and min pixel values which in this case that is 0, 255.

The end result is each crop styled with its respective color. When a particular crop is selected from the menu, it styles all other crop values as transparent.

Rendering continuous raster data

The crop map we created used categorical data, but we also experimented with styling continuous data. You can explore how we did this using climate stress data and tweak the styling yourself in an Observable notebook here. The process for styling continuous raster data is similar to how you style categorical data so we’re not going to describe it in detail. The one point to call out is to use linear interpolation instead of nearest in the Mapbox layer.

We hope this helps you kick off your exploration of these new Mapbox features. Share with us how you use these features or let us know if you have other ideas for how we could use these to create a more climate friendly food system.

--

--

Cameron Kruse
Earth Genome

Creative Technologist at Earth Genome and Bridges to Prosperity. I moonlight as a National Geographic Explorer