Contributing to AequilibraE for QGIS#
This page presents some initial instructions on how to setup your system to start contributing to QAequilibraE and lists the requirements for all pull-requests to be merged into main.
Software Design and requirements#
QAequilibraE is built on top of AequilibraE’s main features, and the most important piece of AequilibraE’s backend is, without a doubt, NumPy.
The user might not see or know, but whenever vectorization is not possible through the use of NumPy functions, compiled code written in Cython is developed in order to accelerate computation.
QAequilibraE also observes a strong requirement of only using libraries that are available in the Python installation used by QGIS on Windows.
We have not yet found an ideal source of recommendations for developing QAequilibraE, but a good initial take can be found in this article.
Please notice that QAequilibraE installation MUST WORK at least in the most recent long-term release (LTR).
Developing QAequilibraE#
We recommend using a dedicated virtual environment to develop QAequilibraE, using the version of Python related to the most recent QGIS long-term release. When this section was updated (October/2025),LTR 3.40.12 was coming with a default 3.12.11 Python environment.
We also assume you are using one of PyCharm or VSCode, which are good IDEs for Python. If you are using a different IDE, we would welcome if you could contribute with instructions to set that up.
(For us,) The easiest way of developing a QGIS plugin is using a Docker container to build
an image containing a QGIS installation. When cloning QAequilibraE repository into your local
machine you will find a Dockerfile with this recipe.
git clone https://github.com/AequilibraE/qaequilibrae.git
Then all you have to do is activate the virtual environment and adding the environmental variables. Without adding these variables, your installation of AequilibraE in QGIS is goint to be useless.
We understood that the creation of a virtual development environment within a container would be redundant, however after facing some developing issues related to PEP 668, we believe that using a virtual environment would be a good practice.
. .venv/bin/activate
export PYTHONPATH=$(pwd)/qaequilibrae/packages:$PYTHONPATH
export QT_QPA_PLATFORM=offscreen
If you have to test changes in QAequilibraE after its installed in QGIS, we strongly recommend using the Plugin Reloader, a plugin to reload another plugins.
Developing QAequilibraE and AequilibraE simultaneously#
This is a very specific case for features that are being developed simultaneously in the Python package and in the QGIS interface. Here, we need to create a symbolic link that reflects the changes in AequilibraE within QGIS. The following step-by-step instructions are for a Windows operating system (if you are using a different operating system, contributions to this documentation are welcome).
First, let’s create a virtual environment for AequilibraE.
python -m venv .venv
. .venv/bin/activate
pip install -U pip uv
# Check the branch you are going to install
git status
git pull
# Install AequilibraE in an editable version
uv pip install -e .
Open PowerShell as administrator.
# Navigate to where your QGIS plugins are
cd C:\Users\renat\AppData\Roaming\QGIS\QGIS3\profiles\default\python\plugins
# Create the symbolic link
New-Item -Path ./qaequilibrae -ItemType SymbolicLink -Value C:\Users\renat\Documents\GitHub\qaequilibrae\qaequilibrae
Proceed with the QAequilibraE installation in QGIS normally. Then, navigate to the folder where your plugin was installed and delete the AequilibraE folders. Return to the AequilibraE virtual environment.
# Install AequilibraE in QAequilibraE
uv pip install . --target C:\Users\renat\Documents\GitHub\qaequilibrae\qaequilibrae\packages --no-deps
This approach for installing AequilibraE in QAequilibraE has a major disadvantage: whenever there is a change in AequilibraE, it is necessary to reinstall it, however this is the simplest configuration for this case.
Developing QAequilibraE with AequilibraE’s develop#
There are two different scenarios: 1) you will develop updates in QAequilibraE based on AequilibraE’s develop branch, or 2) you will test in QGIS (software) whether what you did in the development environment actually works.
The first case is very simple: we install the develop branch in the qaequilibrae/packages folder and clean up the installation of redundant packages in QGIS. “But wouldn’t it be easier to just install AequilibraE directly in the virtual environment and call it a day?” Yes, but this way, we wouldn’t be developing and testing the plugin in the way it is actually used.
python -m uv pip install "git+https://github.com/AequilibraE/aequilibrae@develop" --target qaequilibrae/packages
python -c "from qaequilibrae.download_extra_packages_class import DownloadAll; DownloadAll().clean_packages('qaequilibrae/packages')"
For the second case, I’m assuming you will test the installation from the QAequilibraE ZIP file. If I’m not mistaken, these git installation operations are not permitted in QGIS, so an alternative is to install the AequilibraE binary, available as an artifact of the Build workflow. Look for the one that corresponds to develop and matches your operating system.
And how do we install this in QGIS? The alternative is to install QAequilibraE from a ZIP file and, initially, cancel the installation of additional packages. An error message reporting that QAequilibraE will not work is displayed, but we can ignore it for now. Go ahead and close QGIS as well. The next operations are performed in the OS4GEO shell.
# Check the QGIS python version to be sure which wheel is going to be installed
python --version
# Navigate to where your wheels are stored
cd C:\Users\renat\Downloads\aequilibrae_wheels
# And install it at the 'packages' folder inside QAequilibraE, just like we did before.
python -m pip install aequilibrae-1.5.0-cp312-cp312-win_amd64.whl --target "C:\Users\renat\AppData\Roaming\QGIS\QGIS3\profiles\default\python\plugins\qaequilibrae\packages"
Reopen QGIS. QAequilibraE will ask you again if you want to install the additional packages. This time answer yes and let QAequilibraE automatically remove the installation of duplicate packages. If your installation runs without errors, the plugin will be available for use containing the develop version of AequilibraE; otherwise, check the error message on the screen.
Development Guidelines#
QAequilibraE development (tries) to follow a few standards. A huge effort is being undertaken by the development team to update several portions of the code are still not up to such standards.
We try as much as possible to use built-in QGIS tools to develop QAequilibraE. If you need a guide to develop, try the QGIS testing developer cookbook or the QGIS Python API documentation. These two are going to be your development life jackets.
Style#
Python code should follow (mostly) the pycodestyle style guide.
Python docstrings should follow the reStructuredText Docstring Format.
We are big fans of auto-code formatting. For that, we use Black.
Negating some of what we have said so far, we use maximum line length of 120 characters.
Imports#
Imports should be one per line.
Imports should be grouped into standard library, third-party, and intra-library imports.
Imports of NumPy should follow the following convention:
import numpy as np
Translatable Strings#
If you are adding or modifying any piece of QAequilibraE’s code that includes translatable strings,
which are the strings displayed in the widget windows, please ensure you use the tr function
to locate the strings. This will guarantee that the strings are included in our future
translations. Currently, only classes that have a self method support the localization of
strings.
# Indicates that the message "You need at least three centroids to route." will be
# set for translation.
self.iface_error_message(self.tr("You need at least three centroids to route."))
# In case you have to insert any text into a string, the best way is to use string format
self.error = self.tr("ID {} is non unique in your selected field").format(str(id))
Strings in QAequilibraE Processing Provider can also be translated. To indicate the strings, import the translation function and configure it to return the context and the message.
from qaequilibrae.i18n.translate import trlt
class YourClassHere():
...
# YourClassHere functions
...
def processAlgorithm(self, parameters, context, model_feedback):
...
feedback.pushInfo(self.tr("Running assignment")) # indicates the translatable string
...
def tr(self, message):
return trlt("TrafficAssignYAML", message)
QAequilibraE’s translations are all hosted in Transifex. If you want to contribute to QAequilibraE by translating the plugin to other languages or reviewing the existing translations, please let us know in our AequilibraE Google Group. Feel free to request another languages for translation!
In the plugin internationalization page, you can find more information on creating your account and start translating QAequilibraE.
Contributing to AequilibraE for QGIS#
GitHub has a nice visual explanation on how collaboration is done GitHub Flow. (For us,) The most important points there are:
The main branch contains the latest working/release version of QAequilibraE
Work is done in an issue/feature branch (or a fork) and then pushed to a new branch
Automated testing is run using Github Actions. All tests must pass:
Unit testing
Build/packaging tests
Documentation building test
If the tests pass, then a manual pull request can be approved to merge into main
The main branch is protected and therefore can only be written to after the code has been reviewed and approved
No individual has the privileges to push to the main branch
Release versions#
For the past few years, QAequilibraE’s release versioning was related to the major and minor releases in AequilibraE. If you frequently update your plugin, you might have noticed that we recently jumped from v1.3.1 to v1.4.3, without any micro releases in between, just because these were the most recent AequilibraE version when the releases happened.
To add the Plugin Repository feature, the development team decided to change how the QAequilibraE versioning is done. We’ll move from version tags based on AequilibraE, for time-based tags when the release is made (so don’t be scared if you see a version such as 25.192.23).
We’ll continue using the de-facto Python standard for versioning, but with a different version scheme.
MAJOR.MINOR[.MICRO]
MAJOR designates the year of the release
MINOR designates the number of the day in the year
MICRO designates the hour of the day the release was made
Testing#
QAequilibraE testing is done with some tools:
Black, the uncompromising code formatter
Ruff, a linter and code formatter
pytest, a Python testing tool
pytest-cov, a tool for measuring test code coverage
pytest-qt, a tool for testing PyQt5 applications
pytest-qgis, a tool for writing QGIS tests
To run the tests locally, you will need to figure out what to do…
These same tests are run by GitHub Actions with each push to the repository. These tests need to pass in order to somebody manually review the code before merging it into main (or returning for corrections).
In some cases, test targets need to be updated to match the new results produced by the code since these
are now the correct results. In order to update the test targets, first determine which tests are
failing and then review the failing lines in the source files. These are easy to identify since each
test ultimately comes down to one of Python’s various types of assert statements. Once you identify
which assert is failing, you can work your way back through the code that creates the test targets in
order to update it. After updating the test targets, re-run the tests to confirm the new code passes all
the tests.
Tip
If you want to check if the test values are at the right place in the UI, qtbot can help you. Add qtbot in the function definition and take a screenshot of the UI. To visualize it, don’t forget to use a print statement.
path = qtbot.screenshot(dialog)
print(path)
Documentation#
All the QAequilibraE documentation is (unfortunately) written in reStructuredText and built with Sphinx. Although reStructuredText is often unnecessarily convoluted to write, Sphinx is capable of converting it to standard-looking HTML pages, while also bringing the docstring documentation along for the ride.
Build the documentation in HTML format with the following commands run from the root folder.
The Dockerfile has already installed the documentation packages in your virtual environment.
. .venv/bin/activate
# Replace the variables if needed
export LANG=C.UTF-8
export LC_ALL=C.UTF-8
cd docs
make html
Finally#
A LOT of the structure around the documentation was borrowed (copied) from the excellent project ActivitySim.