reflex-mapgl-maplibre 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. reflex_mapgl_maplibre-0.1.0/LICENSE +21 -0
  2. reflex_mapgl_maplibre-0.1.0/PKG-INFO +223 -0
  3. reflex_mapgl_maplibre-0.1.0/README.md +188 -0
  4. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/__init__.py +64 -0
  5. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/advanced.py +65 -0
  6. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/advanced.pyi +173 -0
  7. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/base.py +41 -0
  8. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/base.pyi +85 -0
  9. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/controls.py +82 -0
  10. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/controls.pyi +382 -0
  11. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/deckgl_overlay.jsx +11 -0
  12. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/draw_control.jsx +28 -0
  13. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/map.py +173 -0
  14. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/map.pyi +151 -0
  15. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/markers.py +62 -0
  16. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/markers.pyi +196 -0
  17. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/provider.py +21 -0
  18. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/provider.pyi +81 -0
  19. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/py.typed +0 -0
  20. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/sources.py +69 -0
  21. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre/sources.pyi +185 -0
  22. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre.egg-info/PKG-INFO +223 -0
  23. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre.egg-info/SOURCES.txt +34 -0
  24. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre.egg-info/dependency_links.txt +1 -0
  25. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre.egg-info/requires.txt +8 -0
  26. reflex_mapgl_maplibre-0.1.0/custom_components/reflex_mapgl_maplibre.egg-info/top_level.txt +1 -0
  27. reflex_mapgl_maplibre-0.1.0/pyproject.toml +66 -0
  28. reflex_mapgl_maplibre-0.1.0/setup.cfg +4 -0
  29. reflex_mapgl_maplibre-0.1.0/tests/test_advanced.py +38 -0
  30. reflex_mapgl_maplibre-0.1.0/tests/test_base.py +36 -0
  31. reflex_mapgl_maplibre-0.1.0/tests/test_component.py +81 -0
  32. reflex_mapgl_maplibre-0.1.0/tests/test_controls.py +55 -0
  33. reflex_mapgl_maplibre-0.1.0/tests/test_demo_pages.py +34 -0
  34. reflex_mapgl_maplibre-0.1.0/tests/test_map.py +138 -0
  35. reflex_mapgl_maplibre-0.1.0/tests/test_markers.py +44 -0
  36. reflex_mapgl_maplibre-0.1.0/tests/test_sources.py +40 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ernesto Crespo
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.
@@ -0,0 +1,223 @@
1
+ Metadata-Version: 2.4
2
+ Name: reflex-mapgl-maplibre
3
+ Version: 0.1.0
4
+ Summary: Reflex wrapper for react-map-gl (maplibre endpoint) — interactive MapLibre GL JS maps in pure Python.
5
+ Author-email: Ernesto Crespo <ecrespo@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ecrespo/reflex-mapgl-maplibre
8
+ Project-URL: Source, https://github.com/ecrespo/reflex-mapgl-maplibre
9
+ Project-URL: Bug Tracker, https://github.com/ecrespo/reflex-mapgl-maplibre/issues
10
+ Project-URL: Upstream (react-map-gl), https://github.com/visgl/react-map-gl
11
+ Keywords: reflex,reflex-custom-components,maplibre,react-map-gl,map,gis,geospatial,webgl
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: GIS
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: reflex>=0.9.0
28
+ Provides-Extra: dev
29
+ Requires-Dist: build; extra == "dev"
30
+ Requires-Dist: twine; extra == "dev"
31
+ Requires-Dist: pytest; extra == "dev"
32
+ Requires-Dist: pytest-cov; extra == "dev"
33
+ Requires-Dist: ruff; extra == "dev"
34
+ Dynamic: license-file
35
+
36
+ # reflex-mapgl-maplibre
37
+
38
+ Interactive **MapLibre GL JS** maps for [Reflex](https://reflex.dev), in pure
39
+ Python. A thin, idiomatic wrapper around
40
+ [`react-map-gl`](https://github.com/visgl/react-map-gl) via its
41
+ `react-map-gl/maplibre` endpoint — markers, popups, GeoJSON sources & layers,
42
+ clustering, heatmaps, controls, feature interaction and imperative camera
43
+ control, with **no JavaScript or React required**.
44
+
45
+ > Status: **beta (0.1.0).** Core map, markers/popups, sources/layers, controls,
46
+ > multi-map provider and optional deck.gl/draw overlays are implemented, with a
47
+ > 58-test contract suite (100% wrapper coverage) and a demo app mapped to the
48
+ > upstream examples gallery. See [`specs/`](specs/) for the full design.
49
+
50
+ ## Why
51
+
52
+ Reflex has no built-in map component. `react-map-gl/maplibre` is the standard
53
+ React wrapper for MapLibre, but it is unreachable from Python without a wrapper.
54
+ This package exposes it as Reflex components and ships a demo app that reproduces
55
+ the upstream examples.
56
+
57
+ ## Install
58
+
59
+ ```bash
60
+ uv add reflex-mapgl-maplibre # or: pip install reflex-mapgl-maplibre
61
+ ```
62
+
63
+ It pulls the npm packages `react-map-gl` and `maplibre-gl` into the compiled
64
+ frontend automatically, and injects `maplibre-gl/dist/maplibre-gl.css` for you.
65
+
66
+ ## Quickstart
67
+
68
+ ```python
69
+ import reflex as rx
70
+ import reflex_mapgl_maplibre as mapgl
71
+
72
+ STYLE = "https://demotiles.maplibre.org/style.json" # key-less basemap
73
+
74
+ def index() -> rx.Component:
75
+ return mapgl.map(
76
+ mapgl.navigation_control(position="top-right"),
77
+ mapgl.marker(longitude=-122.4, latitude=37.8, color="#ef4444"),
78
+ initial_view_state={"longitude": -122.4, "latitude": 37.8, "zoom": 11},
79
+ map_style=STYLE,
80
+ style={"width": "100%", "height": "100vh"},
81
+ )
82
+
83
+ app = rx.App()
84
+ app.add_page(index)
85
+ ```
86
+
87
+ ## Components
88
+
89
+ | Factory | Wraps | Purpose |
90
+ |---|---|---|
91
+ | `mapgl.map(...)` | `Map` (default export) | Root map canvas, camera, style, events |
92
+ | `mapgl.marker(...)` | `Marker` | DOM marker at a lng/lat |
93
+ | `mapgl.popup(...)` | `Popup` | Floating info bubble |
94
+ | `mapgl.source(...)` | `Source` | Data source (GeoJSON, tiles, clustering) |
95
+ | `mapgl.layer(...)` | `Layer` | Style layer (circle/line/fill/symbol/heatmap/…) |
96
+ | `mapgl.navigation_control(...)` | `NavigationControl` | Zoom + compass |
97
+ | `mapgl.geolocate_control(...)` | `GeolocateControl` | Find my location |
98
+ | `mapgl.fullscreen_control(...)` | `FullscreenControl` | Fullscreen toggle |
99
+ | `mapgl.scale_control(...)` | `ScaleControl` | Scale bar |
100
+ | `mapgl.attribution_control(...)` | `AttributionControl` | Attribution box |
101
+ | `mapgl.map_provider(...)` | `MapProvider` | Multi-map / synced maps |
102
+
103
+ Optional, heavier overlays live in `reflex_mapgl_maplibre.advanced`
104
+ (`deckgl_overlay`, `draw_control`) and pull extra npm dependencies on demand.
105
+
106
+ Full contract: [`specs/api/component-api-v1.md`](specs/api/component-api-v1.md).
107
+
108
+ ## Imperative camera control
109
+
110
+ `map(...)` returns an instance whose camera helpers produce event specs you can
111
+ attach to any handler. The map needs an `id`:
112
+
113
+ ```python
114
+ m = mapgl.map(id="main", initial_view_state={...}, map_style=STYLE)
115
+
116
+ rx.button("Paris", on_click=m.fly_to(2.35, 48.85, zoom=11))
117
+ rx.button("Fit area", on_click=m.fit_bounds([[-10, 36], [30, 60]], padding=40))
118
+ ```
119
+
120
+ `fly_to`, `ease_to`, `jump_to`, `fit_bounds` are forwarded to the MapLibre
121
+ `MapRef` (see Technical Design DD-003).
122
+
123
+ ## Examples (demo app)
124
+
125
+ The `mapgl_maplibre_demo/` app maps the upstream examples gallery to one page
126
+ each:
127
+
128
+ | Page | Example |
129
+ |---|---|
130
+ | `/` | Basic map |
131
+ | `/controls` | Controls |
132
+ | `/markers` | Markers & Popups |
133
+ | `/geojson` | GeoJSON sources & layers |
134
+ | `/clusters` | Clustering |
135
+ | `/heatmap` | Heatmap |
136
+ | `/interaction` | Click features → Reflex state |
137
+ | `/animation` | Viewport animation (`fly_to` / `fit_bounds`) |
138
+ | `/side-by-side` | Two maps under a `MapProvider` |
139
+
140
+ Run it:
141
+
142
+ ```bash
143
+ uv sync # create .venv with the wrapper + dev tools
144
+ cd mapgl_maplibre_demo
145
+ uv run reflex run # the local wrapper is installed in editable mode
146
+ ```
147
+
148
+ > Basemaps use key-less styles (MapLibre demotiles, CARTO). For MapTiler styles,
149
+ > pass `map_style="https://api.maptiler.com/maps/streets/style.json?key=YOUR_KEY"`.
150
+
151
+ ## Development & publishing (Reflex custom component, uv)
152
+
153
+ This repo follows the [Reflex custom-components](https://reflex.dev/docs/custom-components/overview/)
154
+ layout (`custom_components/`, a demo app, and a publishable `pyproject.toml`) and
155
+ uses **uv** as the package manager.
156
+
157
+ ```bash
158
+ # 1. Set up the dev environment (creates .venv, installs the wrapper editable
159
+ # plus the dev group: build, twine, pytest, pytest-cov, ruff).
160
+ uv sync
161
+
162
+ # 2. Quality gates.
163
+ uv run ruff check custom_components tests
164
+ uv run pytest # 58 tests, 100% wrapper coverage
165
+
166
+ # 3. Build the component (regenerates the .pyi type stubs and the dist/ artifacts:
167
+ # .whl + .tar.gz). `python -m reflex` keeps the repo root importable so the
168
+ # stub generator can resolve `custom_components.*`.
169
+ uv run python -m reflex component build
170
+
171
+ # 4. Validate the distribution metadata for PyPI.
172
+ uv run twine check dist/*
173
+
174
+ # 5. Publish (requires a PyPI account + API token; see prerequisites docs).
175
+ uv publish # or: uv run twine upload dist/*
176
+ # TestPyPI first: uv publish --publish-url https://test.pypi.org/legacy/
177
+ ```
178
+
179
+ > `reflex component build` produces the same `dist/` artifacts; `uv build` works
180
+ > too (both invoke the setuptools backend). The published wheel ships the `.pyi`
181
+ > stubs and a `py.typed` marker (PEP 561) for IDE autocomplete.
182
+
183
+ To list the component on the Reflex gallery after publishing: `reflex component share`.
184
+
185
+ ## Project layout
186
+
187
+ ```
188
+ reflex-mapgl-maplibre/
189
+ ├── custom_components/reflex_mapgl_maplibre/ # the wrapper package
190
+ │ ├── base.py # shared base: library, CSS, NoSSR
191
+ │ ├── map.py # Map root + imperative camera helpers
192
+ │ ├── markers.py # Marker, Popup
193
+ │ ├── sources.py # Source, Layer
194
+ │ ├── controls.py # Navigation/Geolocate/Fullscreen/Scale/Attribution
195
+ │ ├── provider.py # MapProvider
196
+ │ └── advanced.py # deck.gl overlay & draw control (optional deps)
197
+ ├── mapgl_maplibre_demo/ # demo Reflex app
198
+ ├── specs/ # SDD artifacts (see specs/README.md)
199
+ ├── tests/ # compile/contract tests
200
+ └── scripts/create_github_repo.sh
201
+ ```
202
+
203
+ ## Design docs (SDD)
204
+
205
+ This project is spec-driven. See [`specs/README.md`](specs/README.md):
206
+ PRD · Component API Spec · Technical Design · Phases · Implementation Plan ·
207
+ Tasks · Upstream Analysis (examples catalog).
208
+
209
+ ## Compatibility
210
+
211
+ - Python ≥ 3.10, Reflex ≥ 0.9
212
+ - Frontend: `react-map-gl@^8.1.1`, `maplibre-gl@^5`
213
+
214
+ ## License
215
+
216
+ [MIT](LICENSE). Upstream `react-map-gl` and `maplibre-gl` are MIT-licensed;
217
+ this wrapper is published with attribution.
218
+
219
+ ## Credits
220
+
221
+ - [react-map-gl](https://github.com/visgl/react-map-gl) by vis.gl
222
+ - [MapLibre GL JS](https://maplibre.org/)
223
+ - Wrapper by Ernesto Crespo
@@ -0,0 +1,188 @@
1
+ # reflex-mapgl-maplibre
2
+
3
+ Interactive **MapLibre GL JS** maps for [Reflex](https://reflex.dev), in pure
4
+ Python. A thin, idiomatic wrapper around
5
+ [`react-map-gl`](https://github.com/visgl/react-map-gl) via its
6
+ `react-map-gl/maplibre` endpoint — markers, popups, GeoJSON sources & layers,
7
+ clustering, heatmaps, controls, feature interaction and imperative camera
8
+ control, with **no JavaScript or React required**.
9
+
10
+ > Status: **beta (0.1.0).** Core map, markers/popups, sources/layers, controls,
11
+ > multi-map provider and optional deck.gl/draw overlays are implemented, with a
12
+ > 58-test contract suite (100% wrapper coverage) and a demo app mapped to the
13
+ > upstream examples gallery. See [`specs/`](specs/) for the full design.
14
+
15
+ ## Why
16
+
17
+ Reflex has no built-in map component. `react-map-gl/maplibre` is the standard
18
+ React wrapper for MapLibre, but it is unreachable from Python without a wrapper.
19
+ This package exposes it as Reflex components and ships a demo app that reproduces
20
+ the upstream examples.
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ uv add reflex-mapgl-maplibre # or: pip install reflex-mapgl-maplibre
26
+ ```
27
+
28
+ It pulls the npm packages `react-map-gl` and `maplibre-gl` into the compiled
29
+ frontend automatically, and injects `maplibre-gl/dist/maplibre-gl.css` for you.
30
+
31
+ ## Quickstart
32
+
33
+ ```python
34
+ import reflex as rx
35
+ import reflex_mapgl_maplibre as mapgl
36
+
37
+ STYLE = "https://demotiles.maplibre.org/style.json" # key-less basemap
38
+
39
+ def index() -> rx.Component:
40
+ return mapgl.map(
41
+ mapgl.navigation_control(position="top-right"),
42
+ mapgl.marker(longitude=-122.4, latitude=37.8, color="#ef4444"),
43
+ initial_view_state={"longitude": -122.4, "latitude": 37.8, "zoom": 11},
44
+ map_style=STYLE,
45
+ style={"width": "100%", "height": "100vh"},
46
+ )
47
+
48
+ app = rx.App()
49
+ app.add_page(index)
50
+ ```
51
+
52
+ ## Components
53
+
54
+ | Factory | Wraps | Purpose |
55
+ |---|---|---|
56
+ | `mapgl.map(...)` | `Map` (default export) | Root map canvas, camera, style, events |
57
+ | `mapgl.marker(...)` | `Marker` | DOM marker at a lng/lat |
58
+ | `mapgl.popup(...)` | `Popup` | Floating info bubble |
59
+ | `mapgl.source(...)` | `Source` | Data source (GeoJSON, tiles, clustering) |
60
+ | `mapgl.layer(...)` | `Layer` | Style layer (circle/line/fill/symbol/heatmap/…) |
61
+ | `mapgl.navigation_control(...)` | `NavigationControl` | Zoom + compass |
62
+ | `mapgl.geolocate_control(...)` | `GeolocateControl` | Find my location |
63
+ | `mapgl.fullscreen_control(...)` | `FullscreenControl` | Fullscreen toggle |
64
+ | `mapgl.scale_control(...)` | `ScaleControl` | Scale bar |
65
+ | `mapgl.attribution_control(...)` | `AttributionControl` | Attribution box |
66
+ | `mapgl.map_provider(...)` | `MapProvider` | Multi-map / synced maps |
67
+
68
+ Optional, heavier overlays live in `reflex_mapgl_maplibre.advanced`
69
+ (`deckgl_overlay`, `draw_control`) and pull extra npm dependencies on demand.
70
+
71
+ Full contract: [`specs/api/component-api-v1.md`](specs/api/component-api-v1.md).
72
+
73
+ ## Imperative camera control
74
+
75
+ `map(...)` returns an instance whose camera helpers produce event specs you can
76
+ attach to any handler. The map needs an `id`:
77
+
78
+ ```python
79
+ m = mapgl.map(id="main", initial_view_state={...}, map_style=STYLE)
80
+
81
+ rx.button("Paris", on_click=m.fly_to(2.35, 48.85, zoom=11))
82
+ rx.button("Fit area", on_click=m.fit_bounds([[-10, 36], [30, 60]], padding=40))
83
+ ```
84
+
85
+ `fly_to`, `ease_to`, `jump_to`, `fit_bounds` are forwarded to the MapLibre
86
+ `MapRef` (see Technical Design DD-003).
87
+
88
+ ## Examples (demo app)
89
+
90
+ The `mapgl_maplibre_demo/` app maps the upstream examples gallery to one page
91
+ each:
92
+
93
+ | Page | Example |
94
+ |---|---|
95
+ | `/` | Basic map |
96
+ | `/controls` | Controls |
97
+ | `/markers` | Markers & Popups |
98
+ | `/geojson` | GeoJSON sources & layers |
99
+ | `/clusters` | Clustering |
100
+ | `/heatmap` | Heatmap |
101
+ | `/interaction` | Click features → Reflex state |
102
+ | `/animation` | Viewport animation (`fly_to` / `fit_bounds`) |
103
+ | `/side-by-side` | Two maps under a `MapProvider` |
104
+
105
+ Run it:
106
+
107
+ ```bash
108
+ uv sync # create .venv with the wrapper + dev tools
109
+ cd mapgl_maplibre_demo
110
+ uv run reflex run # the local wrapper is installed in editable mode
111
+ ```
112
+
113
+ > Basemaps use key-less styles (MapLibre demotiles, CARTO). For MapTiler styles,
114
+ > pass `map_style="https://api.maptiler.com/maps/streets/style.json?key=YOUR_KEY"`.
115
+
116
+ ## Development & publishing (Reflex custom component, uv)
117
+
118
+ This repo follows the [Reflex custom-components](https://reflex.dev/docs/custom-components/overview/)
119
+ layout (`custom_components/`, a demo app, and a publishable `pyproject.toml`) and
120
+ uses **uv** as the package manager.
121
+
122
+ ```bash
123
+ # 1. Set up the dev environment (creates .venv, installs the wrapper editable
124
+ # plus the dev group: build, twine, pytest, pytest-cov, ruff).
125
+ uv sync
126
+
127
+ # 2. Quality gates.
128
+ uv run ruff check custom_components tests
129
+ uv run pytest # 58 tests, 100% wrapper coverage
130
+
131
+ # 3. Build the component (regenerates the .pyi type stubs and the dist/ artifacts:
132
+ # .whl + .tar.gz). `python -m reflex` keeps the repo root importable so the
133
+ # stub generator can resolve `custom_components.*`.
134
+ uv run python -m reflex component build
135
+
136
+ # 4. Validate the distribution metadata for PyPI.
137
+ uv run twine check dist/*
138
+
139
+ # 5. Publish (requires a PyPI account + API token; see prerequisites docs).
140
+ uv publish # or: uv run twine upload dist/*
141
+ # TestPyPI first: uv publish --publish-url https://test.pypi.org/legacy/
142
+ ```
143
+
144
+ > `reflex component build` produces the same `dist/` artifacts; `uv build` works
145
+ > too (both invoke the setuptools backend). The published wheel ships the `.pyi`
146
+ > stubs and a `py.typed` marker (PEP 561) for IDE autocomplete.
147
+
148
+ To list the component on the Reflex gallery after publishing: `reflex component share`.
149
+
150
+ ## Project layout
151
+
152
+ ```
153
+ reflex-mapgl-maplibre/
154
+ ├── custom_components/reflex_mapgl_maplibre/ # the wrapper package
155
+ │ ├── base.py # shared base: library, CSS, NoSSR
156
+ │ ├── map.py # Map root + imperative camera helpers
157
+ │ ├── markers.py # Marker, Popup
158
+ │ ├── sources.py # Source, Layer
159
+ │ ├── controls.py # Navigation/Geolocate/Fullscreen/Scale/Attribution
160
+ │ ├── provider.py # MapProvider
161
+ │ └── advanced.py # deck.gl overlay & draw control (optional deps)
162
+ ├── mapgl_maplibre_demo/ # demo Reflex app
163
+ ├── specs/ # SDD artifacts (see specs/README.md)
164
+ ├── tests/ # compile/contract tests
165
+ └── scripts/create_github_repo.sh
166
+ ```
167
+
168
+ ## Design docs (SDD)
169
+
170
+ This project is spec-driven. See [`specs/README.md`](specs/README.md):
171
+ PRD · Component API Spec · Technical Design · Phases · Implementation Plan ·
172
+ Tasks · Upstream Analysis (examples catalog).
173
+
174
+ ## Compatibility
175
+
176
+ - Python ≥ 3.10, Reflex ≥ 0.9
177
+ - Frontend: `react-map-gl@^8.1.1`, `maplibre-gl@^5`
178
+
179
+ ## License
180
+
181
+ [MIT](LICENSE). Upstream `react-map-gl` and `maplibre-gl` are MIT-licensed;
182
+ this wrapper is published with attribution.
183
+
184
+ ## Credits
185
+
186
+ - [react-map-gl](https://github.com/visgl/react-map-gl) by vis.gl
187
+ - [MapLibre GL JS](https://maplibre.org/)
188
+ - Wrapper by Ernesto Crespo
@@ -0,0 +1,64 @@
1
+ """reflex-mapgl-maplibre.
2
+
3
+ Reflex component library wrapping ``react-map-gl`` (``react-map-gl/maplibre``
4
+ endpoint) to bring the MapLibre GL JS WebGL map engine to pure-Python Reflex apps.
5
+
6
+ Public API
7
+ ----------
8
+ >>> import reflex_mapgl_maplibre as mapgl
9
+ >>> mapgl.map(
10
+ ... mapgl.navigation_control(position="top-right"),
11
+ ... mapgl.marker(longitude=-122.4, latitude=37.8, color="#ef4444"),
12
+ ... initial_view_state={"longitude": -122.4, "latitude": 37.8, "zoom": 11},
13
+ ... map_style="https://demotiles.maplibre.org/style.json",
14
+ ... style={"width": "100%", "height": "100vh"},
15
+ ... )
16
+
17
+ Advanced (optional, heavy deps) live in ``reflex_mapgl_maplibre.advanced``.
18
+ """
19
+
20
+ from .controls import (
21
+ AttributionControl,
22
+ FullscreenControl,
23
+ GeolocateControl,
24
+ NavigationControl,
25
+ ScaleControl,
26
+ attribution_control,
27
+ fullscreen_control,
28
+ geolocate_control,
29
+ navigation_control,
30
+ scale_control,
31
+ )
32
+ from .map import Map, map
33
+ from .markers import Marker, Popup, marker, popup
34
+ from .provider import MapProvider, map_provider
35
+ from .sources import Layer, Source, layer, source
36
+
37
+ __version__ = "0.1.0"
38
+
39
+ __all__ = [
40
+ # factories (idiomatic, snake_case)
41
+ "map",
42
+ "marker",
43
+ "popup",
44
+ "source",
45
+ "layer",
46
+ "navigation_control",
47
+ "geolocate_control",
48
+ "fullscreen_control",
49
+ "scale_control",
50
+ "attribution_control",
51
+ "map_provider",
52
+ # classes (for typing / subclassing)
53
+ "Map",
54
+ "Marker",
55
+ "Popup",
56
+ "Source",
57
+ "Layer",
58
+ "NavigationControl",
59
+ "GeolocateControl",
60
+ "FullscreenControl",
61
+ "ScaleControl",
62
+ "AttributionControl",
63
+ "MapProvider",
64
+ ]
@@ -0,0 +1,65 @@
1
+ """Optional, heavyweight overlays kept out of the core package (DD-006).
2
+
3
+ These wrap local JSX bridges (``deckgl_overlay.jsx``, ``draw_control.jsx``) that
4
+ use ``react-map-gl/maplibre``'s ``useControl`` hook. They pull large optional npm
5
+ dependencies, so importing this module is opt-in:
6
+
7
+ from reflex_mapgl_maplibre.advanced import deckgl_overlay, draw_control
8
+
9
+ Install the matching extras in your project before using them:
10
+
11
+ * ``deckgl_overlay`` → ``@deck.gl/mapbox`` (and a deck.gl layers package)
12
+ * ``draw_control`` → ``@mapbox/mapbox-gl-draw``
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import reflex as rx
18
+ from reflex.components.component import NoSSRComponent
19
+
20
+ # Copy the local JSX bridges into the compiled frontend's public dir.
21
+ _DECK_PATH = rx.asset("./deckgl_overlay.jsx", shared=True)
22
+ _DRAW_PATH = rx.asset("./draw_control.jsx", shared=True)
23
+
24
+ DECK_PKG = "@deck.gl/mapbox@^9.0.0"
25
+ DRAW_PKG = "@mapbox/mapbox-gl-draw@^1.4.3"
26
+ DRAW_CSS = "@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css"
27
+
28
+
29
+ class DeckGLOverlay(NoSSRComponent):
30
+ """deck.gl overlay rendered on top of the map (returns null in the DOM)."""
31
+
32
+ library = f"$/public{_DECK_PATH}"
33
+ tag = "DeckGLOverlay"
34
+ is_default = False
35
+ lib_dependencies: list[str] = [DECK_PKG]
36
+
37
+ # deck.gl layer instances are constructed in the page module and passed in.
38
+ layers: rx.Var[list]
39
+ get_tooltip: rx.Var
40
+
41
+
42
+ class DrawControl(NoSSRComponent):
43
+ """Polygon/line/point draw control via @mapbox/mapbox-gl-draw."""
44
+
45
+ library = f"$/public{_DRAW_PATH}"
46
+ tag = "DrawControl"
47
+ is_default = False
48
+ lib_dependencies: list[str] = [DRAW_PKG]
49
+
50
+ position: rx.Var[str]
51
+ display_controls_default: rx.Var[bool]
52
+ controls: rx.Var[dict]
53
+ default_mode: rx.Var[str]
54
+
55
+ on_create: rx.EventHandler[rx.event.no_args_event_spec]
56
+ on_update: rx.EventHandler[rx.event.no_args_event_spec]
57
+ on_delete: rx.EventHandler[rx.event.no_args_event_spec]
58
+
59
+ def add_imports(self) -> dict:
60
+ """Inject the draw control stylesheet."""
61
+ return {"": DRAW_CSS}
62
+
63
+
64
+ deckgl_overlay = DeckGLOverlay.create
65
+ draw_control = DrawControl.create