How to Create a Population Density Heat Map in QGIS and Python
QGIS is a wonderful open-source mapping software that can be used to create many types of maps. In this tutorial, I will show you how to create an aesthetic population density map with examples and pictures. I will use QGIS and some simple Python code for this tutorial.
Downloading and Manipulating the Data
For population density maps, obtaining usable and accurate data is critical. For population density maps (especially for countries), CSV files with city population and location data are best. There are many websites where you can download different countries’s city data from, such as Back4App or SimpleMaps. The dataset must contain a comprehensive list of cities in the country with population, longitude, and latitude for each city. For this tutorial, we will use this dataset on Brazilian cities found here. Download the data.
The dataset contains a wealth of data for each city, but we are only interested in the population and coordinate data. However, before we can start mapping the data, we must first manipulate it slightly for it to be usable.
We want to add an additional row that represents the log_base2 population for each city and removes all cities with no population data. We want to use a logarithmic scale to represent our map because our heat map will be weighted by population, and without using a logarithmic scale, the larger cities like São Paulo or Rio de Janeiro would overshadow all other cities.
Open your downloaded file with your preferred code editor that can read python code. Then paste this code below into a python file in the directory. We are going to run a short script to manipulate the data slightly.
import csv
import mathwith open("BRAZIL_CITIES_REV2022.CSV", "r") as file:
reader = csv.reader(file)
i = 0
with open("EDITED_BRAZIL.csv", "x", newline="") as output:
writer = csv.writer(output)
for row in reader:
if i == 0:
writer.writerow(row + ["log_scale_pop"])
i = 1
continue
pop = int(row[3])
if pop > 0:
writer.writerow(row + [math.log(pop, 2)])
This code creates a new file called EDITED_BRAZIL.csv that contains a new column with the log_base2 value of the population.
Mapping the Data
Next, open QGIS. Type “world” into the coordinate box in the bottom left. This will create a simple basemap to guide us.
Make sure that your map is in EPSG:4326 (You can check in the bottom right corner). Next click “Open Data Source Manager” in the top left corner. This should open a window with multiple possible data inputs. Since our file is a CSV file, click “Delimited Text”. Then select the EDITED_BRAZIL.csv file as your input file and set LONG as your x field and LAT as your y field.
Your map now should look like this.
The map currently shows a single dot for each city, but we want to create a heat map of population density, not just cities. For that, we must use some QGIS-built in functions.
Click Processing -> Toolbox to open a number of QGIS functions. Then put “heatmap” into the toolbox search bar.
Click on the heatmap function. Here is where you can decide on your own setting depending on how you want to represent your data. Select EDITED_BRAZIL.csv as your input file and make sure you set your “Weight from field” to log_scale_pop. Also you can decide on the radius of effect of each one of your cities. I put 0.175 for the radius in this case. You don’t have to change any other values, but I would recommend increasing the number of rows and columns. I have set rows to 12,000 in this example. Something important to note, increasing the number of rows/columns will make the function run much slower.
Run the function. This might take a while, depending on how many rows and columns the program has to calculate.
The resulting heatmap should look somewhat like this.
It is in grayscale by default, but you change it by double clicking the new Heatmap layer, then Symbology and then change the Render type from Singleband Gray to Singleband Pseudocolor. You can then choose from one of the provided QGIS color ramps or make your own one. I will use the QGIS magma color ramp for this example.
You might notice that many places in Brazil are unpopulated and not covered by the heatmap. To make the map look better, download a simple Brazil country shapefile from here. Download the shapefile for Brazilian Administrative Areas. Then drag the file BRA_adm0.shp to QGIS below the heatmap layer. Then change the shapefile color to the lowest color on your chosen color ramp (Black if you used magma). You can also delete or hide the EDITED_BRAZIL.csv file.
We are almost finished with our map. You might notice that our map looks a bit fuzzy at the borders or the coast. This is because our heatmap algorithm does not take into account borders. We have to clip our heatmap into the Brazilian shape. Click Raster -> Extraction -> Clip Raster by Mask Layer. Your input file should be Heatmap and your mask layer should be BRA_adm0. Check Keep resolution of input raster.
You do not have to put anything for the other settings. Click run. This can also take a while, depending on the resolution of your heatmap.
The Clip Raster by Mask Layer function creates a new layer called Clipped. Change the Clipped layer’s color ramp also to magma or your preferred color ramp. You can remove the other layers, besides the Brazil shapefile and the basemap. You are now done with all the mapping! Here is how my map looks now.
Exporting the Map
Click Project -> New Print Layout. This creates a new canvas where you can style and design your final map.
You can add your map by clicking Add Item -> Add Map and then dragging across the blank canvas. You can also add a title and a data source using text boxes. There is many things you could add, but since this is a qualitative map of just one country, there isn’t a point of adding a legend or other things. You can also choose to remove the gray basemap if you want to.
To export the image, click Layout -> Export as Image and you can select what resolution you want your final picture to be. And you’re done!
Here is how my final map looks like:
If you liked this tutorial, consider clapping, commenting, or following!