26  Stochastic Cellular Automata

Code
import tobler
import contextily as ctx
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from geosnap import DataStore
from geosnap.analyze.dynamics import transition, predict_markov_labels, draw_sequence_from_gdf
from geosnap.io import get_acs
from geosnap.visualize import animate_timeseries, plot_transition_matrix
from IPython.display import Image
from libpysal.weights import Queen, Rook, KNN
from matplotlib.colors import to_hex
from tobler.util import h3fy
from tobler.dasymetric import extract_raster_features

%load_ext watermark
%watermark -a 'eli knaap' -v -d -u -p geopandas,geosnap
OMP: Info #276: omp_set_nested routine deprecated, please use omp_set_max_active_levels instead.
Author: eli knaap

Last updated: 2025-11-24

Python implementation: CPython
Python version       : 3.12.12
IPython version      : 9.7.0

geopandas: 1.1.1
geosnap  : 0.15.3

Or, Tobler rides again: a nouveau computer movie simulating urban growth in detroit

Simulating land-use change in a Cellular Automata (CA) framework is one of the oldest traditions in urban growth modeling. Traditionally, a land-use model like SLEUTH uses a few different parameters calibrated on historical data to control the probability of a unit of land transitioning into a different kind of use (Brail, 2008; Dietzel & Clarke, 2006; Dietzel & Clarke, 2007; Guan & Clarke, 2010; Jantz et al., 2010).

Here, we’ll start with no data and build a simple CA model with a single transition rule using a spatial Markov framework. That is, the probability of a unit transitioning into different uses is a probabilistic function of its current state, and the states of the units around it. Using data from NLCD, we can observe how often these transitions occurred in the past, including how those transitions are conditioned by different spatial contexts. Then we can use that model to simulate conditions into the future.

26.1 Data

Start by grabbing tract-level data for the Detroit-ish region

Code
datasets = DataStore()
counties = ['26163', '26099', '26125']
tracts = get_acs(datasets, county_fips=counties, level="tract", years=[2018])
tracts = tracts.to_crs(tracts.estimate_utm_crs())
tracts.explore(tooltip=[])
Make this Notebook Trusted to load map: File -> Trust Notebook