Skip to content

Commit

Permalink
improvement: better leafmap formatters (#2852)
Browse files Browse the repository at this point in the history
* Adds formatters for plotly and kepler.
* Warns about using the default leafmap formatter
* Smoke test for different backends. 

opengeos/leafmap#973 will help when doing
`import leafmap`
mscolnick authored Nov 12, 2024
1 parent 6cab387 commit 4353713
Showing 5 changed files with 138 additions and 18 deletions.
4 changes: 4 additions & 0 deletions docs/api/plotting.md
Original file line number Diff line number Diff line change
@@ -110,6 +110,10 @@ selection, consider using [`mo.ui.altair_chart`](#marimo.ui.altair_chart).
.. autofunction:: marimo.mpl.interactive
```

## Leafmap support

marimo supports rendering [Leafmap](https://leafmap.org/) maps using the `folium` and `plotly` backends.

## Other plotting libraries

You can use all the popular plotting libraries with marimo. Such as:
60 changes: 44 additions & 16 deletions marimo/_output/formatters/leafmap_formatters.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# Copyright 2024 Marimo. All rights reserved.
from __future__ import annotations

from typing import Any, cast

from marimo._messaging.mimetypes import KnownMimeType
from marimo._output.builder import h
from marimo._output.formatters.formatter_factory import FormatterFactory
from marimo._output.formatters.utils import src_or_src_doc
from marimo._output.md import md
from marimo._output.utils import flatten_string


@@ -16,18 +15,21 @@ def package_name() -> str:
return "leafmap"

def register(self) -> None:
import leafmap # type: ignore[import-not-found]
# Different backends
# plotly is handled by PlotlyFormatter
import leafmap.foliumap as leafmap_folium # type: ignore[import-untyped]
import leafmap.leafmap as leafmap # type: ignore[import-untyped]

from marimo._output import formatting

@formatting.formatter(leafmap.folium.Map)
@formatting.formatter(leafmap_folium.Map)
def _show_folium_map(
fmap: leafmap.folium.Map,
fmap: leafmap_folium.Map,
) -> tuple[KnownMimeType, str]:
# leafmap.folium.Map has a _repr_html_, which we have
# another custom formatter for, but this wraps the map in an
# additional iframe which can cause weird layout issues
html_content = cast(Any, fmap).to_html()
html_content = fmap.to_html()
return (
"text/html",
flatten_string(
@@ -44,20 +46,46 @@ def _show_folium_map(
def _show_map(
lmap: leafmap.Map,
) -> tuple[KnownMimeType, str]:
# 540px is the pixel height that makes the map fit in the
# notebook without scrolling
height = lmap.layout.height or "540px"
width = lmap.layout.width or "100%"
html_content = lmap.to_html(width=width, height=height)
del lmap
return (
"text/html",
(
md(
"""
leafmap.Map objects are not supported in marimo.
Please change the backend to `folium` or `plotly`.
```python
import leafmap.foliumap as leafmap
# or
import leafmap.plotlymap as leafmap
```
"""
)
.callout()
.text,
)

# Kepler is an optional dependency
# so we don't want this to fail if it's not installed
try:
import leafmap.kepler as leafmap_kepler # type: ignore[import-untyped]

@formatting.formatter(leafmap_kepler.Map)
def _show_kepler_map(
kmap: leafmap_kepler.Map,
) -> tuple[KnownMimeType, str]:
contents = kmap.to_html() or ""
return (
"text/html",
flatten_string(
h.iframe(
**src_or_src_doc(html_content),
**src_or_src_doc(contents),
onload="__resizeIframe(this)",
style="min-height: 540px",
width="100%",
)
)
),
)
),
)

except (ImportError, ModuleNotFoundError):
pass
1 change: 1 addition & 0 deletions marimo/_output/formatters/plotly_formatters.py
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@ def register(self) -> None:
from marimo._output import formatting

@formatting.formatter(plotly.graph_objects.Figure)
@formatting.formatter(plotly.graph_objects.FigureWidget)
def _show_plotly_figure(
fig: plotly.graph_objects.Figure,
) -> tuple[KnownMimeType, str]:
86 changes: 86 additions & 0 deletions marimo/_smoke_tests/third_party/leafmap/backends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import marimo

__generated_with = "0.9.17"
app = marimo.App(width="medium")


@app.cell
def __():
import marimo as mo
return (mo,)


@app.cell
def __():
import leafmap as default_leamap
import leafmap.leafmap as leafmap
import leafmap.foliumap as leafmap_folium
import leafmap.plotlymap as leafmap_plotly
return default_leamap, leafmap, leafmap_folium, leafmap_plotly


@app.cell
def __():
import keplergl
import leafmap.kepler as leafmap_kepler
return keplergl, leafmap_kepler


@app.cell
def __(default_leamap):
default_leamap.Map
return


@app.cell
def __(leafmap):
data = leafmap.examples.datasets.countries_geojson
return (data,)


@app.cell
def __(data, leafmap):
_m = leafmap.Map()
_m.add_data(
data,
column="POP_EST",
scheme="Quantiles",
cmap="Blues",
legend_title="Population",
)
_m
return


@app.cell
def __(leafmap_plotly):
_m = leafmap_plotly.Map(center=(40, -100), zoom=3, height=500)
_m
return


@app.cell
def __(data, leafmap_folium):
_m = leafmap_folium.Map()
_m.add_data(
data,
column="POP_EST",
scheme="Quantiles",
cmap="Blues",
legend_title="Population",
)
_m
return


@app.cell
def __(leafmap_kepler):
_m = leafmap_kepler.Map(
center=[40, -100], zoom=2, height=600, widescreen=False
)
_m
return


if __name__ == "__main__":
app.run()
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -145,6 +145,7 @@ installer = "uv"
dependencies = [
"mypy~=1.10.1",
# Types for mypy
"leafmap~=0.39.2",
"panel~=1.5.3",
"polars~=1.9.0",
"narwhals>=1.12.0",
@@ -179,7 +180,7 @@ python = ["3.9","3.10", "3.11", "3.12"]
test = "pytest{env:HATCH_TEST_ARGS:} {args:tests}"
default = "pytest{env:HATCH_TEST_ARGS:} {args:tests}"
# This is used externally from the narwhals repo to run our tests from their repo.
# This should include any tests that may use narwhals.
# This should include any tests that may use narwhals.
# It is ok if we test more than narwhals here, but we should not test less.
test-narwhals = """
pytest{env:HATCH_TEST_ARGS:} \
@@ -267,7 +268,7 @@ ignore = [
"D301", # Use r""" if any backslashes in a docstring
# TODO: we should fix these, and enable this rule
"PT011", # `pytest.raises(ValueError)` is too broad, set the `match` parameter or use a more specific exception
"E501", # Line too long, we still trim
"E501", # Line too long, we still trim
]
extend-select = [
# pyflakes

0 comments on commit 4353713

Please sign in to comment.