diff --git a/_experimental/create_html.py b/_experimental/create_html.py
new file mode 100644
index 00000000..23e7f72f
--- /dev/null
+++ b/_experimental/create_html.py
@@ -0,0 +1,7 @@
+from maplibre import Map, MapOptions
+from maplibre.basemaps import Carto
+from maplibre.utils import save_map
+
+m = Map(MapOptions(style=Carto.VOYAGER))
+filename = save_map(m, preview=True)
+print(filename)
diff --git a/_experimental/my_marimo_notebook.py b/_experimental/my_marimo_notebook.py
new file mode 100644
index 00000000..a40dba6f
--- /dev/null
+++ b/_experimental/my_marimo_notebook.py
@@ -0,0 +1,90 @@
+import marimo
+
+__generated_with = "0.6.23"
+app = marimo.App(width="medium")
+
+
+@app.cell
+def __():
+ import marimo as mo
+
+ return (mo,)
+
+
+@app.cell
+def __():
+ from maplibre.controls import NavigationControl, ScaleControl
+ from maplibre.ipywidget import MapOptions, MapWidget
+
+ return MapOptions, MapWidget, NavigationControl, ScaleControl
+
+
+@app.cell
+def __():
+ deck_grid_layer = {
+ "@@type": "GridLayer",
+ "id": "GridLayer",
+ "data": "https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json",
+ "extruded": True,
+ "getPosition": "@@=COORDINATES",
+ "getColorWeight": "@@=SPACES",
+ "getElevationWeight": "@@=SPACES",
+ "elevationScale": 4,
+ "cellSize": 200,
+ "pickable": True,
+ }
+ return (deck_grid_layer,)
+
+
+@app.cell
+def __(MapOptions):
+ map_options = MapOptions(
+ center=(-122.4, 37.74),
+ zoom=12,
+ hash=True,
+ pitch=40,
+ )
+ return (map_options,)
+
+
+@app.cell
+def __(MapWidget, NavigationControl, deck_grid_layer, map_options):
+ m = MapWidget(map_options)
+ m.use_message_queue(False)
+ m.add_control(NavigationControl())
+ m.add_deck_layers([deck_grid_layer])
+ m
+ return (m,)
+
+
+@app.cell
+def __(m):
+ m.clicked
+ return
+
+
+@app.cell
+def __(m):
+ m.zoom
+ return
+
+
+@app.cell
+def __(m):
+ m.center
+ return
+
+
+@app.cell
+def __(ScaleControl, m):
+ m.add_control(ScaleControl())
+ return
+
+
+@app.cell
+def __():
+ return
+
+
+if __name__ == "__main__":
+ app.run()
diff --git a/_experimental/my_marimo_notebook_anywidget.py b/_experimental/my_marimo_notebook_anywidget.py
new file mode 100644
index 00000000..f196a440
--- /dev/null
+++ b/_experimental/my_marimo_notebook_anywidget.py
@@ -0,0 +1,88 @@
+import marimo
+
+__generated_with = "0.6.23"
+app = marimo.App(width="medium")
+
+
+@app.cell
+def __():
+ import anywidget
+ import marimo as mo
+ import traitlets
+
+ class CounterWidget(anywidget.AnyWidget):
+ # Widget front-end JavaScript code
+ _esm = """
+ function render({ model, el }) {
+ let getCount = () => model.get("count");
+ let button = document.createElement("button");
+ button.innerHTML = `count is ${getCount()}`;
+ button.addEventListener("click", () => {
+ model.set("count", getCount() + 1);
+ model.save_changes();
+ });
+ model.on("change:count", () => {
+ button.innerHTML = `count is ${getCount()}`;
+ });
+ el.appendChild(button);
+ }
+ export default { render };
+ """
+ _css = """
+ button {
+ padding: 5px !important;
+ border-radius: 5px !important;
+ background-color: #f0f0f0 !important;
+
+ &:hover {
+ background-color: lightblue !important;
+ color: white !important;
+ }
+ }
+ """
+
+ # Stateful property that can be accessed by JavaScript & Python
+ count = traitlets.Int(0).tag(sync=True)
+
+ cw = CounterWidget()
+ widget = mo.ui.anywidget(cw)
+ return CounterWidget, anywidget, cw, mo, traitlets, widget
+
+
+@app.cell
+def __(widget):
+ widget
+ return
+
+
+@app.cell
+def __(widget):
+ widget.value["count"] = 19
+ return
+
+
+@app.cell
+def __(widget):
+ widget.value
+ return
+
+
+@app.cell
+def __(cw):
+ cw.count = 20
+ return
+
+
+@app.cell
+def __(cw):
+ cw.count
+ return
+
+
+@app.cell
+def __():
+ return
+
+
+if __name__ == "__main__":
+ app.run()
diff --git a/docs/changelog.md b/docs/changelog.md
index 1c4e6de8..eb9f9cfe 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -1,5 +1,9 @@
# Changelog for MapLibre for Python
+## maplibre v0.2.6 (unreleased)
+
+* Add function to save map and display it in the browser
+
## maplibre v0.2.5
* Add custom `LayerSwitcherControl` (#69)
diff --git a/maplibre/_utils.py b/maplibre/_utils.py
index 2146b7fb..d4c43797 100644
--- a/maplibre/_utils.py
+++ b/maplibre/_utils.py
@@ -3,6 +3,7 @@
import os
import pathlib
from tempfile import mkdtemp
+from uuid import uuid4
# TODO: from pydantic import BaseModel as PydanticBaseModel
from pydantic import BaseModel as BaseModel_
@@ -13,7 +14,7 @@ def fix_keys(d: dict) -> dict:
return {k.replace("_", "-"): v for k, v in d.items() if v is not None}
-def get_output_dir(output_dir: str = None, prefix: str = "pymaplibregl_") -> str:
+def get_output_dir(output_dir: str = None, prefix: str = "py-maplibre-gl-") -> str:
if not output_dir:
output_dir = mkdtemp(prefix=prefix)
else:
@@ -22,8 +23,12 @@ def get_output_dir(output_dir: str = None, prefix: str = "pymaplibregl_") -> str
return output_dir
+def get_temp_filename(file_extension: str = ".html") -> str:
+ return get_output_dir() + os.sep + str(uuid4()).replace("-", "") + file_extension
+
+
def get_internal_file_path(*args):
- print(os.path.dirname(__file__))
+ # print(os.path.dirname(__file__))
return os.path.join(os.path.dirname(__file__), *args)
diff --git a/maplibre/utils.py b/maplibre/utils.py
index 1912663e..e142ef25 100644
--- a/maplibre/utils.py
+++ b/maplibre/utils.py
@@ -1,8 +1,12 @@
from __future__ import annotations
import json
+import webbrowser
from enum import Enum
+from ._utils import get_temp_filename
+from .map import Map
+
try:
from pandas import DataFrame
except ImportError:
@@ -55,3 +59,16 @@ def get_bounds(geojson: dict) -> list:
return
return list(shapely.bounds(shapely.from_geojson(json.dumps(geojson))))
+
+
+def save_map(map: Map, filename: str = None, preview=True, **kwargs) -> str:
+ if not filename:
+ filename = get_temp_filename()
+
+ with open(filename, "w") as f:
+ f.write(map.to_html(**kwargs))
+
+ if preview:
+ webbrowser.open(filename)
+
+ return filename
diff --git a/pyproject.toml b/pyproject.toml
index ede3e38b..a4ad228c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "maplibre"
-version = "0.2.5"
+version = "0.2.5.1"
description = "Python bindings for MapLibre GL JS"
authors = ["Stefan Kuethe "]
readme = "README.md"
diff --git a/tests/test_internal_utils.py b/tests/test_internal_utils.py
new file mode 100644
index 00000000..a9535336
--- /dev/null
+++ b/tests/test_internal_utils.py
@@ -0,0 +1,14 @@
+from os.path import splitext
+
+from maplibre._utils import get_temp_filename
+
+
+def test_get_temp_filename():
+ filename = get_temp_filename()
+
+ print(filename)
+ filename, file_extension = splitext(filename)
+ print(filename, file_extension)
+
+ assert file_extension == ".html"
+ assert "py-maplibre-gl-" in filename