Skip to content
← Back to blog
TechnologyOctober 9, 2026

Machine Learning for Land Cover: A Random Forest on Sentinel-2

A first machine learning workflow for GIS: train a random forest on labelled points, classify a Sentinel-2 stack, and measure accuracy honestly.

technologyMachine Learning

"AI in GIS" covers a lot of ground, from chatbots to deep learning on aerial photos. The most established use is much simpler: teach a computer to label pixels, such as water, vegetation and built-up land, by showing it examples.

This post walks through that workflow with a random forest, which is a good first model for GIS because it works on ordinary tables of numbers and needs no special hardware.

What is supervised classification?

In supervised classification you provide examples where you already know the answer, called training data. The model learns the link between the pixel values and the label, then applies it to every other pixel.

A random forest is a collection of many decision trees. Each tree votes on the class of a pixel and the majority wins. It copes well with many bands, is hard to overfit badly, and works with little tuning.

Machine learning is the umbrella term, and a random forest is one machine learning method. It is not the same as deep learning, which uses neural networks and usually needs far more data.

Why it matters

Hand-digitising land cover for a district takes days and differs between people. A trained model classifies the same area in minutes and applies identical rules to every pixel, which also makes it repeatable for the next year's image.

The model is only as good as its training data and your checking of it. A confident-looking map can be wrong in specific places, and the accuracy figure tells you where.

How it works

Each pixel becomes one row in a table. The columns are the band values (blue, green, red, NIR and so on), and often indices such as NDVI. For labelled pixels, one more column holds the class.

  1. Take the pixels at your training points to build a table of band values and labels.
  2. Split the table into a training part and a test part.
  3. Train the model on the training part.
  4. Predict the test part and compare with the known labels. This gives accuracy.
  5. Predict every pixel in the image to make the map.

Practical GIS example

Classify an area into four classes: 1 water, 2 vegetation, 3 built-up, 4 bare soil. You have:

  • s2_stack.tif: a multi-band raster with the Sentinel-2 bands you want, all on the same 10 m grid.
  • training_points.gpkg: points with a class_id column holding 1 to 4.
# Random forest land cover classification of a multi-band raster.
# pip install geopandas rasterio scikit-learn numpy

import geopandas as gpd
import numpy as np
import rasterio
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, confusion_matrix
from sklearn.model_selection import train_test_split

with rasterio.open("s2_stack.tif") as src:

    # Step 1: Read the training points and match them to the raster's CRS
    points = gpd.read_file("training_points.gpkg").to_crs(src.crs)

    # Step 2: Read the band values under each point
    coords = [(geom.x, geom.y) for geom in points.geometry]
    X = np.array(list(src.sample(coords)))     # rows = points, columns = bands
    y = points["class_id"].to_numpy()

    # Step 3: Read the whole image for prediction later
    image = src.read()                         # shape: (bands, rows, cols)
    profile = src.profile

# Step 4: Hold back 30% of the points to test the model
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42, stratify=y
)

# Step 5: Train the random forest
model = RandomForestClassifier(n_estimators=200, random_state=42)
model.fit(X_train, y_train)

# Step 6: Check accuracy on points the model has not seen
predicted = model.predict(X_test)
print("Overall accuracy:", round(accuracy_score(y_test, predicted), 3))
print("Confusion matrix (rows = true, columns = predicted):")
print(confusion_matrix(y_test, predicted))

# Step 7: Predict every pixel. Reshape to one row per pixel, then back.
bands, rows, cols = image.shape
pixels = image.reshape(bands, -1).T
classified = model.predict(pixels).reshape(rows, cols).astype("uint8")

# Step 8: Save the map. Class ids start at 1, so 0 can mean "no data".
profile.update(dtype="uint8", count=1, nodata=0)
with rasterio.open("landcover.tif", "w", **profile) as dst:
    dst.write(classified, 1)

The code assumes the stack has no no-data pixels. If yours has, mask those pixels before predicting, otherwise the model assigns them a class.

Read the confusion matrix by row. If many true built-up points are predicted as bare soil, those two classes look alike in your bands, and you may need more bands, an index or better training points.

Workflow in practice

  1. Decide the question and the classes. Fewer, clearly different classes work better.
  2. Choose cloud-free imagery from the right season, and note preprocessing (Level-2A surface reflectance, cloud mask).
  3. Collect training points on pure pixels, spread across the whole area, with a similar count per class.
  4. Train, test, and look at the confusion matrix.
  5. Inspect the map against the image and against places you know.
  6. Record the date, the data, the model settings and the accuracy so the run can be repeated.

Common mistakes

  • Testing on training data. Accuracy near 100% on points the model learned from says nothing. Always hold some back.
  • Nearby test and training points. Points next to each other are correlated, so random splitting can overstate accuracy. Collect test points separately, or split by area.
  • Uneven classes. If water has 10 points and vegetation 500, the model favours vegetation.
  • Mixed pixels in training. A point on a boundary between field and road teaches the model nothing clean.
  • Using one date for everything. Crops and bare soil can look alike in one season and different in another.
  • Treating a demo as a production system. Check the result on new areas and new dates, and watch for the model getting worse as conditions change, which is called model drift.
  • No documentation. Without a record of data and settings, nobody can repeat or audit the map.

Key takeaways

  • Supervised classification learns from labelled examples and applies the rules to every pixel.
  • A random forest is a sensible first model: it works on band tables and needs little tuning.
  • Training data quality matters more than model choice.
  • Report accuracy from held-out points, and read the confusion matrix, not only the headline number.
  • Keep a record of data, settings and accuracy so the workflow can be repeated.
T&S
Time & Spatial
Engineers who've shipped real systems
Share