Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors
Filter by Categories
ArcGIS Pro

How To Create and host an interactive web map with Python

Tutorial: Create and host an interactive web map with Python

Interactive maps are a great medium for data visualization because they allow users to explore a dataset for themselves.  Most interactive maps have functionality for a user to pan & zoom around a map, click on markers, and get more data from popup windows. Creating an interactive map also highlights the creator’s ability to conduct back-end data acquisition and processing, as well as to design an effective front-end user experience.

For this reason, adding an interactive map to your portfolio is a stellar way to showcase your ability to clients, make yourself stand out to potential employers, or share a personal project with the wider community.

We will walk through the entire workflow to produce an interactive map of London historical markers from data acquisition and processing, through map design and hosting online. 


When exploring London city streets, you can immerse yourself in history by reading some of the thousands of historical markers connecting locations with notable events, buildings, and people throughout history. However, not everyone will have the chance to do this in person. An interactive map is an ideal medium for users to explore and learn more about the history of London from a database of historical markers.

A picture containing text, ground

Description automatically generated

Example of one of many historical markers spread across London and the rest of the UK. Image from:

Although we choose historical plaques in London, the general workflow here can be followed for any other dataset and/or location. We highly suggest you choose a location and dataset of interest to you — data research and acquisition are fantastically useful skills! See the post on “Free GIS Data and Where to Find it”  for some good tips.

If you find this tutorial helpful in making a map of your own please share it with us on Twitter @MapScaping!


A Python 3.8 environment with the following packages: Folium 0.12.1, GeoPandas 0.9.0, and Pandas 1.4.2. If you set up a geospatial Python environment following our tutorial here, you’re already good to go.

Part 1 – Data Acquisition

We will source London borough boundaries and historic plaque data from the following sources.

OpenPlaques is a crowdsourced database of historical markers. We use data from the UK only, but the database includes historic markers across the globe (consider aiming your map at your favorite city!)

Using Pandas and GeoPandas, read the data into GeoDataFrames.

import folium

from folium.plugins import MarkerCluster

import geopandas as gpd

import pandas as pd

# Read in UK Historical Plaques data

plaques_df = pd.read_json(“”).dropna().drop(‘updated_at’, axis=1)

plaques = gpd.GeoDataFrame(

    plaques_df, geometry=gpd.points_from_xy(plaques_df.longitude, plaques_df.latitude, crs=4326)


plaques_df = None

# Read in London boroughs data

boroughs = gpd.read_file(“”)

Part 2 – Data Processing

In this example, we will incorporate both a clickable point layer and a polygon choropleth layer. To connect our choropleth layer to the Plaques point features, we perform a spatial join to extract the number of historical Plaque points that are contained within each Borough polygon.

# Find the number of plaques in each borough

boroughs = boroughs.join(

            gpd.sjoin(plaques, boroughs).groupby(“index_right”).size().rename(“numPlaques”),



Knowing how many markers are in each borough is a fairly useful metric, but we can do better. Say you are planning a trip and would like to see as many Plaques as possible in a single day, knowing the Plaque density would be a more useful metric. 

The Boroughs GeoDataFrame contains an area field in hectares. We convert this is more intuitive square kilometers and calculate the Plaque density for each Borough polygon.

# Calculate the plaque density

boroughs[‘area_sqkm’] = boroughs[‘area_hectares’] / 100

boroughs[‘PlaqueDensity_sqkm’] = boroughs[‘numPlaques’] / boroughs[‘area_sqkm’]

Now we have the data in the proper formats and are ready to plot on a map.

Part 3 – Create a Map with Folium

Initializing a map with Folium is as simple as calling the “Map” function with the coordinates of the map center.

map = folium.Map(location=[51.507351, 0.127758])


There are several parameters we can apply including defining the zoom_start and base map style. Folium uses OpenStreetMap as the base map by default, but we choose Stamen Toner because the style is cleaner and looks more historical. We also reduce the map height to make room for a title.

# Initialize map over London

map = folium.Map(location=[51.507351, 0.127758], tiles=“Stamen Toner”, 

zoom_start=10, height=‘92%’, prefer_canvas=True)

# Add a title

title_html = ”’

             <h5 align=”center”; margin-bottom=0px; padding-bottom=0px; style=”font-size:20px”><b>Historical Plaques of London</b></h3>

             <h5 align=”center”; margin-top=0px; padding-top=0px; style=”font-size:14px”>Click on the blue circles for more info</h3>




Part 4 – Add Data and Style Map


There are 3370 markers in our historical Plaques dataset. Plotting each plaque as an individual marker would make the map hard to read, especially when zoomed out. We will utilize the MarkerCluster feature in Folium to aggregate nearby markers together when the map is zoomed out. After adding the features we will see that this makes the map look cleaner while still conveying the general location of historical Plaques.

The for loop below configures a popup, styles the Plaque marker, and adds the feature to the MarkerCluster layer.

# Create a marker for each plaque location. Format popup

marker_cluster = MarkerCluster().add_to(map)

for index, row in plaques.iterrows():

    html = f”””<strong>Title:</strong> {row[‘title’]}<br>


        <strong>Inscription:</strong> {row[‘inscription’]}<br>


        <strong>Erected:</strong> {row[‘erected_at’]}<br>


        Find more info <a href={row[‘uri’]} target=”_blank”>here</a><br>


    iframe = folium.IFrame(html,



    popup = folium.Popup(iframe,


    folium.CircleMarker(location=[row[“latitude”], row[“longitude”]],








First, we will define the styling of the London Boroughs layer. Folium includes the Branca library for choropleth color styling. We define the scale of the dataset and then apply a colormap. Branca has some built-in colormaps, but here we define our own. This color map was selected from the set of data-driven color schemes provided by CARTO. Lastly, we define a function to apply the color map, set opacity, and feature edge stroke.

# Choropleth styling

import branca.colormap as bcm

# Create color map

# “Fall” color ramp from

scale = (boroughs[‘PlaqueDensity_sqkm’].quantile((0, 0.02, 0.5, 0.9, 0.98, 1))).tolist()

colormap = bcm.LinearColormap(colors=[‘#008080’,‘#70a494’, ‘#b4c8a8’, ‘#edbb8a’, ‘#de8a5a’,‘#ca562c’], 




style_function = lambda x: {

    ‘fillColor’: colormap(x[‘properties’][‘PlaqueDensity_sqkm’]),

    ‘color’: ‘black’,

    ‘weight’: 1.5,

    ‘fillOpacity’: 0.3


Apply Boroughs features as a GeoJson overlay on the map with styling and a popup tooltip indicating the Borough name and number of Plaques.

Adding the colormap to the map adds a legend in the top right corner.

# Plot choropleth layer





        fields=[‘name’, ‘numPlaques’],

        aliases=[‘Borough’, ‘Historic Markers’],




colormap.caption = ‘Historic markers per sq. km’


Part 5 – Explore the Map!

That’s it! We have a great-looking map we can use to explore the dataset and learn about the history of London (and the rest of the UK too). You’ll notice that each of the Plaque popups contains a link to the OpenPlaques page with further information. Try looking around for yourself and see what interesting things you find. 

One marker significant to the Geospatial community can be found at the corner of Broadwick Street and Lexington Street in the Borough of Westminster (southeast of Regent Street and Oxford Street).

The sign is dedicated to Dr. John Snow, an English physician who made significant advances in epidemiology and public health. Snow is recognized as one of the first to use geospatial analysis to understand disease in his work proving that Cholera was being spread through the London drinking water in the mid-1800s.

Your map should look something like this.

Part 6 – Save and Host Online

Folium has the functionality to export a map view to a Leaflet HTML file that can be rendered in a web browser. Let’s export our map as HTML and also save our processed Plaques and Boroughs data while we’re at it.“./index.html”)

with open(“./UK-plaques-2021-06-26.json”, “w”) as outfile:


boroughs.to_file(“./london-boroughs.geojson”, driver=“GeoJSON”) 

There are several ways to host web maps online in HTML format. GitHub Pages is a great option for smaller maps. However, the map we generated in this tutorial is too large to load on GitHub. 

AWS S3 is a good option and offers a free tier of object storage up to 5GB. After creating an account, open the AWS s3 console, create a new bucket and apply the following settings: 

  • Under “Object Ownership”, Select “ACLs enabled”
  • Make sure to uncheck the “Block all public access” box as we want to be able to share the map publicly.

In your new bucket, upload the HTML file you exported.

Lastly, you need to make the file public. Select the file in the AWS s3 console. Under “Actions” select “Make public using ACL”

Now click on the file and copy the object URL. This is a link that you can share with the public to access your web map hosted on s3.


This tutorial should have given you the experience you need to create engaging interactive web maps. Try this out on a dataset of interest to you and feel free to share the results with us on Twitter & LinkedIn.

Happy mapping 😊

About the Author
I'm Daniel O'Donohue, the voice and creator behind The MapScaping Podcast ( A podcast for the geospatial community ). With a professional background as a geospatial specialist, I've spent years harnessing the power of spatial to unravel the complexities of our world, one layer at a time.