Library for handling diffs for geospatial data. Works with GeoPackage files and PostGIS databases (as well as with non-spatial SQLite and PostgreSQL databases).
Geodiff library is used by Mergin Maps - a platform for easy sharing of spatial data.
The first use case for geodiff library is to take two datasets with the same structure of tables and compare them - the comparison will create a "diff" file containing entries that were inserted/updated/deleted between the two datasets. A diff file can be applied to an existing dataset (e.g. a GeoPackage) and the dataset will be updated accordingly by applying the differences one by one. If you are familiar with diff and patch tools from UNIX world, this is meant to be an equivalent for spatial data.
The next use case is to merge changes from different copies of the same dataset that have been modified independently. Generally such changes cannot be applied cleanly. For example, if multiple users changed the same row of a table, or added a new row with the same ID. The library has functionality to "rebase" a diff file on top of another diff file, resolving any conflicts, so that all the changes can be applied cleanly. There still may be conflicts that can't be resolved fully automatically (e.g. if the same value is modified in different copies), these are written to a separate conflict file that can be addressed later (such changes are typically rare).
It is possible to apply diffs across different databases supported by geodiff drivers (nowadays supporting SQLite/GeoPackage and PostgreSQL/PostGIS). That means one can seamlessly find out difference between tables of two schemas in a PostGIS database, and apply the changes to a GeoPackage (or vice versa). Thanks to that, it is possible to keep data in sync even if the backends are completely different.
There are multiple ways how geodiff can be used:
geodiffcommand line interface (CLI) toolpygeodiffPython modulegeodifflibrary using C API
The library nowadays comes with support for two drivers:
- SQLite / GeoPackage - always available
- PostgreSQL / PostGIS - optional, needs to be compiled
To get changes between two GeoPackage files and write them to a-to-b.diff (a binary diff file):
geodiff diff data-a.gpkg data-b.gpkg a-to-b.diffTo print changes between two GeoPackage files to the standard output:
geodiff diff --json data-a.gpkg data-b.gpkgTo apply changes from a-to-b.diff to data-a.gpkg:
geodiff apply data-a.gpkg a-to-b.diffTo invert a diff file a-to-b.diff and revert data-a.gpkg to the original content:
geodiff invert a-to-b.diff b-to-a.diff
geodiff apply data-a.gpkg b-to-a.diff
The geodiff tool supports other various commands, use geodiff help for the full list.
Install the module from pip:
pip3 install pygeodiffIf you get error ModuleNotFoundError: No module named 'skbuild' try to update pip with command
python -m pip install --upgrade pip
Sample usage of the Python module:
import pygeodiff
geodiff = pygeodiff.GeoDiff()
# create a diff between two GeoPackage files
geodiff.create_changeset('data-a.gpkg', 'data-b.gpkg', 'a-to-b.diff')
# apply changes from a-to-b.diff to the GeoPackage file data-a.gpkg
geodiff.apply_changeset('data-a.gpkg', 'a-to-b.diff')
# export changes from the binary diff format to JSON
geodiff.list_changes('a-to-b.diff', 'a-to-b.json')If there are any problems, calls will raise pygeodiff.GeoDiffLibError exception.
If you have Mergin plugin for QGIS installed, you can also use pygeodiff from QGIS' Python Console by starting with
import Mergin.mergin.deps.pygeodiff as pygeodiff
See geodiff.h header file for the list of API calls and their documentation.
Output messages can be adjusted by GEODIFF_LOGGER_LEVEL environment variable.
Changes between datasets are read from and written to a binary changeset format.
Install postgresql client and sqlite3 library, e.g. for Linux
sudo apt-get install libsqlite3-dev libpq-devor MacOS (using SQLite from QGIS deps) by defining SQLite variables in a cmake configuration as following:
SQLite3_INCLUDE_DIR=/opt/QGIS/qgis-deps-${QGIS_DEPS_VERSION}/stage/include
SQLite3_LIBRARY=/opt/QGIS/qgis-deps-${QGIS_DEPS_VERSION}/stage/lib/libsqlite3.dylibCompile geodiff:
cd geodiff
mkdir build
cd build
cmake .. -DWITH_POSTGRESQL=TRUE
makeC++ tests: run make test or ctest to run all tests. Alternatively run just a single test, e.g. ./tests/geodiff_changeset_reader_test
Python tests: you need to setup GEODIFFLIB with path to .so/.dylib from build step
GEODIFFLIB=`pwd`/../build/libgeodiff.dylib GEODIFFCLI=`pwd`/build/geodiff pytest- run
python3 ./scripts/update_version.py --version x.y.z - push to GitHub
- tag the master & create github release - Python wheels will be automatically published to PyPI!
We use Astyle (C++), Cppcheck(C++) and Black (Python) for formatting
- run
./scripts/run_astyle.sh - run
./scripts/run_cppcheck.sh - run
./scripts/run_black.sh
Library uses its own copy of

