Map algebra provides data-driven analyses that derive new outputs by evaluating one or more expressions. An expression is applied to an input field of spatial information and returns a new field as a result. Expressions can be chained together to build more complex analyses, each one building on the previous results. Because map algebra expressions are not evaluated until the final result is requested, you can compose complex expressions without creating intermediate outputs.

Map algebra operates on the underlying data (not only what is currently visible in the view), so results are consistent and can be reused in follow-on workflows. You can visualize results dynamically, export them for persistence, and chain multiple operations to build more advanced analysis models.

NDVI map algebra example

Use map algebra when you need to derive a new surface or category map from one or more input datasets. Common examples include:

  • creating suitability or risk surfaces from weighted criteria
  • classifying continuous values into discrete ranges
  • masking analysis to include only areas that meet a condition
  • combining intermediate analysis outputs into a final model

Map algebra expressions

In ArcGIS Maps SDK for .NET, you create map algebra expressions with ContinuousFieldFunction, DiscreteFieldFunction, and BooleanFieldFunction objects, then evaluate the final function to produce a new field. Many expresions can be built using common operators rather than method calls, for example using + to add two fields together or > to compare values between fields.

The following code creates a map algebra expression to create an NDVI (Normalized Difference Vegetation Index) raster from a multispectral image. The expression uses the red and near-infrared bands of the image to calculate the NDVI value for each pixel.

// Create continuous fields for the red and near-infrared bands of the multispectral image.
var redField = await ContinuousField.CreateAsync([multispectralImagePath], 0);
var nirField = await ContinuousField.CreateAsync([multispectralImagePath], 1);
// Create continuous field functions for the red and near-infrared bands.
var redFunction = ContinuousFieldFunction.Create(redField);
var nirFunction = ContinuousFieldFunction.Create(nirField);
// Compute the NDVI using the formula: (NIR - Red) / (NIR + Red).
var ndviFunction = (nirFunction - redFunction) / (nirFunction + redFunction);
// While less readable, you could also use method calls:
// nirFunction.Subtract(redFunction).Divide(nirFunction.Add(redFunction));
// Evaluate the function to create a continuous field representing the NDVI values.
var ndviField = await ndviFunction.EvaluateAsync();

When designing map algebra expressions, it may help to break the logic into intermediate functions rather than composing everything in one statement. This can make complex models easier to read, test, and update.

A common pattern is:

  • normalize or scale input values so they can be compared consistently
  • mask out cells that should be excluded from analysis
  • derive intermediate boolean or discrete categories from thresholds
  • combine intermediate outputs into the final classified or continuous result

Map algebra operations

The tables below list many of the available operations for each type of field function. Each operation is implemented as a method on the field function class. Many can also be expressed with common operators. See the ContinuousFieldFunction, DiscreteFieldFunction, and BooleanFieldFunction for details.

MethodOperatorComposes a function that
ContinuousFieldFunction.Add()
DiscreteFieldFunction.Add()
+Adds, point-wise, another compatible field function or a constant value.
ContinuousFieldFunction.Subtract()
DiscreteFieldFunction.Subtract()
-Subtracts, point-wise, another compatible field function or a constant value.
ContinuousFieldFunction.Multiply()
DiscreteFieldFunction.Multiply()
*Multiplies, point-wise, by another compatible field function or a constant value.
ContinuousFieldFunction.Divide()
DiscreteFieldFunction.Divide()
/Divides, point-wise, by another compatible field function or a constant value.
ContinuousFieldFunction.Remainder()
DiscreteFieldFunction.Remainder()
%Computes the remainder, point-wise, when dividing by another compatible field function or a constant value.
ContinuousFieldFunction.Abs()
DiscreteFieldFunction.Abs()
Returns the absolute value of the function’s field result across its extent.
ContinuousFieldFunction.Pow()Raises each value to a specified exponent, point-wise.
ContinuousFieldFunction.Exp()
ContinuousFieldFunction.Exp10()
Computes the natural exponential (base $e$) or base-10 exponential of each value, point-wise.
ContinuousFieldFunction.Log()
ContinuousFieldFunction.Log10()
Computes the natural logarithm or base-10 logarithm of each value, point-wise.
ContinuousFieldFunction.Sqrt()Computes the square root of each value, point-wise.
ContinuousFieldFunction.Reciprocal()Computes the reciprocal ($1/x$) of each value, point-wise.
ContinuousFieldFunction.Ceil()
ContinuousFieldFunction.Floor()
ContinuousFieldFunction.Round()
Rounds continuous values up, down, or to the nearest integer across the function’s extent.

Implement a map algebra workflow

You can implement map algebra using a pattern like the following:

  1. Create one or more input fields from input spatial data.

    You can load raster data into ContinuousField, DiscreteField, or BooleanField objects. Use API like ContinuousField.CreateAsync() to create fields from source raster datasets.

  2. Create field functions from the input fields and prepare data for analysis.

    Create ContinuousFieldFunction, DiscreteFieldFunction, or BooleanFieldFunction objects from the input fields. Prepare the data for analysis by masking out areas that should be excluded, normalizing values, or applying other transformations to the input fields.

  3. Compose map algebra expressions.

    Build expressions from arithmetic, trigonometric, logical, relational, and conditional operations to transform values and produce derived outputs. You can also convert between field types, for example with ContinuousFieldFunction.ToDiscreteFieldFunction() and DiscreteFieldFunction.ToContinuousFieldFunction().

  4. Evaluate the final function.

    Evaluation of an expression is deferred until it is explicitly requested (lazy evaluation). Call ContinuousFieldFunction.EvaluateAsync() (or the equivalent evaluate method for the function type you are working with) on the final expression to create the output field.

  5. Visualize and optionally persist the result.

    Display continuous results with a StretchRenderer and categorical results with a ColormapRenderer. You can add analyses to an AnalysisOverlay to display results. An AnalysisOverlay allows you to group related analyses and control visibility for all members of the collection. A GeoView can contain many analysis overlays. You can also export output fields for reuse by writing GeoTIFF files, for example with ContinuousField.ExportToFilesAsync().

Example

Categorize an elevation raster with map algebra

The following example follows the pattern described above to apply map algebra to an elevation raster and categorize values into classes. The workflow creates a field function expression, evaluates it to produce a result field, and then renders the categories in a map view.

  1. Create a source field from raster data.

    Create a ContinuousField from an elevation raster. Create a Map to display the original raster so users can compare inputs and outputs.

    // Create a continuous field from the elevation raster file.
    _elevationField = await ContinuousField.CreateAsync([elevationRasterPath], 0);
    // Create a map with a dark hillshade basemap, set the initial viewpoint, and add an elevation raster layer.
    MyMapView.Map = new Map(BasemapStyle.ArcGISHillshadeDark)
    {
    InitialViewpoint = new Viewpoint(55.584612, -5.234218, 300000),
    OperationalLayers = { new RasterLayer(new Raster(elevationRasterPath)) }
    };
  2. Create a field function from the raster field and mask out values at or below sea level.

    Create a ContinuousFieldFunction from the ContinuousField of elevation values. Mask out values at or below sea level so that only land areas are categorized in the final output.

    // Create a continuous field function from the elevation field.
    var continuousFieldFunction = ContinuousFieldFunction
    .Create(_elevationField);
    // Mask out values below sea level to categorize only land.
    var elevationFieldFunction = continuousFieldFunction
    .Mask(continuousFieldFunction >= 0);
  3. Build expressions that create categories of elevation values.

    Compose map algebra expressions that use mathematical and relational operations to create categorized output fields based on elevation.

    // Round elevation values down to the lower 10m interval, then convert to a discrete field function.
    var tenMeterBinFunction = ((elevationFieldFunction / 10).Floor() * 10)
    .ToDiscreteFieldFunction();
    // Create boolean fields for each geomorphic category based on the nearest 10m interval field.
    // Operator overloads (>=, <, &) can be used in place of IsGreaterThanOrEqualTo, IsLessThan, and LogicalAnd.
    var isRaisedShoreline = (tenMeterBinFunction >= 0) & (tenMeterBinFunction < 10);
    var isIceCovered = (tenMeterBinFunction >= 10) & (tenMeterBinFunction < 600);
    var isIceFreeHighGround = tenMeterBinFunction >= 600;
    // Assign geomorphic categories based on the boolean fields:
    // raised shoreline = 1, ice covered = 2, ice-free high ground = 3.
    var geomorphicCategoryFunction = tenMeterBinFunction
    .ReplaceIf(isRaisedShoreline, 1)
    .ReplaceIf(isIceCovered, 2)
    .ReplaceIf(isIceFreeHighGround, 3);
  4. Evaluate the final expression to produce a discrete field of geomorphic categories.

    None of the intermediate functions are evaluated until the final expression is evaluated. Call ContinuousFieldFunction.EvaluateAsync() to produce a DiscreteField of geomorphic categories.

    _geomorphicCategoryField = await geomorphicCategoryFunction.EvaluateAsync();
  5. Render the output categories.

    Export the evaluated field if needed, create a RasterLayer from the result, and apply a ColormapRenderer so each category is symbolized with a distinct color.

    // Export the discrete field result to a raster file.
    // Depending on raster size, the export may create multiple files (named with the specified prefix).
    var exportedFiles = await _geomorphicCategoryField.ExportToFilesAsync(tempDir, filenamesPrefix);
    // Create a raster layer from the exported geomorphic categorization file.
    _geomorphicRasterLayer = new RasterLayer(new Raster(exportedFiles[0]));
    // Create an array of colors for the geomorphic categories.
    // The array index maps directly to the pixel value in the raster.
    var colors = new Color[]
    {
    Color.Transparent, // 0 = no category (NoData pixels are hidden)
    Color.FromArgb(25, 118, 210), // 1 = raised shoreline - blue
    Color.FromArgb(128, 203, 196), // 2 = ice covered - teal
    Color.FromArgb(121, 85, 72), // 3 = ice-free high ground - brown
    };
    // Create a colormap renderer to assign colors to pixel values and apply it to the raster layer.
    _geomorphicRasterLayer.Renderer = new ColormapRenderer(colors);
    // Set opacity to allow the basemap hillshade to show through.
    _geomorphicRasterLayer.Opacity = 0.5;
    // Add the geomorphic categorization raster layer to the map.
    MyMapView.Map.OperationalLayers.Add(_geomorphicRasterLayer);

This pattern can be adapted to many use cases beyond elevation classification, such as vegetation index thresholding, terrain suitability modeling, and multi-criteria risk mapping.