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

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
npx create-react-app your-app-name --template typescript
#or
yarn create react-app your-app-name --template typescript
npm install streamlit-component-lib
npm init
npm install --save body-parser
npm install --save express
npm install --tigergraph.js

Get Started with Google Maps API

Step 1: Create a Google Cloud Platform account

Google get started page
Create a new project in Google Cloud Platform
Google Cloud Platform — API page
<script
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>
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.

cd [your-react-app-name-directory]
npm start
Streamlit run weighted.py

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.

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

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.

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.

Step 5: Draw shortest path on map

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

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.

Resources

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

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store