Creating Interactive Maps: The Easy way with Python, Folium and Leaflet.js

Read data with geopandas, create and add markers for each location on your map.

Chibuzor Nwachukwu
3 min readMar 17, 2023
World map looking green :)
Photo by Monster on Pexels

Many times, as a data scientist, you will come across data with coordinates and geospatial features. It sure would be bang if you can build a nice map to gain insights into the general spread of data.

In this brief tutorial, we will be designing an interactive map even without deep knowledge of Folium or Shapefiles.

Getting started

Requirements

  • Python 3.7 or higher
  • Geopandas
  • Folium 0.12.0 or higher

Our data

The data we’ll be working with is Commodity Processing Points from the World Bank Data Catalog.

It holds information on the locations of commodity processing points in Nigeria; both existing and potential processors.

What you need to know

A shapefile is a geospatial, non-topological vector format for storing the geometric location and geographic features. Most geospatial analysis -including ours today, involves data in this format.

Folium is a powerful Python library that helps you create several types of Leaflet maps. It makes it easy to visualize and bind data that’s been manipulated in Python on an interactive leaflet map.

Leaflet is an open-source JavaScript library for simple, efficient, mobile friendly and interactive maps. It is loved for its plugins, and uncomplicated API. For a bunch of interesting leaflet tutorials, click here

Let the coding begin !

Importing geopandas and folium

import geopandas as gpd
import folium

Getting our data

aqua_data    = gpd.read_file("data/commodity_processing_pts/Aquaculture.shp")
cassava_data = gpd.read_file("data/commodity_processing_pts/Cassava.shp")
cocoa_data = gpd.read_file("data/commodity_processing_pts/Cocoa.shp")
cotton_data = gpd.read_file("data/commodity_processing_pts/Cotton.shp")
oil_data = gpd.read_file("data/commodity_processing_pts/Palm_oil.shp")
rice_data = gpd.read_file("data/commodity_processing_pts/Rice.shp")
sorghum_data = gpd.read_file("data/commodity_processing_pts/Sorghum.shp")

Next step is to concatenate the datasets to a more comprehensive one processing_pts

processing_pts = pd.concat([aqua_data, cassava_data, cocoa_data, cotton_data, oil_data, rice_data, sorghum_data], axis= 0)
processing_pts = processing_pts.reset_index().drop(["index","Id"],axis=1)
processing_pts.head()

Now we can have a better look

a view of the first five data points

As we know it, real-world data is almost never perfect, now we get to some cleaning : )

#Fixing the inconsistencies in the descript column
processing_pts["descript"].replace("Potential State","Potential processor", inplace=True)
processing_pts["descript"].replace(["Existing Processor", "Existing processor",
"Existing processors", "Existing Processors"],
"Existing processor", inplace=True)

The geometry column keeps track of the underlying GEOS geometry in form of coordinate points. To plot the map we have unzip the geometric points into LATITUDE and LONGITUDE columns

processing_pts['LATITUDE']  = processing_pts.geometry.y
processing_pts['LONGITUDE'] = processing_pts.geometry.x

Now we can initialize our map

my_map = folium.Map(location = [processing_pts['LATITUDE'].mean(), processing_pts['LONGITUDE'].mean()], 
zoom_start = 6.2, control_scale = True)
my_map

Although I used a zoom_scale 6.2x, you are free to modify and adjust the view of the map.

Setting up point markers and a nice popup

for index, location_info in processing_pts.iterrows(): 
folium.Marker([location_info["LATITUDE"], location_info["LONGITUDE"]],
popup = "<span style='font-size:1.9rem; color: green;'>%s</span><br><b>Status: </b>%s<br><b>Coordinates: </b>(%f, %f)" %(location_info["Commodity"], location_info["descript"], location_info["LONGITUDE"], location_info["LATITUDE"]),
icon = folium.Icon(color="green")).add_to(my_map)
my_map

Adding layers to our map using folium.raster_layers

folium.raster_layers.TileLayer('Open Street Map').add_to(my_map)
folium.raster_layers.TileLayer('Stamen Terrain').add_to(my_map)
folium.raster_layers.TileLayer('Stamen Toner').add_to(my_map)
folium.raster_layers.TileLayer('Stamen Watercolor').add_to(my_map)

# add layer control to show different maps
folium.LayerControl().add_to(my_map)

And voila !

my_map
Nigerian map with different layers

Saving your masterpiece !

my_map.save("All food processing points.html")

Conclusion

Folium is easily one of the best libraries for map building, because with just a few lines of code, a whole lot can be done. Check out the documentation

To view the Jupyter Notebook here. To check out a live version of the map

--

--

Chibuzor Nwachukwu

I write about the science of data, software and algorithms