├── .gitignore ├── LICENSE ├── Procfile ├── README.md ├── app.py ├── apps ├── backends.py ├── basemaps.py ├── customize.py ├── demo.py ├── home.py ├── slider.py ├── tile_layer.py ├── uber_nyc.py ├── url.py └── vector.py ├── data └── create_data.py ├── multiapp.py ├── requirements.txt └── setup.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *.html 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | pip-wheel-metadata/ 25 | share/python-wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .nox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | *.py,cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | .python-version 87 | 88 | # pipenv 89 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 90 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 91 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 92 | # install all needed dependencies. 93 | #Pipfile.lock 94 | 95 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 96 | __pypackages__/ 97 | 98 | # Celery stuff 99 | celerybeat-schedule 100 | celerybeat.pid 101 | 102 | # SageMath parsed files 103 | *.sage.py 104 | 105 | # Environments 106 | .env 107 | .venv 108 | env/ 109 | venv/ 110 | ENV/ 111 | env.bak/ 112 | venv.bak/ 113 | 114 | # Spyder project settings 115 | .spyderproject 116 | .spyproject 117 | 118 | # Rope project settings 119 | .ropeproject 120 | 121 | # mkdocs documentation 122 | /site 123 | 124 | # mypy 125 | .mypy_cache/ 126 | .dmypy.json 127 | dmypy.json 128 | 129 | # Pyre type checker 130 | .pyre/ 131 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Qiusheng Wu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: sh setup.sh && streamlit run app.py -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # leafmap-streamlit-apps 2 | Interactive web apps created using leafmap and streamlit 3 | 4 | 5 | ## Credits 6 | 7 | This app is created using the template from [upraneelnihar/streamlit-multiapps](https://github.com/upraneelnihar/streamlit-multiapps). -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from multiapp import MultiApp 3 | from apps import ( 4 | home, 5 | backends, 6 | basemaps, 7 | customize, 8 | demo, 9 | tile_layer, 10 | timelapse, 11 | uber_nyc, 12 | vector, 13 | ) 14 | 15 | # st.set_page_config(layout="wide") 16 | 17 | 18 | apps = MultiApp() 19 | 20 | # Add all your application here 21 | apps.add_app("Timelapse", timelapse.app) 22 | apps.add_app("Home", home.app) 23 | apps.add_app("Create an interactive map", backends.app) 24 | apps.add_app("Customize the default map", customize.app) 25 | apps.add_app("Change basemaps", basemaps.app) 26 | apps.add_app("Add tile layers", tile_layer.app) 27 | apps.add_app("Add vector", vector.app) 28 | apps.add_app("Demo", demo.app) 29 | apps.add_app("Pydeck", uber_nyc.app) 30 | 31 | # The main app 32 | apps.run() 33 | -------------------------------------------------------------------------------- /apps/backends.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | 4 | def app(): 5 | 6 | st.title("Create an interactive map") 7 | 8 | backend = st.selectbox( 9 | "Select a plotting backend", 10 | ["ipyleaflet", "folium", "heremap", "keperl.gl"], 11 | index=1, 12 | ) 13 | 14 | if backend == "ipyleaflet": 15 | with st.echo(): 16 | import leafmap.leafmap as leafmap 17 | elif backend == "folium": 18 | with st.echo(): 19 | import leafmap.foliumap as leafmap 20 | elif backend == "heremap": 21 | with st.echo(): 22 | import leafmap.heremap as leafmap 23 | elif backend == "keperl.gl": 24 | with st.echo(): 25 | import leafmap.kepler as leafmap 26 | 27 | with st.echo(): 28 | m = leafmap.Map() 29 | m.to_streamlit() 30 | -------------------------------------------------------------------------------- /apps/basemaps.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import leafmap.foliumap as leafmap 3 | 4 | 5 | def app(): 6 | st.title("Change basemaps") 7 | 8 | keys = list(leafmap.folium_basemaps.keys())[1:] 9 | 10 | basemap = st.selectbox("Select a basemap", keys) 11 | 12 | code = f"""import leafmap 13 | m = leafmap.Map() 14 | m.add_basemap('{basemap}') 15 | m""" 16 | st.code(code) 17 | 18 | m = leafmap.Map() 19 | m.add_basemap(basemap) 20 | m.to_streamlit() 21 | -------------------------------------------------------------------------------- /apps/customize.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import leafmap.foliumap as leafmap 3 | 4 | 5 | def app(): 6 | 7 | st.title("Customize the default map") 8 | 9 | def _update_slider(width_value, height_value, zoom_value, lat_value, lon_value): 10 | st.session_state["width_slider"] = width_value 11 | st.session_state["height_slider"] = height_value 12 | st.session_state["zoom_slider"] = zoom_value 13 | st.session_state["lat_slider"] = lat_value 14 | st.session_state["lon_slider"] = lon_value 15 | 16 | if "width_slider" not in st.session_state: 17 | st.session_state["width_slider"] = 700 18 | if "height_slider" not in st.session_state: 19 | st.session_state["height_slider"] = 500 20 | if "zoom_slider" not in st.session_state: 21 | st.session_state["zoom_slider"] = 4 22 | if "lat_slider" not in st.session_state: 23 | st.session_state["lat_slider"] = 40 24 | if "lon_slider" not in st.session_state: 25 | st.session_state["lon_slider"] = -100 26 | 27 | col1, col2, col3 = st.columns(3) 28 | col4, col5 = st.columns(2) 29 | 30 | width = col1.slider( 31 | "Map width", key="width_slider", min_value=100, max_value=1000, step=50 32 | ) 33 | 34 | height = col2.slider( 35 | "Map height", key="height_slider", min_value=100, max_value=1000, step=50 36 | ) 37 | 38 | zoom = col3.slider( 39 | "Zoom level", key="zoom_slider", min_value=1, max_value=18, step=1 40 | ) 41 | 42 | lat = col4.slider( 43 | "Center latitude", key="lat_slider", min_value=-90.0, max_value=90.0, step=0.1 44 | ) 45 | 46 | lon = col5.slider( 47 | "Center longitude", 48 | key="lon_slider", 49 | min_value=-180.0, 50 | max_value=180.0, 51 | step=0.1, 52 | ) 53 | 54 | st.button( 55 | "Reset", 56 | on_click=_update_slider, 57 | kwargs={ 58 | "width_value": 700, 59 | "height_value": 500, 60 | "zoom_value": 4, 61 | "lat_value": 40, 62 | "lon_value": -100, 63 | }, 64 | ) 65 | 66 | code = f""" 67 | import leafmap 68 | m = leafmap.Map(center=({lat}, {lon}), zoom={zoom}, width={width}, height={height}) 69 | m 70 | """ 71 | 72 | st.code(code, language="python") 73 | 74 | m = leafmap.Map(center=[lat, lon], zoom=zoom) 75 | m.to_streamlit(width=width, height=height) 76 | -------------------------------------------------------------------------------- /apps/demo.py: -------------------------------------------------------------------------------- 1 | """Run 'streamlit run app.py' in the terminal to start the app. 2 | """ 3 | import streamlit as st 4 | 5 | # st.set_page_config(layout="wide") 6 | 7 | def app(): 8 | 9 | "# leafmap streamlit demo" 10 | st.markdown('Source code: ') 11 | 12 | "## Create a 3D map using Kepler.gl" 13 | with st.echo(): 14 | import leafmap.kepler as leafmap 15 | 16 | m = leafmap.Map(center=[37.7621, -122.4143], zoom=12) 17 | in_csv = 'https://raw.githubusercontent.com/giswqs/leafmap/master/examples/data/hex_data.csv' 18 | config = 'https://raw.githubusercontent.com/giswqs/leafmap/master/examples/data/hex_config.json' 19 | m.add_csv(in_csv, layer_name="hex_data", config=config) 20 | m.to_streamlit() 21 | 22 | 23 | "## Create a heat map" 24 | with st.echo(): 25 | import leafmap.foliumap as leafmap 26 | 27 | filepath = "https://raw.githubusercontent.com/giswqs/leafmap/master/examples/data/us_cities.csv" 28 | m = leafmap.Map(tiles='stamentoner') 29 | m.add_heatmap(filepath, latitude="latitude", longitude='longitude', value="pop_max", name="Heat map", radius=20) 30 | m.to_streamlit(width=700, height=500, add_layer_control=True) 31 | 32 | 33 | "## Load a GeoJSON file" 34 | with st.echo(): 35 | 36 | m = leafmap.Map(center=[0, 0], zoom=2) 37 | in_geojson = 'https://raw.githubusercontent.com/giswqs/leafmap/master/examples/data/cable-geo.geojson' 38 | m.add_geojson(in_geojson, layer_name="Cable lines") 39 | m.to_streamlit() 40 | 41 | 42 | "## Add a colorbar" 43 | with st.echo(): 44 | 45 | m = leafmap.Map() 46 | m.add_basemap('USGS 3DEP Elevation') 47 | colors = ['006633', 'E5FFCC', '662A00', 'D8D8D8', 'F5F5F5'] 48 | vmin = 0 49 | vmax = 4000 50 | m.add_colorbar(colors=colors, vmin=vmin, vmax=vmax) 51 | m.to_streamlit() 52 | 53 | 54 | "## Change basemaps" 55 | with st.echo(): 56 | m = leafmap.Map() 57 | m.add_basemap("Esri.NatGeoWorldMap") 58 | m.to_streamlit() 59 | 60 | 61 | -------------------------------------------------------------------------------- /apps/home.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | 4 | def app(): 5 | st.title("Home") 6 | 7 | st.header("Introduction") 8 | st.write( 9 | "This web app demonstrates how to create interactive maps using leafmap and streamlit." 10 | ) 11 | 12 | st.markdown("Source code: ") 13 | st.markdown("Web app: ") 14 | 15 | st.header("Example") 16 | 17 | with st.echo(): 18 | import leafmap.foliumap as leafmap 19 | 20 | filepath = "https://raw.githubusercontent.com/giswqs/leafmap/master/examples/data/us_cities.csv" 21 | m = leafmap.Map(tiles="stamentoner") 22 | m.add_heatmap( 23 | filepath, 24 | latitude="latitude", 25 | longitude="longitude", 26 | value="pop_max", 27 | name="Heat map", 28 | radius=20, 29 | ) 30 | m.to_streamlit(width=700, height=500) 31 | -------------------------------------------------------------------------------- /apps/slider.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import random 3 | 4 | 5 | def app(): 6 | 7 | def _update_slider(value): 8 | st.session_state["test_slider"] = value 9 | 10 | if "test_slider" not in st.session_state: 11 | st.session_state["test_slider"] = 0 12 | 13 | slider = st.slider("My slider", key="test_slider", 14 | min_value=-100, max_value=100) 15 | 16 | st.write("Current value:", slider) 17 | 18 | st.button("Update slider values", on_click=_update_slider, 19 | kwargs={"value": random.randint(-100, 100)}) 20 | -------------------------------------------------------------------------------- /apps/tile_layer.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | 4 | def app(): 5 | 6 | st.title("Add tile layers") 7 | 8 | st.header("Add XYZ tile layer") 9 | 10 | with st.echo(): 11 | import leafmap.foliumap as leafmap 12 | 13 | m = leafmap.Map() 14 | m.add_tile_layer( 15 | url="https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}", 16 | name="Google Satellite", 17 | attribution="Google", 18 | ) 19 | m.to_streamlit() 20 | 21 | st.header("Add WMS tile layer") 22 | with st.echo(): 23 | import leafmap.foliumap as leafmap 24 | 25 | m = leafmap.Map() 26 | naip_url = "https://services.nationalmap.gov/arcgis/services/USGSNAIPImagery/ImageServer/WMSServer?" 27 | m.add_wms_layer( 28 | url=naip_url, 29 | layers="0", 30 | name="NAIP Imagery", 31 | format="image/png", 32 | shown=True, 33 | ) 34 | m.to_streamlit() 35 | 36 | st.header("Add xyzservices provider") 37 | with st.echo(): 38 | import os 39 | import leafmap.foliumap as leafmap 40 | import xyzservices.providers as xyz 41 | 42 | basemap = xyz.HEREv3.basicMap 43 | basemap["apiKey"] = os.environ["HEREMAPS_API_KEY"] 44 | m = leafmap.Map() 45 | m.add_basemap(basemap) 46 | m.to_streamlit() 47 | -------------------------------------------------------------------------------- /apps/uber_nyc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018-2019 Streamlit Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """An example of showing geographic data.""" 17 | 18 | import streamlit as st 19 | import pandas as pd 20 | import numpy as np 21 | import altair as alt 22 | import pydeck as pdk 23 | 24 | 25 | def app(): 26 | # SETTING PAGE CONFIG TO WIDE MODE 27 | 28 | # LOADING DATA 29 | DATE_TIME = "date/time" 30 | DATA_URL = "http://s3-us-west-2.amazonaws.com/streamlit-demo-data/uber-raw-data-sep14.csv.gz" 31 | 32 | @st.cache(persist=True) 33 | def load_data(nrows): 34 | data = pd.read_csv(DATA_URL, nrows=nrows) 35 | lowercase = lambda x: str(x).lower() 36 | data.rename(lowercase, axis="columns", inplace=True) 37 | data[DATE_TIME] = pd.to_datetime(data[DATE_TIME]) 38 | return data 39 | 40 | data = load_data(100000) 41 | 42 | # CREATING FUNCTION FOR MAPS 43 | 44 | def map(data, lat, lon, zoom): 45 | st.write( 46 | pdk.Deck( 47 | map_style="mapbox://styles/mapbox/light-v9", 48 | initial_view_state={ 49 | "latitude": lat, 50 | "longitude": lon, 51 | "zoom": zoom, 52 | "pitch": 50, 53 | }, 54 | layers=[ 55 | pdk.Layer( 56 | "HexagonLayer", 57 | data=data, 58 | get_position=["lon", "lat"], 59 | radius=100, 60 | elevation_scale=4, 61 | elevation_range=[0, 1000], 62 | pickable=True, 63 | extruded=True, 64 | ), 65 | ], 66 | ) 67 | ) 68 | 69 | # LAYING OUT THE TOP SECTION OF THE APP 70 | row1_1, row1_2 = st.columns((2, 3)) 71 | 72 | with row1_1: 73 | st.title("NYC Uber Ridesharing Data") 74 | hour_selected = st.slider("Select hour of pickup", 0, 23) 75 | 76 | with row1_2: 77 | st.write( 78 | """ 79 | ## 80 | Examining how Uber pickups vary over time in New York City's and at its major regional airports. 81 | By sliding the slider on the left you can view different slices of time and explore different transportation trends. 82 | """ 83 | ) 84 | 85 | # FILTERING DATA BY HOUR SELECTED 86 | data = data[data[DATE_TIME].dt.hour == hour_selected] 87 | 88 | # LAYING OUT THE MIDDLE SECTION OF THE APP WITH THE MAPS 89 | row2_1, row2_2, row2_3, row2_4 = st.columns((2, 1, 1, 1)) 90 | 91 | # SETTING THE ZOOM LOCATIONS FOR THE AIRPORTS 92 | la_guardia = [40.7900, -73.8700] 93 | jfk = [40.6650, -73.7821] 94 | newark = [40.7090, -74.1805] 95 | zoom_level = 12 96 | midpoint = (np.average(data["lat"]), np.average(data["lon"])) 97 | 98 | with row2_1: 99 | st.write( 100 | "**All New York City from %i:00 and %i:00**" 101 | % (hour_selected, (hour_selected + 1) % 24) 102 | ) 103 | map(data, midpoint[0], midpoint[1], 11) 104 | 105 | with row2_2: 106 | st.write("**La Guardia Airport**") 107 | map(data, la_guardia[0], la_guardia[1], zoom_level) 108 | 109 | with row2_3: 110 | st.write("**JFK Airport**") 111 | map(data, jfk[0], jfk[1], zoom_level) 112 | 113 | with row2_4: 114 | st.write("**Newark Airport**") 115 | map(data, newark[0], newark[1], zoom_level) 116 | 117 | # FILTERING DATA FOR THE HISTOGRAM 118 | filtered = data[ 119 | (data[DATE_TIME].dt.hour >= hour_selected) 120 | & (data[DATE_TIME].dt.hour < (hour_selected + 1)) 121 | ] 122 | 123 | hist = np.histogram(filtered[DATE_TIME].dt.minute, bins=60, range=(0, 60))[0] 124 | 125 | chart_data = pd.DataFrame({"minute": range(60), "pickups": hist}) 126 | 127 | # LAYING OUT THE HISTOGRAM SECTION 128 | 129 | st.write("") 130 | 131 | st.write( 132 | "**Breakdown of rides per minute between %i:00 and %i:00**" 133 | % (hour_selected, (hour_selected + 1) % 24) 134 | ) 135 | 136 | st.altair_chart( 137 | alt.Chart(chart_data) 138 | .mark_area( 139 | interpolate="step-after", 140 | ) 141 | .encode( 142 | x=alt.X("minute:Q", scale=alt.Scale(nice=False)), 143 | y=alt.Y("pickups:Q"), 144 | tooltip=["minute", "pickups"], 145 | ) 146 | .configure_mark(opacity=0.5, color="red"), 147 | use_container_width=True, 148 | ) 149 | -------------------------------------------------------------------------------- /apps/url.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | st.title("Experimental: Play with Query Strings!") 4 | 5 | app_state = st.experimental_get_query_params() 6 | app_state = {k: v[0] if isinstance(v, list) else v for k, v in app_state.items()} # fetch the first item in each query string as we don't have multiple values for each query string key in this example 7 | 8 | # [1] Checkbox 9 | default_checkbox = eval(app_state["checkbox"]) if "checkbox" in app_state else False 10 | checkbox1 = st.checkbox("Are you really happy today?", key="checkbox1", value = default_checkbox) 11 | app_state["checkbox"] = checkbox1 12 | st.experimental_set_query_params(**app_state) 13 | st.markdown("") 14 | 15 | 16 | # [2] Radio 17 | radio_list = ['Eat', 'Sleep', 'Both'] 18 | default_radio = int(app_state["radio"]) if "radio" in app_state else 0 19 | genre = st.radio("What are you doing at home during quarantine?", radio_list, index = default_radio) 20 | if genre: 21 | app_state["radio"] = radio_list.index(genre) 22 | st.experimental_set_query_params(**app_state) 23 | st.markdown("") 24 | 25 | 26 | # [3] Text Input 27 | default_title = app_state["movie"] if "movie" in app_state else "" 28 | title = st.text_input('Movie title', value = default_title) 29 | app_state["movie"] = title 30 | st.experimental_set_query_params(**app_state) 31 | 32 | -------------------------------------------------------------------------------- /apps/vector.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import leafmap 3 | 4 | 5 | def app(): 6 | st.title("Add vector datasets") 7 | 8 | url = "https://raw.githubusercontent.com/giswqs/data/main/world/world_cities.csv" 9 | in_csv = st.text_input("Enter a URL to a vector file", url) 10 | 11 | m = leafmap.Map() 12 | 13 | if in_csv: 14 | m.add_xy_data(in_csv, x="longitude", y="latitude", layer_name="World Cities") 15 | 16 | m.to_streamlit() 17 | -------------------------------------------------------------------------------- /data/create_data.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | 4 | def create_table(n=7): 5 | df = pd.DataFrame({"x": range(1, 11), "y": n}) 6 | df['x*y'] = df.x * df.y 7 | return df -------------------------------------------------------------------------------- /multiapp.py: -------------------------------------------------------------------------------- 1 | """Frameworks for running multiple Streamlit applications as a single app. 2 | """ 3 | import streamlit as st 4 | 5 | # app_state = st.experimental_get_query_params() 6 | # app_state = {k: v[0] if isinstance(v, list) else v for k, v in app_state.items()} # fetch the first item in each query string as we don't have multiple values for each query string key in this example 7 | 8 | 9 | class MultiApp: 10 | """Framework for combining multiple streamlit applications. 11 | Usage: 12 | def foo(): 13 | st.title("Hello Foo") 14 | def bar(): 15 | st.title("Hello Bar") 16 | app = MultiApp() 17 | app.add_app("Foo", foo) 18 | app.add_app("Bar", bar) 19 | app.run() 20 | It is also possible keep each application in a separate file. 21 | import foo 22 | import bar 23 | app = MultiApp() 24 | app.add_app("Foo", foo.app) 25 | app.add_app("Bar", bar.app) 26 | app.run() 27 | """ 28 | 29 | def __init__(self): 30 | self.apps = [] 31 | 32 | def add_app(self, title, func): 33 | """Adds a new application. 34 | Parameters 35 | ---------- 36 | func: 37 | the python function to render this app. 38 | title: 39 | title of the app. Appears in the dropdown in the sidebar. 40 | """ 41 | self.apps.append({"title": title, "function": func}) 42 | 43 | def run(self): 44 | app_state = st.experimental_get_query_params() 45 | app_state = { 46 | k: v[0] if isinstance(v, list) else v for k, v in app_state.items() 47 | } # fetch the first item in each query string as we don't have multiple values for each query string key in this example 48 | 49 | # st.write('before', app_state) 50 | 51 | titles = [a["title"] for a in self.apps] 52 | functions = [a["function"] for a in self.apps] 53 | default_radio = titles.index(app_state["page"]) if "page" in app_state else 0 54 | 55 | st.sidebar.title("Navigation") 56 | 57 | title = st.sidebar.radio("Go To", titles, index=default_radio, key="radio") 58 | 59 | app_state["page"] = st.session_state.radio 60 | # st.write('after', app_state) 61 | 62 | st.experimental_set_query_params(**app_state) 63 | # st.experimental_set_query_params(**st.session_state.to_dict()) 64 | functions[titles.index(title)]() 65 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | geopandas 2 | keplergl 3 | streamlit 4 | git+https://github.com/giswqs/leafmap 5 | git+https://github.com/giswqs/geemap 6 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | mkdir -p ~/.streamlit/ 2 | echo "\ 3 | [server]\n\ 4 | headless = true\n\ 5 | port = $PORT\n\ 6 | enableCORS = false\n\ 7 | \n\ 8 | " > ~/.streamlit/config.toml --------------------------------------------------------------------------------