A python streamlit app to visualize the "public-transportation distance" between different locations in switzerland.
Specifying a destination (a public transportation stop), a date, and a time a choropleth map of the country is generated where each municipality is colored according to the latest departure time necessary to arrive in time at the specified destination. Different stops in the same municipality are aggregated by the minimum function. Granularity of aggregation depends on the "features" in the geojson data. E.g. given a geojson file containing a voronoi diagram of all public transportation stops in the country, there would be no aggregation at all.
The image above shows a screenshot from the app: In this example, we want to get to Bern, Wankdorf by 9:15 AM while limiting the departure time to be after 7:00 AM. Hovering over Zofingen shows that from Zofingen we need to leave by 8:32 to arrive on time. Municipalities from which Bern, Wankdorf cannot be reached in time when leaving after 7:00 AM are shown in black. Clicking on the polygon of the choropleth representing Zofingen, a list of stations is displayed on the right hand side of the map. This list contains all stops that lie within the polygon of Zofingen, together with their departure time. Selecting a stop in this list, another table appears below the first table, showing the itinerary to get from this stop to Bern, Wankdorf.
Apart from the basic queries, the app allows computing accumulations of commutes. Consider the four queries and their result in the table below:
Zürich Altstetten, 2024-01-15, 09:15:00, 07:00:00, 1 |
Zofingen, 2024-01-20, 20:00:00, 18:00:00, 1 |
Bern Wankdorf, 2024-01-15, 09:15:00, 07:00:00, 1 |
Emmenbrücke, 2024-01-15, 09:00:00, 07:00:00, 4 |
Each element in the table specifies the query as "Location, Date, Time, Earliest Departure, Multiplier". Summing up the commutes of all four queries, each multiplied by its multiplier results in the following accumulated map. Note that locations which cannot satisfy all four queries are black.
In this map, each location is coloured according to the total commute time resulting from the four queries above.
The implementation is generic - providing the following data should suffice to "port" the app to other countries
- GTFS Data about the public transportation of the country
- Geojson file with a partition of the country specifying the granularity of the choropleth map
- A relational database is populated with the GTFS data using the init_db.sql script
- A simple Streamlit app allows the user to specify a destination station, date and time and displays the resulting map using the Folium library.
- A BFS algorithm traverses the "connection graph" of the public transportation network
The data source depends on the country. For Switzerland we can get the relevant data from
- GTFS: Open Transport Data
- Geojson: Datahub
The code currently requires that each feature in the geojson file specifies an "id" in properties
Setup a database and a database user if necessary. You can use Postgres for instance, but any relational database will do. For a postgres on Linux setup you can follow the Arch Linux documentation.
After initial setup is done, create an empty database (see also the documentation)
CREATE DATABASE sbb_map WITH OWNER <user>;
Initialize the database using the GTFS data and the initialization script
# Inside the directory with the GTFS data
psql -d sbb_map -U <user> -W -f ./<path-to-src>/init_db.sql -1
In order for python to be able to access the database we need to ensure that the correct environment variables are set. This is best achieved by using direnv.
# Inside the root directory of the project and after installing direnv
direnv allow .
echo "export DBUSER=<user>" > .envrc
echo "export DBPASS=<password>" >> .envrc
echo "export DBPORT=5432" >> .envrc
echo "export DBHOST=localhost" >> .envrc
echo "export DBNAME=sbb_map" >> .envrc
python -m venv .env
source .env/bin/activate
pip install -r requirements.txt
# Inside the root directory of the project with virtual environment enabled
python -m streamlit run main.py
- Allow for geojsons without properties.id
- Visualization:
- Add code to generate a Voronoi-diagram based geojson
- "deformed" maps
- Streamline the "building" of the application (database construction, installation of dependencies etc.)