In this notebook we will demonstrate how we can use Climate Tools and dhis2eo to retrieve climate data for a set of DHIS2 organisation units, based on the ERA5-Land dataset, hosted at the ECMWF Climate Data Store (CDS).
Important: Make sure you have followed these instructions to authenticate and allow API access the CDS portal.
import geopandas as gpd
import dhis2eo
from dhis2eo.data.cds import era5_land
from dhis2eo import utilsIn order to know which geographical area we should download data for, we also load our Sierra Leone organisation unit GeoJSON file downloaded from DHIS2. Alternative, see our guide for how to fetch organisation units directly from your DHIS2 instance.
org_units = gpd.read_file('../../data/sierra-leone-districts.geojson')
org_unitsDownloading ERA5-Land data¶
The dhis2eo.data.cds.era5_land module allows you to easily download climate data for different time periods from the ERA5-Land dataset.
Hourly data¶
To import daily ERA5-Land data into DHIS2, we recommend downloading ERA5-Land data at hourly levels, and then aggregating up to the daily level. This is because the ERA5-Land at daily levels does not include the precipitation variable.
Downloading this data can be done by calling the function dhis2eo.data.cds.era5_land.hourly.get(...). By default, this downloads the most commonly requested ERA5 climate variables: 2m temperature and total precipitation, but you can also set the variables parameters to any of the ERA5-Land variables.
Due to the large amounts of data, this dataset only allows downloading one month at a time. We therefore need to loop over each year and month, and call the download function for each year and month value.
For the purpose of this notebook, let’s download data for the last 3 months:
start_year = 2025
start_month = 7
end_year = 2025
end_month = 9
for year,month in utils.time.iter_months(start_year, start_month, end_year, end_month):
# download the climate data
print(f'Month: {year}-{month}')
hourly_data = era5_land.hourly.get(year=year, month=month, bbox=org_units.total_bounds)
# do something with the data
# e.g. save to disk, aggregate, or import to DHIS2
# ...Month: 2025-7
dhis2eo.data.cds.era5_land.hourly - INFO - Downloading data from CDS API...
dhis2eo.data.cds.era5_land.hourly - INFO - Request parameters:
{"variable": ["2m_temperature", "total_precipitation"], "year": "2025", "month": ["07"], "day": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"], "time": ["00:00", "01:00", "02:00", "03:00", "04:00", "05:00", "06:00", "07:00", "08:00", "09:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00", "19:00", "20:00", "21:00", "22:00", "23:00"], "area": [10.0004, -13.3035, 6.9176, -10.2658], "data_format": "netcdf", "download_format": "unarchived"}
2025-12-02 15:26:15,126 INFO Request ID is 5138f84b-a41e-4e3b-97f2-ac946e0d5c0e
2025-12-02 15:26:15,222 INFO status has been updated to accepted
2025-12-02 15:26:20,208 INFO status has been updated to running
2025-12-02 15:30:33,937 INFO status has been updated to successful
Month: 2025-8
dhis2eo.data.cds.era5_land.hourly - INFO - Downloading data from CDS API...
dhis2eo.data.cds.era5_land.hourly - INFO - Request parameters:
{"variable": ["2m_temperature", "total_precipitation"], "year": "2025", "month": ["08"], "day": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"], "time": ["00:00", "01:00", "02:00", "03:00", "04:00", "05:00", "06:00", "07:00", "08:00", "09:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00", "19:00", "20:00", "21:00", "22:00", "23:00"], "area": [10.0004, -13.3035, 6.9176, -10.2658], "data_format": "netcdf", "download_format": "unarchived"}
2025-12-02 15:30:40,010 INFO Request ID is c57b7cb6-ad45-4ab3-92b8-11119acb3da1
2025-12-02 15:30:40,073 INFO status has been updated to accepted
2025-12-02 15:30:48,692 INFO status has been updated to running
2025-12-02 15:34:58,896 INFO status has been updated to successful
Month: 2025-9
dhis2eo.data.cds.era5_land.hourly - INFO - Downloading data from CDS API...
dhis2eo.data.cds.era5_land.hourly - INFO - Request parameters:
{"variable": ["2m_temperature", "total_precipitation"], "year": "2025", "month": ["09"], "day": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30"], "time": ["00:00", "01:00", "02:00", "03:00", "04:00", "05:00", "06:00", "07:00", "08:00", "09:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00", "19:00", "20:00", "21:00", "22:00", "23:00"], "area": [10.0004, -13.3035, 6.9176, -10.2658], "data_format": "netcdf", "download_format": "unarchived"}
2025-12-02 15:35:01,464 INFO Request ID is ffc2c8c6-45e4-4ccd-9034-828f6c459be1
2025-12-02 15:35:01,527 INFO status has been updated to accepted
2025-12-02 15:35:15,100 INFO status has been updated to running
2025-12-02 15:37:53,934 INFO status has been updated to successful
To inspect the contents of the downloaded data, let’s view the data of the last element in the loop:
hourly_dataWe see that this is hourly data for the month of September 2025, and contains data variables t2m (2m temperature), and tp (total precipitation).
Note: Since DHIS2 does not deal directly with hourly data, you will also need to aggregate from hourly to daily data before you can import the data to DHIS2.
Monthly data¶
Sometimes you may only be interested in monthly climate data at longer time scales. In these cases, we recommend downloading the monthly ERA5-Land data.
These data can be downloaded by calling the function dhis2eo.data.cds.era_land.monthly.get(...). Similarly to the hourly data, the default variables are temperature and precipitation, and can be customized be specifying the variables parameter.
Since monthly data takes a lot less space, you can download all the data you need by specifying a list of years and a list of months.
Let’s download monthly data for the last 10 years:
years = list(range(2015, 2025+1))
months = list(range(1, 12+1))
monthly_data = era5_land.monthly.get(years=years, months=months, bbox=org_units.total_bounds)dhis2eo.data.cds.era5_land.monthly - INFO - Downloading data from CDS API...
dhis2eo.data.cds.era5_land.monthly - INFO - Request parameters:
{"product_type": ["monthly_averaged_reanalysis"], "variable": ["2m_temperature", "total_precipitation"], "year": ["2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"], "month": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"], "time": ["00:00"], "area": [10.0004, -13.3035, 6.9176, -10.2658], "data_format": "netcdf", "download_format": "unarchived"}
2025-12-02 15:37:55,973 INFO Request ID is f7ae0c9c-9788-46e1-820f-30062d243c98
2025-12-02 15:37:56,057 INFO status has been updated to accepted
2025-12-02 15:38:17,403 INFO status has been updated to running
2025-12-02 15:38:28,982 INFO status has been updated to accepted
2025-12-02 15:38:46,151 INFO status has been updated to successful
And inspect the results:
monthly_dataWe see the result contains 130 months (over 10 years) of data for temperature and precipitation variables.
Important note about monthly precipitation!¶
The monthly version of ERA5-Land reports all variables as daily averages. For some of the variables such as precipitation, we are instead interested in the total precipitation for the entire month. This can be done by multiplying the average daily precipitation (tp) with the number of days per month (xarray date fields provide this as an attribute .dt.days_in_month):
total_precip = monthly_data['tp'] * monthly_data.valid_time.dt.days_in_monthNext steps¶
This notebook has showed how to download both hourly and monthly ERA5-Land climate data. For guidance on what to do with the data after downloading, see:
If you want to skip to the end, you can also check out this example workflow that includes all the steps needed to download and import ERA5-Land data from start to finish.