DEV Community

Yosuke Hanaoka
Yosuke Hanaoka

Posted on • Edited on

Creating Maps in Streamlit Apps using Folium

Introduction

In this blog post, I will show you how to use Folium to create maps in a Streamlit application. In the sample app, I will use the following two data provided by the CITY OF VANCOUVER OPEN DATA PORTAL.

What I Made

  • MarkerCluster Example
    Screenshot1-EV Charging Stations in the Vancouver

  • GeoJson Example
    Screenshot2-Local Area Boundary in the Vancouver

Code & Explanation

Preparation

Importing libraries

import csv
import pandas as pd
import streamlit as st
import folium
from folium import plugins
from streamlit_option_menu import option_menu
Enter fullscreen mode Exit fullscreen mode

I think everyone can understand csv, pandas, streamlit and forlium. So, I don't need to explain about them.

I used streamlit_option_menu to create a menu in the sidebar. I know that there is a component called streamlit-forlium but I didn't use it this time.

Page Setting

st.set_page_config(
    page_title=None,
    page_icon=None,
    layout="wide",
    initial_sidebar_state="auto",
    menu_items=None,
)
Enter fullscreen mode Exit fullscreen mode

The layout was specified as wide because I wanted to display the map as large as the full width of the screen.

Creating a Map with MarkerCluster

map1 = folium.Map(location=[49.255, -123.13], zoom_start=12)
marker_cluster = plugins.MarkerCluster().add_to(map1)

data = read_csv_data(datafile1)
for station in data:
    location = [station["latitude"], station["longitude"]]
    folium.Marker(
        location,
        popup=folium.Popup(
            "<b>Operator:</b><br>" + station["operator"] + "<br>"
            "<b>Address:</b><br>" + station["address"],
            max_width=450,
        ),
    ).add_to(marker_cluster)
Enter fullscreen mode Exit fullscreen mode

Firstly, I need to do the basic settings for the map with "folium.Map()". The center position of the map (latitude and longitude) and zoom are specified as the minimum settings. Shifting the values a little bit at a time, I looked for a value where Vancouver would just fit.

Next, generate a layer of marker clusters by "plugins.MarkerCluster()” and add them to the map I just created. MarkerCluster is one of folium's many Plugins. Using this, markers placed on the map can be grouped together when the map is zoomed out. The number of markers in the cluster is displayed instead of the number of markers in the map. When the map is zoomed in, the markers are displayed one by one.

Finally, marker information contained in the CSV file is retrieved one by one and added to the marker cluster layer.

Creating a Map with GeoJson

map2 = folium.Map(location=[49.255, -123.13], zoom_start=12)

popup = folium.GeoJsonPopup(
    fields=["name"],
    aliases=["Area Name:"],
)

folium.GeoJson(
    datafile2,
    popup=popup,
).add_to(map2)
Enter fullscreen mode Exit fullscreen mode

The basic map settings are the same. Then add GeoJSON data to the map. GeoJSON data itself is provided by CITY OF VANCOUVER OPEN DATA PORTAL, and I didn't edit the file.

We can display a popup by specifying the argument named popup in folium.GeoJson. folium has a function called "GeoJsonPopup" that retrieves all specified information from a JSON file and displays it in a popup.

Displaying a Map on the Screen

st.header("EV Charging Stations in the Vancouver", divider=True)
st.components.v1.html(folium.Figure().add_child(map1).render(), height=500)

st.header("Local Area Boundary in the Vancouver (GeoJSON)", divider=True)
st.components.v1.html(folium.Figure().add_child(map2).render(), height=500)
Enter fullscreen mode Exit fullscreen mode

Streamlit's Components API is used here. Please check the following url for details. (Components API Reference)

There are two types of Streamlit components: static components and bi-directional components. This time, my goal is solely to render a map from a Python visualization library (forlium); I used st.components.v1.html

It is very easy to use and is as follows.

Function signature
st.components.v1.html(html, width=None, height=None, scrolling=False)
Parameters
html (str) The HTML string to embed in the iframe.
width (int) The width of the frame in CSS pixels. Defaults to the app's default element width.
height (int) The height of the frame in CSS pixels. Defaults to 150.
scrolling (bool) If True, show a scrollbar when the content is larger than the iframe. Otherwise, do not show a scrollbar. Defaults to False.

The HTML returned from the forlium’s render method is passed as the first parameter, the width is not specified but left to the screen size, and the height is set to 500px.

As explained in the Streamlit documentation, the result is incorporated as an iframe.

Screenshot3-Development Tool

By the way, I didn't use bi-directional component in this blog post, but it is something not simply controlled and displayed only from the Streamlit (Python) side but instead receives input values from the component's UI, passes them to the Streamlit (Python) side for processing and then returns the results to the component side.

I want to try bi-directional component next time.

That's all for this blog post!

Top comments (0)