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