PyFriday Tutorial: Creating Leaflet Maps in Python with Folium and GeoPandas
Introduction
Welcome to this PyFriday tutorial! Today, we’re diving into a project where we’ll use Python to create an interactive map that shows critical health infrastructure across the United States. Imagine you’ve been assigned a project to understand the distribution of two types of hospitals:
- Level I Trauma Centers: These are hospitals with the highest capability to handle emergency trauma cases.
- General Hospitals with 100+ Beds: These hospitals are large enough to handle significant patient loads.
Our goal is to help public health analysts quickly understand where these resources are located, providing insights into how well regions are served by medical facilities.
In this tutorial, we will be using Folium for mapping, GeoPandas for handling geographic data, and Pandas for data processing. Let’s get started with a step-by-step approach!
1. Importing Necessary Libraries
The first step in any Python project is to import the necessary libraries. Here, we’re using Folium, GeoPandas, and Pandas:
Each library has its unique role in this project:
folium
: This helps create interactive maps that you can view in a web browser.pandas
: Used for handling and analyzing data, allowing us to manage non-geographic parts of our datasets.geopandas
: Extends Pandas to include spatial data, which is essential for working with maps and geographic information.
2. Loading the Data
Next, we load the hospital and county boundary data. This includes a GeoJSON file for hospitals and a shapefile for county boundaries:
hospitals = gpd.read_file()
: This command reads a GeoJSON file containing hospital information.counties = gpd.read_file()
: This command reads a shapefile with county boundaries, which helps us identify the geographic regions we are mapping.
You may have noticed they’re the same command; GeoPandas read_file()
can take multiple file types due to the nature of the function itself. Both datasets are loaded into GeoDataFrames
, which are like Pandas DataFrames but are specially designed to work with geographic data.
3. Handling JSON Serialization Issues
Sometimes, the hospital data contains columns that cannot be directly converted into JSON format, such as datetime fields. This can cause issues when adding data to the map, and unlucky for us, we have some datetime objects in our geojson data. To solve this, we convert any datetime
columns to strings:
For the above chunk, we’re using hospitals.select_dtypes(include=['datetime', 'datetimetz'])
to go into the hospitals DataFrame, then selecting any columns with the data type of datetime or the alternative datetimetz, and once these are selected, we simply use the hospitals[column] = hospitals[column].astype(str)
to reassign the type from a date type to a type of string. By converting datetime fields to strings, we make the data compatible for visualization. Folium uses GeoJSON, which requires all data to be serializable (i.e., easy to convert into a JSON format that the browser can read).
4. Filtering the Data for Specific Criteria
General Hospitals with 100+ Beds
We only want to display general medical and surgical hospitals with 100 or more beds, as these represent significant healthcare resources:
Here, we use filtered_hospitals_points
to create a new dataset from the hospitals
data, but only keep rows that match our conditions:
- The hospital must be of type “GENERAL MEDICAL AND SURGICAL HOSPITALS”.
- It must have at least 100 beds.
This ensures that our map only shows larger hospitals capable of handling more patients. As an aside, this also greatly reduces the amount of data being used, and thus the end product will be more manageable. To create an end product for all hospitals in a way similar to how we’re doing it would create an interactive map over 10GB in size.
5. Adding Tooltips for Hospital Information
To make our map interactive, we add tooltips. A tooltip is the information box that pops up when you hover over a point on the map. We add details like hospital name, address, and bed count:
The tooltips give more context to users of the map. You’ll notice that within the pop-up, html is used for bolding and creating new lines, and we can call values to be displayed by pointing to the column name, with the row being the location being clicked on.
6. Creating the Base Map
Now it’s time to create our base map. We will center it on the USA, with a zoom level of 5:
Explanation for New Users: The folium.Map()
function creates a basic map that will serve as the canvas. We use:
location=[39.8283, -98.5795]
to center the map roughly over the USA.zoom_start=5
sets the initial zoom level, giving a wide view of the country. Feel free to mess around with this setting to find your preferred view.
7. Adding Hospital Data to the Map
We can now add a layer to display the general hospitals with 100+ beds. This layer will be initially hidden so users can choose to see it:
We use folium.FeatureGroup
to create this group of features (in this case, hospital points).
8. Visualizing Trauma Level I Facilities by County
Now we’ll add information about Level I trauma facilities, focusing on county-level aggregation:
- We filter the hospital data to only include hospitals with Level I trauma centers.
- We then count how many such hospitals exist in each county using
groupby
andmerge
the results with our county data. - For any counties that have no Level I trauma centers, we then fill the NA values with 0.
- This gives us the data we need to create a choropleth layer, showing which counties have Level I trauma facilities.
9. Adding Styling and Tooltips for Trauma Layers
To make the trauma data more visually informative, we define a color scale based on the number of Level I trauma centers in each county. This helps users quickly see which areas may have potential gaps or much higher expenses associated with accessing care.
We use different shades of color to visually communicate the distribution of trauma facilities. The darker the color, the more trauma centers are present in that county. We also are joining our hospital locations to our county layer, making it so that users can see exactly where facilities are, alongside a count of how many facilities are available by county.
10. Adding a Custom Legend
A map is much easier to interpret if it has a legend that explains the color coding. We add a custom HTML legend to the map:
This custom HTML legend helps users understand what the different colors on the map represent, making it much more intuitive to interpret. There is a weakness to this approach in that it won’t adjust if there’s a sudden influx or removal of facilities, but considering the fact most Trauma I facilities last for decades and take a long time to build, this isn’t so much of a concern here.
11. Finalizing the Map with Layer Control and Saving
To complete our interactive map, we add a layer control widget that allows users to toggle between different layers:
Finally, we save the map to an HTML file so it can be viewed in a web browser:
Saving the map as an HTML file means that anyone with a browser can view it. It also makes sharing easy, allowing the map to be embedded in websites or shared directly. It should be noted though that depending on the size of your map, some ways of sending data (e.g. email) may not be ideal.
Checking Results
Now that we have a map, let’s open it up and check our results. Keep in mind you can adjust layers using the sandwich marker on the right hand side.
Conclusion and Further Improvements
Congratulations on completing this project! You now have a functional, interactive map that provides insights into the distribution of major hospitals and trauma centers in the United States. This project has covered some core concepts:
- Loading and cleaning geospatial data
- Using Folium and GeoPandas to create interactive maps
- Adding informative layers and tooltips to enhance map usability
Ideas for Further Development
- Add Real-Time Data: Integrate live data feeds (e.g., emergency response units) to make the map dynamic and responsive to ongoing events.
- Additional Data Layers: Add new layers, such as healthcare shortages or hospital capacity updates, to provide a more comprehensive view.
- Filter Options: Allow users to filter hospitals by bed count, type, or other criteria through an interactive interface.
- Enhanced Styling: Experiment with different color scales or icons to improve visual aesthetics and clarity.
We hope this tutorial has helped you feel more comfortable with mapping in Python! Keep in mind this is an entire field on its own, so there’s plenty more to learn!
References
Humanities Moment
The featured image for this article is Boy with Map of Scandinavia (1771) by Ulrika Pasch (Swedish, 1735-1796). Ulrika “Ulla” Fredrica Pasch was a prominent Swedish Rococo painter and miniaturist. Born into an artistic family, she was the daughter of painter Lorens Pasch the Elder and the sister of Lorens Pasch the Younger. Trained by her father, Ulrika began her professional career in the 1750s, eventually establishing her own studio and clientele. In 1773, she became the only female member inducted into the Royal Swedish Academy of Arts that year. Throughout her career, Pasch was commissioned by members of the Swedish aristocracy and royal court, earning recognition as one of the most successful female artists in Sweden before the 19th century.