4 | Streamlit Mapbox
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/st_mapbox/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "streamlit_mapbox",
3 | "version": "0.0.1",
4 | "private": true,
5 | "dependencies": {
6 | "@types/jest": "^24.0.0",
7 | "@types/node": "^12.0.0",
8 | "@types/react": "^16.9.0",
9 | "@types/react-dom": "^16.9.0",
10 | "@mapbox/mapbox-gl-draw": "1.1.2",
11 | "mapbox-gl": "1.1.1",
12 | "react": "^17.0.2",
13 | "react-dom": "^17.0.2",
14 | "react-scripts": "^4.0.3",
15 | "react-mapbox-gl": "4.5.1",
16 | "react-mapbox-gl-draw": "2.0.2",
17 | "streamlit-component-lib": "~1.3.0",
18 | "typescript": "~3.8.0"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": "react-app"
28 | },
29 | "browserslist": {
30 | "production": [">0.2%", "not dead", "not op_mini all"],
31 | "development": [
32 | "last 1 chrome version",
33 | "last 1 firefox version",
34 | "last 1 safari version"
35 | ]
36 | },
37 | "homepage": "."
38 | }
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 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 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 | with open("README.md") as readme_file:
4 | readme = readme_file.read()
5 |
6 | setuptools.setup(
7 | name="streamlit-mapbox",
8 | version="0.0.1",
9 | author="Qiusheng Wu",
10 | author_email="giswqs@gmail.com",
11 | description="Streamlit component for rendering Mapbox GL JS",
12 | long_description=readme,
13 | long_description_content_type="text/markdown",
14 | url="https://github.com/giswqs/streamlit-mapbox",
15 | packages=setuptools.find_packages(),
16 | include_package_data=True,
17 | classifiers=[
18 | "Development Status :: 2 - Pre-Alpha",
19 | "Intended Audience :: Developers",
20 | "License :: OSI Approved :: MIT License",
21 | "Natural Language :: English",
22 | "Programming Language :: Python :: 3",
23 | "Programming Language :: Python :: 3.7",
24 | "Programming Language :: Python :: 3.8",
25 | "Programming Language :: Python :: 3.9",
26 | "Programming Language :: Python :: 3.10",
27 | ],
28 | python_requires=">=3.7",
29 | install_requires=[
30 | "streamlit >= 0.63",
31 | ],
32 | )
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 | st_mapbox/frontend/node_modules/
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 | st_mapbox/frontend/package-lock.json
132 |
--------------------------------------------------------------------------------
/st_mapbox/frontend/src/MapboxComponent.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Streamlit,
3 | StreamlitComponentBase,
4 | withStreamlitConnection,
5 | } from "streamlit-component-lib"
6 | import React, { ReactNode } from "react"
7 | // import mapboxgl from '!mapbox-gl'; // eslint-disable-line import/no-webpack-loader-syntax
8 | import ReactMapboxGl from "react-mapbox-gl";
9 | import DrawControl from "react-mapbox-gl-draw";
10 | import "@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css";
11 |
12 |
13 | interface State {
14 | numClicks: number
15 | isFocused: boolean
16 | }
17 |
18 |
19 | const Map = ReactMapboxGl({
20 | accessToken:
21 | "your-access-token"
22 | });
23 |
24 |
25 | /**
26 | * This is a React-based component template. The `render()` function is called
27 | * automatically when your component should be re-rendered.
28 | */
29 | class MapboxComponent extends StreamlitComponentBase {
30 | onDrawCreate = ({ features }: any) => {
31 | Streamlit.setComponentValue(features);
32 | };
33 |
34 | // onDrawCreate = ({ features }: any) => {
35 | // console.log(features);
36 | // };
37 |
38 | onDrawUpdate = ({ features }: any) => {
39 | console.log({ features });
40 | };
41 |
42 |
43 |
44 | public state = { numClicks: 0, isFocused: false }
45 |
46 | public render = (): ReactNode => {
47 | // Arguments that are passed to the plugin in Python are accessible
48 | // via `this.props.args`. Here, we access the "name" arg.
49 | const name = this.props.args["name"]
50 |
51 | // Streamlit sends us a theme object via props that we can use to ensure
52 | // that our component has visuals that match the active theme in a
53 | // streamlit app.
54 | const { theme } = this.props
55 | const style: React.CSSProperties = {}
56 |
57 | // Maintain compatibility with older versions of Streamlit that don't send
58 | // a theme object.
59 | if (theme) {
60 | // Use the theme object to style our button border. Alternatively, the
61 | // theme style is defined in CSS vars.
62 | const borderStyling = `1px solid ${
63 | this.state.isFocused ? theme.primaryColor : "gray"
64 | }`
65 | style.border = borderStyling
66 | style.outline = borderStyling
67 | }
68 |
69 |
70 | // Show a button and some text.
71 | // When the button is clicked, we'll increment our "numClicks" state
72 | // variable, and send its new value back to Streamlit, where it'll
73 | // be available to the Python program.
74 | return (
75 |
76 |
85 |
86 | );
87 | }
88 |
89 | // /** Click handler for our "Click Me!" button. */
90 | // private onClicked = (): void => {
91 | // // Increment state.numClicks, and pass the new value back to
92 | // // Streamlit via `Streamlit.setComponentValue`.
93 | // this.setState(
94 | // (prevState) => ({ numClicks: prevState.numClicks + 1 }),
95 | // () => Streamlit.setComponentValue(this.state.numClicks)
96 | // )
97 | // }
98 |
99 | // /** Focus handler for our "Click Me!" button. */
100 | // private _onFocus = (): void => {
101 | // this.setState({ isFocused: true })
102 | // }
103 |
104 | // /** Blur handler for our "Click Me!" button. */
105 | // private _onBlur = (): void => {
106 | // this.setState({ isFocused: false })
107 | // }
108 | }
109 |
110 | // "withStreamlitConnection" is a wrapper function. It bootstraps the
111 | // connection between your component and the Streamlit app, and handles
112 | // passing arguments from Python -> Component.
113 | //
114 | // You don't need to edit withStreamlitConnection (but you're welcome to!).
115 | export default withStreamlitConnection(MapboxComponent)
116 |
--------------------------------------------------------------------------------
/st_mapbox/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 | import streamlit.components.v1 as components
3 |
4 | # Create a _RELEASE constant. We'll set this to False while we're developing
5 | # the component, and True when we're ready to package and distribute it.
6 | # (This is, of course, optional - there are innumerable ways to manage your
7 | # release process.)
8 | _RELEASE = False
9 |
10 | # Declare a Streamlit component. `declare_component` returns a function
11 | # that is used to create instances of the component. We're naming this
12 | # function "_component_func", with an underscore prefix, because we don't want
13 | # to expose it directly to users. Instead, we will create a custom wrapper
14 | # function, below, that will serve as our component's public API.
15 |
16 | # It's worth noting that this call to `declare_component` is the
17 | # *only thing* you need to do to create the binding between Streamlit and
18 | # your component frontend. Everything else we do in this file is simply a
19 | # best practice.
20 |
21 | if not _RELEASE:
22 | _component_func = components.declare_component(
23 | # We give the component a simple, descriptive name ("my_component"
24 | # does not fit this bill, so please choose something better for your
25 | # own component :)
26 | "my_component",
27 | # Pass `url` here to tell Streamlit that the component will be served
28 | # by the local dev server that you run via `npm run start`.
29 | # (This is useful while your component is in development.)
30 | url="http://localhost:3001",
31 | )
32 | else:
33 | # When we're distributing a production version of the component, we'll
34 | # replace the `url` param with `path`, and point it to to the component's
35 | # build directory:
36 | parent_dir = os.path.dirname(os.path.abspath(__file__))
37 | build_dir = os.path.join(parent_dir, "frontend/build")
38 | _component_func = components.declare_component(
39 | "my_component", path=build_dir)
40 |
41 |
42 | # Create a wrapper function for the component. This is an optional
43 | # best practice - we could simply expose the component function returned by
44 | # `declare_component` and call it done. The wrapper allows us to customize
45 | # our component's API: we can pre-process its input args, post-process its
46 | # output value, and add a docstring for users.
47 | def st_mapbox(name, key=None):
48 | """Create a new instance of "my_component".
49 |
50 | Parameters
51 | ----------
52 | name: str
53 | The name of the thing we're saying hello to. The component will display
54 | the text "Hello, {name}!"
55 | key: str or None
56 | An optional key that uniquely identifies this component. If this is
57 | None, and the component's arguments are changed, the component will
58 | be re-mounted in the Streamlit frontend and lose its current state.
59 |
60 | Returns
61 | -------
62 | int
63 | The number of times the component's "Click Me" button has been clicked.
64 | (This is the value passed to `Streamlit.setComponentValue` on the
65 | frontend.)
66 |
67 | """
68 | # Call through to our private component function. Arguments we pass here
69 | # will be sent to the frontend, where they'll be available in an "args"
70 | # dictionary.
71 | #
72 | # "default" is a special argument that specifies the initial return
73 | # value of the component before the user has interacted with it.
74 | component_value = _component_func(name=name, key=key, default=0)
75 |
76 | # We could modify the value returned from the component if we wanted.
77 | # There's no need to do this in our simple example - but it's an option.
78 | return component_value
79 |
80 |
81 | # Add some test code to play with the component while it's in development.
82 | # During development, we can run this just as we would any other Streamlit
83 | # app: `$ streamlit run my_component/__init__.py`
84 | if not _RELEASE:
85 | import streamlit as st
86 |
87 | st.set_page_config(layout="wide")
88 |
89 | st.subheader("Streamlit Mapbox")
90 | st.write("Bidirectional communication between Streamlit and Mapbox GL JS")
91 |
92 | # Create an instance of our component with a constant `name` arg, and
93 | # print its output value.
94 | token = "your-access-token"
95 | location = st_mapbox(token)
96 | st.markdown("You've clicked: %s" % location)
97 |
98 | # st.markdown("---")
99 | # st.subheader("Component with variable args")
100 |
101 | # Create a second instance of our component whose `name` arg will vary
102 | # based on a text_input widget.
103 | #
104 | # We use the special "key" argument to assign a fixed identity to this
105 | # component instance. By default, when a component's arguments change,
106 | # it is considered a new instance and will be re-mounted on the frontend
107 | # and lose its current state. In this case, we want to vary the component's
108 | # "name" argument without having it get recreated.
109 | # name_input = st.text_input("Enter a name", value="Streamlit")
110 | # num_clicks = st_mapbox(name_input, key="foo")
111 | # st.markdown("You've clicked %s times!" % int(num_clicks))
112 |
--------------------------------------------------------------------------------