A containerized Nextflow workflow for analyzing spatial accessibility to facilities (schools, healthcare) using OSM routing, hexagonal grids, and population data.
This workflow calculates spatial accessibility metrics by:
- Processing OSM road network with OSRM for routing
- Generating H3 hexagonal grids for each district
- Extracting population data for each grid cell
- Assigning grids to nearest facilities
- Calculating actual route distances and travel times
- Computing population-weighted accessibility metrics
- Creating visualization maps
Organize your data in the data/ directory:
data/
├── region.osm.pbf # OSM road network
├── districts.shp # District boundaries
├── facilities.csv # Facility locations
└── population.tif # Population raster
Where to get data:
- OSM PBF: https://download.geofabrik.de/
- Boundaries: Your own data or https://gadm.org/
- Population: https://www.worldpop.org/
- Facilities: Your survey data or government records
Optional but recommended: Run validation to catch data issues early:
python3 scripts/00_validate_data.pyThis checks:
- All required files exist
- Coordinate systems are correct
- CSV columns are properly named
- Data bounds are reasonable
If validation detects projected coordinates (large numbers like 682222):
python3 scripts/00b_convert_coordinates.pyNote: The workflow includes basic validation, so you can skip these scripts if your data is clean.
chmod +x build_containers.sh
./build_containers.shWait ~5-10 minutes for container to build.
nextflow run spatial_access_workflow.nf \
--osm_pbf data/region.osm.pbf \
--districts_shp data/districts.shp \
--facilities_csv data/facilities.csv \
--population_tif data/population.tif \
--facility_type healthcare \
--outdir resultsFormats: Shapefile (.shp), GeoJSON (.geojson), GeoPackage (.gpkg), or ZIP
Requirements:
- Polygon or MultiPolygon geometry
- Name attribute:
name,NAME,district, orDISTRICT - Any CRS (auto-reprojected)
Format: OSM PBF file (.osm.pbf)
Download: https://download.geofabrik.de/ (select your region)
Format: GeoTIFF (.tif) or any GDAL-readable raster
Source: WorldPop (recommended) - population count per pixel
Requirements:
- Population counts (not density)
- Valid nodata values
- Any CRS (auto-reprojected)
Format: CSV file
Required columns:
name(orfacility_name): Facility identifierlatitude(orlat): Decimal degrees (WGS84)longitude(orlon,long): Decimal degrees (WGS84)
Example:
name,latitude,longitude,type
Central Hospital,6.1234,1.2345,hospital
District Clinic,6.5678,1.6789,clinicIMPORTANT: Coordinates MUST be in WGS84 (EPSG:4326) decimal degrees:
- Latitude: -90 to 90
- Longitude: -180 to 180
If your coordinates are projected (UTM, etc.), run the conversion script first.
--osm_pbf PATH # Path to OSM PBF file
--districts_shp PATH # Path to districts shapefile
--facilities_csv PATH # Path to facilities CSV
--population_tif PATH # Path to population raster--facility_type TEXT # Type of facility (default: 'school')
--hex_resolution INT # H3 resolution 1-15 (default: 8)
# Lower = larger hexagons, faster
# Higher = smaller hexagons, more detail
# Recommended: 7-9 for district analysis
--osrm_host TEXT # OSRM server host (default: 'localhost')
--osrm_port INT # OSRM server port (default: 5000)
--outdir PATH # Output directory (default: 'results')nextflow run spatial_access_workflow.nf --helpresults/
├── grids/
│ ├── District_A_grids.geojson # Hexagonal grids
│ └── District_A_accessibility.geojson # Grids with accessibility data
│
├── metrics/
│ └── District_A_metrics.csv # Facility-level statistics
│
├── maps/
│ ├── District_A_accessibility.png # Catchment area map
│ └── District_A_population.png # Population distribution
│
├── summary.csv # Combined metrics for all districts
├── report.html # Nextflow execution report
├── timeline.html # Timeline visualization
└── trace.txt # Detailed execution trace
The workflow outputs facility-level metrics:
- population_served: Total population in assigned grids
- pop_weighted_distance_km: Population-weighted average distance
- mean_distance_km: Average distance across all grids
- median_distance_km: Median distance
- pop_within_5km: Population within 5km of facility
- pop_within_10km: Population within 10km
- pop_within_20km: Population within 20km
- percent_within_5km: % of served population within 5km
Cause: Coordinates are projected (UTM, etc.), not lat/long
Solution:
python3 scripts/00b_convert_coordinates.pyCause: Different column naming
Solution: Rename columns to: name, latitude, longitude
import pandas as pd
df = pd.read_csv('facilities.csv')
df = df.rename(columns={
'Facility_Name': 'name',
'Lat': 'latitude',
'Lon': 'longitude'
})
df.to_csv('facilities.csv', index=False)Solution: Reduce H3 resolution:
--hex_resolution 7 # or even 6 for large regionsSolution: Workflow uses straight-line distance × 1.4 as fallback. This provides reasonable estimates.
If the workflow fails partway through:
nextflow run spatial_access_workflow.nf -resume \
--osm_pbf data/region.osm.pbf \
--districts_shp data/districts.shp \
--facilities_csv data/facilities.csv \
--population_tif data/population.tifThe -resume flag skips already-completed steps.
# 1. (Optional) Validate data
python3 scripts/00_validate_data.py
# 2. (Optional) Convert coordinates if needed
python3 scripts/00b_convert_coordinates.py
# 3. Run workflow
nextflow run spatial_access_workflow.nf \
--osm_pbf data/roads/togo-latest.osm.pbf \
--districts_shp data/boundaries/geoBoundaries-TGO-ADM2.shp \
--facilities_csv data/facilities/Togo_Health_Facilities_wgs84.csv \
--population_tif data/population/tgo_pop_2025_CN_100m_R2025A_v1.tif \
--facility_type healthcare \
--hex_resolution 8 \
--outdir results/togo_healthcare- Nextflow ≥ 23.0.0
- Docker (or Singularity)
- RAM: 16GB recommended (8GB minimum)
- Disk: 2-10GB depending on region size
curl -s https://get.nextflow.io | bash
sudo mv nextflow /usr/local/bin/Follow instructions at: https://docs.docker.com/get-docker/
This workflow is designed to be:
Generalizable:
- ✓ Works for any country or region
- ✓ Handles multiple facility types (schools, healthcare, etc.)
- ✓ Accepts various data formats (Shapefile, GeoJSON, GeoPackage)
- ✓ Auto-detects and reprojects coordinate systems
- ✓ Flexible CSV column naming
FAIR (Findable, Accessible, Interoperable, Reusable):
- ✓ Clear directory structure
- ✓ Standard geospatial formats
- ✓ Open-source tools only
- ✓ Containerized for reproducibility
- ✓ Well-documented with examples
Edit nextflow.config for your cluster:
profiles {
cluster {
process.executor = 'slurm'
process.queue = 'normal'
process.clusterOptions = '--account=your-account'
}
}Then run:
nextflow run spatial_access_workflow.nf -profile cluster ...- Resolution 6: ~36 km² hexagons (regional analysis)
- Resolution 7: ~5 km² hexagons (district analysis)
- Resolution 8: ~0.7 km² hexagons (default, local analysis)
- Resolution 9: ~0.1 km² hexagons (detailed analysis)
If you use this workflow, please cite:
- Nextflow: Di Tommaso P, et al. (2017) Nextflow enables reproducible computational workflows. Nature Biotechnology 35, 316–319
- H3: Uber H3: https://h3geo.org/
- OSRM: Luxen D & Vetter C (2011) Real-time routing with OpenStreetMap data. ACM GIS
MIT License
For questions or issues:
- Check this README
- Run
python3 scripts/00_validate_data.py - Open a GitHub issue
Rutendo Sibanda, 2025