This document provides detailed technical information about installing GeoPublicHealth dependencies on macOS, including Python environment management, troubleshooting, and advanced scenarios.
TL;DR - Best Practice: Use QGIS Python Console to run installation commands. This eliminates all Python environment confusion and is the most reliable method. See Method 1: QGIS Python Console below.
macOS systems often have multiple Python installations:
/usr/bin/python3)
/opt/homebrew/bin/python3 on Apple Silicon, /usr/local/bin/python3 on Intel)
brew install python~/anaconda3 or ~/miniconda3/Applications/QGIS.app/Contents/MacOS/bin/python3)
QGIS bundles its own Python interpreter for several reasons:
Installing packages to the wrong Python means QGIS won’t find them.
When you run pip install libpysal, it installs to whichever Python’s pip you’re using:
pip install libpysal → installs to whatever python3 is in your PATH (usually NOT QGIS)/Applications/QGIS.app/Contents/MacOS/bin/python3 -m pip install libpysal → installs to QGIS Python ✓/Applications/QGIS.app/Contents/MacOS/python3.12
Note: The exact binary name depends on QGIS version:
python3.12python3.11bin/python3 symlinkQGIS typically bundles Python 3.9 - 3.12 depending on the QGIS version:
Check your version:
/Applications/QGIS.app/Contents/MacOS/python3.12 --version
# Or use the symlink if it exists:
/Applications/QGIS.app/Contents/MacOS/bin/python3 --version
QGIS includes several packages by default:
PyQt5 - GUI frameworkqgis - PyQGIS librariesnumpy - Often included, but may need upgradegdal - Geospatial data abstraction libraryQGIS Python packages are installed to:
/Applications/QGIS.app/Contents/Resources/python/site-packages/
Or when using --user flag:
~/Library/Python/3.x/lib/python/site-packages/
Note: The exact paths may vary slightly between QGIS versions.
This is the recommended method for all users.
Advantages:
How it works:
When you run code in QGIS Python Console, it uses QGIS’s bundled Python interpreter automatically. We use subprocess.run([sys.executable, "-m", "pip", ...]) to invoke pip, which is more stable than the deprecated pip.main() API.
Two approaches:
A. Automated Script (install_dependencies_console.py):
install_dependencies_console.py (recommended location: ~/Downloads/).install_dependencies_console.py.If the plugin is not installed after restarting QGIS, follow the manual fallback steps in INSTALL_MAC.md.
B. Manual Commands:
⚠️ WARNING: These simple commands don’t handle all installation complexities (Shapely version conflicts, NumPy 2.x issues, Numba bin directory). Use the automated script instead (Option A above).
If you must use manual commands, here’s the minimum approach (but it will likely fail due to version conflicts):
import subprocess, sys
# This will fail - Shapely 2.0.6 bundled with QGIS conflicts with libpysal
subprocess.run([sys.executable, "-m", "pip", "install", "libpysal", "esda", "--no-build-isolation"])
# This may pull NumPy 2.x which breaks QGIS
subprocess.run([sys.executable, "-m", "pip", "install", "numba"])
Why manual commands don’t work:
--no-deps pulls NumPy 2.x, breaking QGIS’s bundled modules (compiled against NumPy 1.x)./Applications/QGIS.app/Contents/bin which doesn’t exist by default.The automated script solves all these issues by:
--target (overrides bundled version)--no-deps to avoid NumPy upgradeImportant: We use subprocess with sys.executable -m pip instead of the deprecated pip.main() API which can break with pip upgrades.
Why this is better than Terminal:
python3 command to useWhy --no-build-isolation?
Some packages (like libpysal and esda) have build-time dependencies that need to be visible. The --no-build-isolation flag allows them to use already-installed packages during build, preventing errors.
Critical Issues on macOS:
~/Library/Application Support/QGIS/QGIS3/profiles/default/python)RuntimeError: module compiled against API version 0x10 but this version of numpy is 0x11--no-deps when installing packages that depend on NumPy (like numba) to prevent automatic NumPy upgrade/Applications/QGIS.app/Contents/bin directory for binaries--targetCorrect Installation Pattern:
# Framework packages (safe to install normally)
subprocess.run([sys.executable, "-m", "pip", "install", "scipy", "pandas"])
# Shapely - MUST install to profile directory to override bundled 2.0.6
profile_python_dir = Path.home() / "Library/Application Support/QGIS/QGIS3/profiles/default/python"
subprocess.run([sys.executable, "-m", "pip", "install", "shapely>=2.1.2",
"--target", str(profile_python_dir), "--upgrade", "--no-deps"])
# Numba - MUST use --no-deps to avoid NumPy 2.x
subprocess.run([sys.executable, "-m", "pip", "install", "numba",
"--target", str(profile_python_dir), "--no-deps"])
# libpysal/esda - can install normally after Shapely is correct version
subprocess.run([sys.executable, "-m", "pip", "install", "libpysal", "esda", "--no-build-isolation"])
Why this pattern works:
sys.path, so Shapely 2.1.2 overrides bundled 2.0.6--no-deps prevents NumPy upgrade, keeping QGIS stable--target avoids bin directory issues and permission problemsFile: install_mac_dependencies.py
Advantages:
--yes, --timeout, --log, --python-path)How it works:
Uses subprocess to call pip as a separate process. All output is logged to a file for debugging. Supports overriding QGIS Python path and non-interactive execution.
Examples:
# Basic usage
/Applications/QGIS.app/Contents/MacOS/bin/python3 install_mac_dependencies.py
# Non-interactive with custom log path
/Applications/QGIS.app/Contents/MacOS/bin/python3 install_mac_dependencies.py --yes --log /tmp/install.log
# Override QGIS path for QGIS-LTR
python3 install_mac_dependencies.py --python-path /Applications/QGIS-LTR.app/Contents/MacOS/bin/python3 --yes
File: install_mac_dependencies.sh
Advantages:
set -euo pipefail)How it works:
# Default path
QGIS_PYTHON="${QGIS_PYTHON:-/Applications/QGIS.app/Contents/MacOS/bin/python3}"
# Auto-discovery if not found
if [ ! -x "$QGIS_PYTHON" ]; then
# Search for QGIS*.app in /Applications
...
fi
$QGIS_PYTHON -m pip install libpysal esda --no-build-isolation
The script discovers QGIS automatically or uses env var override. All output is logged to a timestamped file.
Examples:
# Basic usage (auto-discovers QGIS)
bash install_mac_dependencies.sh
# Override QGIS path for QGIS-LTR
QGIS_PYTHON="/Applications/QGIS-LTR.app/Contents/MacOS/bin/python3" bash install_mac_dependencies.sh
# Custom log directory
LOG_DIR=/tmp bash install_mac_dependencies.sh
⚠️ NOT RECOMMENDED - This method does not work correctly on macOS due to Shapely version conflicts and NumPy compatibility issues. Use the automated script instead.
Why this fails:
# This will install packages but FAIL at runtime
/Applications/QGIS.app/Contents/MacOS/python3.12 -m pip install numpy scipy pandas numba libpysal esda matplotlib --no-build-isolation
Problems:
AttributeError: 'Geometry' object has no attribute 'exterior' (Shapely 2.0 vs 2.1 API difference)If you must use Terminal, use this pattern (but the automated script is still better):
QGIS_PYTHON="/Applications/QGIS.app/Contents/MacOS/python3.12"
PROFILE_DIR="$HOME/Library/Application Support/QGIS/QGIS3/profiles/default/python"
# Create profile python directory
mkdir -p "$PROFILE_DIR"
# Install Shapely to profile (overrides bundled 2.0.6)
$QGIS_PYTHON -m pip install "shapely>=2.1.2" --target "$PROFILE_DIR" --upgrade --no-deps
# Install numba to profile (avoids NumPy 2.x)
$QGIS_PYTHON -m pip install numba --target "$PROFILE_DIR" --no-deps
# Install other packages normally
$QGIS_PYTHON -m pip install scipy pandas libpysal esda matplotlib --no-build-isolation
Why -m pip instead of just pip?
Using -m pip ensures you’re using the pip module for that specific Python, not a potentially different pip executable in your PATH.
which python3
# Should show /opt/homebrew/bin/python3 or /usr/bin/python3 (NOT QGIS)
/Applications/QGIS.app/Contents/MacOS/bin/python3 -c "import sys; print(sys.executable)"
# Should show the QGIS Python path
🔄 IMPORTANT: Restart QGIS first! QGIS only loads Python packages at startup.
Then open QGIS Python Console and run:
import sys
print("Python:", sys.executable)
print()
# Check required dependencies with version requirements
checks = [
('libpysal', '4.3.0'),
('esda', '2.0.0'),
('numba', '0.50.0'),
('numpy', '1.18.0'),
('shapely', '2.1.2'), # CRITICAL - must be 2.1.2+, not bundled 2.0.6
('geopandas', '0.14.0'), # Required for GeoPackage support
('fiona', '1.9.0'), # Required by geopandas
]
for module_name, min_version in checks:
try:
mod = __import__(module_name)
version = getattr(mod, '__version__', 'unknown')
print(f"✓ {module_name} {version}", end='')
# Special check for Shapely version
if module_name == 'shapely':
if version.startswith('2.0'):
print(" ⚠️ WARNING: Using bundled 2.0.x, need 2.1.2+")
elif version >= '2.1.2':
print(" ✓ (correct version)")
else:
print()
except ImportError:
print(f"✗ {module_name} NOT FOUND (required: >={min_version})")
# Test GeoPackage support
print("\nGeoPackage (.gpkg) support:")
try:
import geopandas as gpd
import fiona
print("✓ GeoPandas + Fiona available - GeoPackage files supported")
except ImportError as e:
print(f"✗ Missing: {e.name} - GeoPackage support disabled (shapefile-only mode)")
Expected output:
✓ libpysal 4.12.2
✓ esda 2.6.0
✓ numba 0.60.0
✓ numpy 1.26.4
✓ shapely 2.1.2 ✓ (correct version)
✓ geopandas 0.14.3
✓ fiona 1.9.6
GeoPackage (.gpkg) support:
✓ GeoPandas + Fiona available - GeoPackage files supported
If you see errors:
shapely 2.0.x - Installation didn’t work, you’re using QGIS’s bundled version. Run the automated script again.geopandas NOT FOUND - GeoPackage files won’t work in autocorrelation analysis. Run the automated script to install.import subprocess
import sys
result = subprocess.run(
[sys.executable, '-m', 'pip', 'list'],
capture_output=True,
text=True
)
print(result.stdout)
Diagnosis: Dependencies were installed to the wrong Python.
Solution:
# Check system/Homebrew Python
python3 -c "import libpysal; print(libpysal.__file__)"
# Check QGIS Python
/Applications/QGIS.app/Contents/MacOS/bin/python3 -c "import libpysal; print(libpysal.__file__)"
Diagnosis: Insufficient permissions to write to QGIS Python’s site-packages directory.
Solutions:
--user flag (installs to user directory):
/Applications/QGIS.app/Contents/MacOS/bin/python3 -m pip install --user libpysal esda numba
Use QGIS Python Console instead (usually has correct permissions)
sudo chown -R $(whoami) /Applications/QGIS.app/Contents/Resources/python/
Symptoms:
ERROR: Failed building wheel for libpysal
Solution:
Use --no-build-isolation flag:
/Applications/QGIS.app/Contents/MacOS/bin/python3 -m pip install libpysal esda --no-build-isolation
Why this works:
These packages need to see build dependencies (like numpy, scipy) during installation. The --no-build-isolation flag allows them to use the already-installed versions.
Diagnosis: You have both QGIS LTR and QGIS Latest installed (or older versions).
Solution:
ls -la /Applications/ | grep -i qgis
/Applications/QGIS.app/Contents/MacOS/bin/python3 -m pip install ...
# or
/Applications/QGIS-LTR.app/Contents/MacOS/bin/python3 -m pip install ...
Possible causes:
import sys; print(sys.executable)/Applications/QGIS.app/Contents/MacOS/bin/python3rm -rf ~/Library/Application\ Support/QGIS/QGIS3/profiles/default/python/__pycache__If you need to report an installation problem, please include the following information:
Required Information:
./geopublichealth_install_YYYYMMDD_HHMMSS.log# Run in QGIS Python Console:
from qgis.core import QgsApplication
print(QgsApplication.version())
# Run in Terminal:
sw_vers
# Run in QGIS Python Console:
import sys
print(sys.version)
print(sys.executable)
Where to report:
Issue template:
### Installation Problem
**Environment:**
- macOS version: [output of sw_vers]
- QGIS version: [output from QGIS console]
- Python path: [sys.executable from QGIS console]
**Installation method used:**
- [ ] QGIS Python Console script
- [ ] QGIS Python Console manual commands
- [ ] Terminal Python script
- [ ] Shell script
- [ ] Terminal one-liner
**Problem description:**
[Describe what happened]
**Log file:**
[Attach geopublichealth_install_*.log file]
Question: Can I use a virtualenv or conda environment with QGIS?
Answer: Not recommended. QGIS expects its bundled Python and pre-installed packages (PyQt5, qgis module). Using a different environment will break QGIS functionality.
Alternative: Install packages directly to QGIS Python using the methods above.
If you’re developing plugins and want to test with development versions of dependencies:
cd ~/projects
git clone https://github.com/pysal/libpysal.git
/Applications/QGIS.app/Contents/MacOS/bin/python3 -m pip install -e ~/projects/libpysal
Question: How do I ensure consistent dependency versions across installations?
Answer: Use the provided requirements-mac.txt file with pinned versions.
Why version pinning matters:
Using pinned versions:
Option 1 - Terminal:
/Applications/QGIS.app/Contents/MacOS/bin/python3 -m pip install -r requirements-mac.txt --no-build-isolation
Option 2 - QGIS Python Console:
import subprocess, sys
subprocess.run([sys.executable, "-m", "pip", "install", "-r", "requirements-mac.txt", "--no-build-isolation"])
Provided file: requirements-mac.txt contains tested, compatible versions.
When to use:
When NOT to use:
Security note: For maximum security and reproducibility, you can add hashes to requirements:
# Generate hashes
pip hash <package-name>==<version>
# Install with hash verification
/Applications/QGIS.app/Contents/MacOS/bin/python3 -m pip install -r requirements-mac.txt --no-build-isolation --require-hashes
Custom requirements file:
You can create your own based on requirements-mac.txt:
cp requirements-mac.txt my-requirements.txtIf you need to start fresh:
/Applications/QGIS.app/Contents/MacOS/bin/python3 -m pip uninstall -y libpysal esda numba matplotlib scipy pandas
/Applications/QGIS.app/Contents/MacOS/bin/python3 -m pip cache purge
/Applications/QGIS.app/Contents/MacOS/bin/python3 -m pip install --no-cache-dir numpy scipy pandas numba libpysal esda matplotlib --no-build-isolation
Some packages may need system libraries. Example:
# Install system dependencies via Homebrew
brew install geos proj
# Then install Python packages to QGIS Python
/Applications/QGIS.app/Contents/MacOS/bin/python3 -m pip install libpysal
Key Takeaways:
Quick Reference:
| Method | Complexity | Reliability | Environment Safety | Recommended For |
|---|---|---|---|---|
| QGIS Console (subprocess.run) | Easy | Highest | 100% Safe | Everyone - PRIMARY METHOD |
| QGIS Console Script | Easy | Highest | 100% Safe | Everyone who prefers automation |
| Terminal One-liner | Medium | Medium | Requires exact path | Advanced users comfortable with Terminal |
| Shell Script | Medium | High | Requires exact path (autodiscovery available) | Terminal users, CI/CD, scripted deployments |
| Terminal Python script | Medium | High | Has environment check, CLI args | Automation with logging, non-interactive mode |
Recommendation Priority: