How to render a React page into Streamlit and build an interactive map

Qi Chen
7 min readApr 5, 2021

Introduction

In the last blog, we talked about how to visualize airport centrality score on web page. We set up TigerGraph database and data visualization in Streamlit. In this article, we will explore weighted shortest path between different airports. We are going to use the same database as last blog, and dive into ‘Streamlit Component’ which could render React page in it.

Why I built this project?

I intended to built an interactive map that let users select markers on the map and let Streamlit get the selected marker data. It seems like there is no directly built-in widget or function that could implement this. Luckily, Streamlit provides us a special Component that can communicate from python to JavaScript and back. In this case, an interactive map can be perfectly built within Streamlit.

Import Library and Create a React App

First, we need to import Streamlit library in python.

import streamlit as st

Then create a typescript React app:

npx create-react-app your-app-name --template typescript
#or
yarn create react-app your-app-name --template typescript

Install Streamlit component to React:

npm install streamlit-component-lib

(Optional) Create another backend to deal with HTTP request in React:

First, create a new directory called react-backend. Then type these in terminal

npm init
npm install --save body-parser
npm install --save express
npm install --tigergraph.js

NOTE: if your react-app doesn’t need to send http request, or your http request doesn’t need to use Cross-Origin Resource Sharing (CORS), skip this step.

Get Started with Google Maps API

Step 1: Create a Google Cloud Platform account

Google get started page

Step 2: Create a new project for our app on Google Cloud Platform

Create a new project in Google Cloud Platform

Step 3: Enable Geocoding API and Maps JavaScript API under the project you just created

Google Cloud Platform — API page

Step 4: After configuring consent screen, you will see a new API key generated in Credentials, copy that API key

Step 5: Go to your-react-app-directory/public/index.html, and insert

<script
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>

into the html file, replace YOUR_API_KEY with the API key you just copied.

Step 6: install google map package in the project

npm i -D @types/google.maps

Graph Database Preparation

Calculate airports distance

Since we want to find the shortest path from one airport to another in a weighted graph, we need to calculate the distance between each airport(Vertex) and save it to database. You could find a query called ‘addWeights’ which is one of the default query in graph. We need to run this query to calculate the distance between each airport. The parameter e-type means edge type. You need to type the edge flight_route when you run this query. After the query finished, the calculated distance would be saved in edge flight_route.

query result

Search airports by country

In last blog, we already learned how to search airports by country, I will not show the gsql in there, please check the gsql statement in my last blog.

Get shortest path between two airports

Here is the query how to get shortest path between two airports:

CREATE QUERY shortest_path_weighted(/* Parameters here */VERTEX<Airport> source, VERTEX<Airport> terminal) FOR GRAPH MyGraph { 
/* Write query logic here */
TYPEDEF TUPLE<FLOAT dist, VERTEX pred> pathTuple;
TYPEDEF TUPLE<VERTEX id, FLOAT lat, FLOAT lon, STRING name> pathVertex;
HeapAccum<pathTuple>(1, dist ASC) @minPath;
ListAccum<pathVertex> @path; # shortest path FROM source
SetAccum<EDGE> @@edgeSet; # list of all edges, if display is needed
OrAccum @visited;
STRING sourceName;
INT iter;
BOOL negativeCycle;
total = {source}; # the connected vertices
start = {source};
##### Get the connected vertices
start = SELECT s
FROM start:s
ACCUM s.@minPath += pathTuple(0, s),
s.@visited = TRUE,
#s.@path += s;
s.@path += pathVertex(s, s.latitude, s.longitude, s.name);
WHILE start.size() > 0 DO
start = SELECT t
FROM start:s -(flight_route:e)-> :t
WHERE NOT t.@visited
ACCUM t.@visited = TRUE;
total = total UNION start;
END;

##### Do V-1 iterations: Consider whether each edge lowers the best-known distance.
iter = total.size() - 1; # the max iteration is V-1
WHILE TRUE LIMIT iter DO
tmp = SELECT s
FROM total:s -(flight_route:e)-> :t
ACCUM
IF s.@minPath.size()>0 AND s.@minPath.top().dist < GSQL_INT_MAX THEN
t.@minPath += pathTuple(s.@minPath.top().dist + e.miles, s)
END;
END;

##### Calculate the paths #####

start = {source};
tmp = SELECT s
FROM total:s
WHERE s != source
ACCUM s.@visited = FALSE;
WHILE start.size() > 0 LIMIT iter DO # Limit the number of hops
start = SELECT t
FROM start:s -(flight_route:e)-> :t
WHERE NOT t.@visited
ACCUM IF s == t.@minPath.top().pred THEN
t.@visited = TRUE,
t.@path += s.@path,
#t.@path += t
t.@path += pathVertex(t, t.latitude, t.longitude, t.name)
END;
END;

total = SELECT s FROM total:s WHERE s == terminal;
PRINT total[total.@minPath.top().dist, total.@path];
}

Render a react page in Streamlit

Now, let’s see how to render react app in Streamlit using Streamlit Component.

Variable country is DataFrame type. Since Streamlit component only accept a String parameter that can be sent to JavaScript, I parse country into String type.components.declare_component accept 2 parameters —this component name and the url which is the React app address. Then I passed country to React app within component_value = _map_component(name="weighted", key=country).

Let’s switch to React and see how React accept the data from Streamlit.

First, we should build the component’s frontend out of HTML and TypeScript:

Run React app in terminal:

cd [your-react-app-name-directory]
npm start

Then, run Streamlit in another terminal:

Streamlit run weighted.py

You would see all countries’ names are displayed in web page. This countryData will be applied to the function — search airport by country.

Creating React Back-end

When we fetch data from tgcloud, we need to set a bearer token in http headers. However, the headers with a bearer token would not allow to use in React which is a front-end, as it violates CORS principle. Thus, we need to build a back-end for react to fetch data from tgcloud.

I used tigergraph.js which is a TigerGraph JavaScript connector created by
Shreya Chaudhary. First, we need to define your tgcloud configuration, which will be stored in config.js file.

We now can use cred to connect to tgcloud. Here is my implementation:

In index.js, I fetch two query result, one is all airports of a country, the another is the shortest path between two airports. The server will run on port 3030.

Implement an Interactive Map in React

We finally start to create the map in React.

Step 1: Initiate google map

To display a google map, we should build a new react component MapComponent. We should initiate a map when MapComponent is being rendered.

Step 2: Add a dropdown to select a country

I used a react dropdown library called react-dropdown. To use that, run the command in terminal:

npm install react-dropdown

and insert import Dropdown from 'react-dropdown' in MapComponent header. Then you are able to use this beautiful element in your react project.

In order to setting the countryData that we get from Streamlit to match the react-Dropdown input, we need to modify the countryData format. Here is how I implement it in MainComponent(the parent component of MapCOmponent):

Next, we can add dropdown to display country option.

Step 3: Add airports markers when country change

Every time when the dropdown value changes, the country changes, airports markers also changes. We can use onChange event to track whether the country has been changed.

When country value changed, we need to fetch the selected country’s airports from react-backend, and add markers on map;

Step 4: Add markers’ click event and location Info Card

Adding marker to each airport merely display the airport location of a country, but we do not know the airport detail information. So we should add click event to marker to display the airport information.

The location info card is inserted in google map, the card has two button to select starter airport or terminal airport. Here is what it looks like:

Step 5: Draw shortest path on map

The last step is show the shortest path fetched from tgcloud.

Here is what the application looks like:

To check the complete code, please check git gist.

What’s next

Although the interactive map is displayed on Streamlit, it’s a little bit inconvenient. Since we need to write and run 3 servers at the same time, which makes the work complicated. In the next, I will try to figure out building an interactive map with other tools like plotly dash.

I hope you like this blog and learned something new. If you have any other ideas or thinkings, please let me know and connect with me.

Resources

https://docs.streamlit.io/en/stable/develop_streamlit_components.html

https://developers.google.com/maps/documentation/javascript/overview

https://github.com/streamlit/component-template/

https://create-react-app.dev/docs/adding-typescript/

https://www.npmjs.com/package/react-dropdown

--

--