QGIS plugin and standalone Python library that propagates a spatial raster through a Lagrangian connectivity / transition matrix for n time steps:
where x is a row vector of cell values and T is the transition matrix (T[i,j] = probability that a particle in cell i moves to cell j in one dt).
spatial-connect/
├── plugin/ - QGIS plugin (self-contained, zippable)
│ ├── __init__.py - QGIS classFactory
│ ├── metadata.txt - QGIS plugin metadata
│ ├── spatial_connect.py - plugin lifecycle (registers Processing provider)
│ ├── processing_provider.py - Processing Toolbox algorithm
│ ├── dependencies.py - auto-install missing packages at load time
│ └── core/ - standalone propagation library
│ ├── __init__.py
│ ├── matrix_loader.py - load .mtx / .npz transition matrices
│ ├── propagator.py - SpatialPropagator (discrete + continuous)
│ └── raster_utils.py - read/write GeoTIFF, compute_cell_ids
├── tests/
│ ├── conftest.py
│ ├── test_propagator.py
│ ├── test_matrix_loader.py
│ ├── test_raster_utils.py
│ └── test_integration.py
├── examples/
├── build_plugin_zip.py - creates dist/SpatialConnect-<version>.zip (cross-platform)
├── build_plugin_zip.sh - same, bash shortcut for Linux/macOS
├── requirements.txt
├── pytest.ini
└── README.md
git clone https://github.com/CNR-ISMAR/spatial-connect.git
cd spatial-connect
pip install -r requirements.txtOption A - Download from GitHub Releases (recommended for end users)
Go to the Releases page
and download SpatialConnect-<version>.zip.
Then in QGIS: Plugins -> Manage and Install Plugins -> Install from ZIP -> select the file.
Option B - Build the ZIP locally (if you have the repo cloned)
python build_plugin_zip.py # cross-platform (Linux / macOS / Windows)
# or on Linux/macOS:
./build_plugin_zip.shThe file is created in dist/SpatialConnect-<version>.zip - install it as above.
Option C - Symlink (recommended for developers)
Symlink plugin/ into the QGIS plugins directory as SpatialConnect:
# Linux / macOS
ln -sfn $(pwd)/plugin \
~/.local/share/QGIS/QGIS3/profiles/default/python/plugins/SpatialConnect
# Windows (run as administrator)
mklink /D "%APPDATA%\QGIS\QGIS3\profiles\default\python\plugins\SpatialConnect" pluginThen in QGIS: Plugins -> Manage and Install Plugins -> Installed -> SpatialConnect -> Enable.
Changes to the source files are reflected immediately (reload plugin to pick them up).
import sys
sys.path.insert(0, "path/to/spatial-connect/plugin") # adds core/ to the path
from core import SpatialPropagator, MatrixLoader, RasterUtils
# 1. Load raster
array, meta = RasterUtils.read_raster("initial_distribution.tif")
# 2. Load transition matrix (.mtx = MatrixMarket, .npz = scipy sparse)
T = MatrixLoader().load("sparse_transition_matrix.mtx")
# 3. Propagate (10 discrete steps, x·T convention)
p = SpatialPropagator(mode="discrete")
result = p.run(array, T, iterations=10)
# 4. Save
RasterUtils.write_raster("output.tif", result.output, meta)| Format | Extension | How to produce |
|---|---|---|
| MatrixMarket | .mtx |
scipy.io.mmwrite() (e.g. from OpenDrift) |
| NumPy sparse | .npz |
scipy.sparse.save_npz(path, matrix) |
| Parameter | Default | Description |
|---|---|---|
mode |
"discrete" |
"discrete" = iterative multiplication; "continuous" = matrix exponential |
normalise |
False |
Row-normalise T -> Markov chain (conserves total mass) |
transpose_connectivity |
True |
True = x·T convention (Lagrangian); False = C·x legacy |
clip_negative |
True |
Clip negative output values to 0 |
nodata_value |
None |
Cells with this value are masked during propagation |
cell_ids |
None |
Masked-domain support - from RasterUtils.compute_cell_ids() |
The plugin registers the algorithm "Propagate Raster" under the SpatialConnect provider. Scriptable from PyQGIS:
import processing
result = processing.run("spatialconnect:propagate_raster", {
"INPUT": "/data/distribution.tif",
"MATRIX": "/data/sparse_transition_matrix.mtx",
"ITERATIONS": 10,
"MODE": 0, # 0=discrete, 1=continuous
"NORMALISE": False,
"CLIP_NEGATIVES": True,
"OUTPUT": "/tmp/output.tif",
})pytest tests/ -v| Package | Required for |
|---|---|
numpy |
all |
scipy |
matrix operations, sparse I/O |
rasterio |
GeoTIFF read/write |
fiona |
RasterUtils.vector_to_raster() only |
- Propagate Vector - a dedicated Processing algorithm that accepts a vector layer
(point/polygon) as initial distribution, rasterises it onto a reference grid via
RasterUtils.vector_to_raster()(requiresfiona), runs the propagation, and returns a GeoTIFF output. Parameters would include the burn attribute, fill value, andall_touchedoption. - Multi-band raster input - propagate each band independently through the same matrix (e.g. one band per species, pollutant, or time snapshot) and return a multi-band output with identical spatial metadata.
- Batch / multi-scenario mode (loop over a folder of rasters or matrix time-slices).
- Time-series output (store all n intermediate steps as a multi-band raster).
GNU General Public License v3 - see LICENSE.