Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,63 @@
# Changelog

## [0.13.0] - 2025-11-05

### New Features :sparkles:

- feat: Suppress JupyterLab context menu on Map right click by @kylebarron in https://github.com/developmentseed/lonboard/pull/889
- feat: Support two render modes: Standard/deck.gl-first and MapboxOverlay by @kylebarron in https://github.com/developmentseed/lonboard/pull/921
- feat: Create richer `Basemap` class and deprecate `basemap_style` arg by @kylebarron in https://github.com/developmentseed/lonboard/pull/935
- feat: Support globe view by @kylebarron in https://github.com/developmentseed/lonboard/pull/908
- feat: Move `TripsLayer` and `ArcLayer` out of experimental by @kylebarron in https://github.com/developmentseed/lonboard/pull/983
- feat: Support for map controls by @kylebarron in https://github.com/developmentseed/lonboard/pull/924
- feat: Provide default controls for Map by @kylebarron in https://github.com/developmentseed/lonboard/pull/992
- feat: Switch basemap default to overlaid (maplibre-controlled) by @kylebarron in https://github.com/developmentseed/lonboard/pull/993
- feat: H3 layer by @kylebarron in https://github.com/developmentseed/lonboard/pull/917
- feat: Add A5Layer by @kylebarron in https://github.com/developmentseed/lonboard/pull/1001
- feat: Add geohash and s2 layers by @kylebarron in https://github.com/developmentseed/lonboard/pull/1007
- feat: Implement view state validation for non map-view states by @kylebarron in https://github.com/developmentseed/lonboard/pull/1008
- feat: Validate that GlobeView is only used with interleaved basemap mode by @kylebarron in https://github.com/developmentseed/lonboard/pull/1012
- feat: Add view parameter to viz by @kylebarron in https://github.com/developmentseed/lonboard/pull/1013

### Performance improvements :zap:

- perf: use ThreadPoolExecutor for Parquet serialization and bounds computation by @kylebarron in https://github.com/developmentseed/lonboard/pull/902
- perf: Define top-level earcut worker pool by @kylebarron in https://github.com/developmentseed/lonboard/pull/954
- perf: Call np.mean once across axis instead of once per column by @kylebarron in https://github.com/developmentseed/lonboard/pull/995
- perf: Remove typedArrayManager settings override by @kylebarron in https://github.com/developmentseed/lonboard/pull/1003
- perf: Avoid generating str repr of `table` during rendering by @kylebarron in https://github.com/developmentseed/lonboard/pull/1015

### Bug fixes :bug:

- fix: Fix HeatmapLayer by bumping deck.gl to 9.2 by @kylebarron in https://github.com/developmentseed/lonboard/pull/910
- fix: Add `pyarrow` to `geopandas` extra in `pyproject.toml` by @kylebarron in https://github.com/developmentseed/lonboard/pull/970
- fix: Fix "fuzziness" of path layers by removing `useDevicePixels` default by @kylebarron in https://github.com/developmentseed/lonboard/pull/969
- fix: Ensure that the default value of `Map.basemap` is `MaplibreBasemap` if no value of `basemap` was passed. by @kylebarron in https://github.com/developmentseed/lonboard/pull/963
- fix: Restore DataFilterExtension functionality with vectorized accessor input by @kylebarron in https://github.com/developmentseed/lonboard/pull/977
- fix: Fix ArcLayer with numpy coords input by @kylebarron in https://github.com/developmentseed/lonboard/pull/989
- fix: Ensure view state is updated in Python with a maplibre map renderer by @kylebarron in https://github.com/developmentseed/lonboard/pull/1017
- fix: Hack a react re-render on canvas resize by @kylebarron in https://github.com/developmentseed/lonboard/pull/1022

### Documentation :book:

- docs: Fix list rendering in `Map.add_layer` API Docs by @kylebarron in https://github.com/developmentseed/lonboard/pull/952
- docs: Add developer docs about code profiling by @kylebarron in https://github.com/developmentseed/lonboard/pull/1016

### Breaking changes :hammer:

- chore!: Remove deprecated `con` parameter to `viz` by @kylebarron in https://github.com/developmentseed/lonboard/pull/953

### Other changes

- feat: automated UI testing with Playwright by @vgeorge in https://github.com/developmentseed/lonboard/pull/906
- chore: Add eslint import ordering to CI by @kylebarron in https://github.com/developmentseed/lonboard/pull/931
- test: add bbox selection overlay mode e2e test by @vgeorge in https://github.com/developmentseed/lonboard/pull/933
- refactor: Refactor HTML map export by @kylebarron in https://github.com/developmentseed/lonboard/pull/934
- refactor: Refactor model initialization logic by @kylebarron in https://github.com/developmentseed/lonboard/pull/923
- test: add pytest-ipywidgets bbox testing, remove playwright by @vgeorge in https://github.com/developmentseed/lonboard/pull/999

**Full Changelog**: https://github.com/developmentseed/lonboard/compare/v0.12.1...v0.13.0

## [0.12.1] - 2025-09-18

### New Features :sparkles:
Expand Down
18 changes: 18 additions & 0 deletions DEVELOP.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,24 @@ Note that the `jupyter-mkdocs` plugin is only turned on when the `CI` env variab
CI=true uv run --group docs mkdocs serve
```

## Developing Jupyter Notebook examples

We use `juv` to store dependencies for each notebook as metadata of the notebook itself.

To run an example notebook with a local version of lonboard, first ensure that you have built the JavaScript bundle:

```bash
npm run build:watch
```

then use:

```bash
ANYWIDGET_HMR=1 uvx juv run --with="../" examples/air-traffic-control.ipynb
```

Note that the path in `--with` is relative to the notebook itself.

## Profiling

### Python
Expand Down
1 change: 1 addition & 0 deletions docs/api/basemap.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# lonboard.basemap

::: lonboard.basemap.MaplibreBasemap
::: lonboard.basemap.CartoStyle
5 changes: 5 additions & 0 deletions docs/api/controls.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# lonboard.controls

::: lonboard.controls.BaseControl
::: lonboard.controls.FullscreenControl
::: lonboard.controls.NavigationControl
::: lonboard.controls.ScaleControl

::: lonboard.controls.MultiRangeSlider
5 changes: 5 additions & 0 deletions docs/api/layers/a5-layer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# A5Layer

::: lonboard.A5Layer
options:
inherited_members: true
5 changes: 5 additions & 0 deletions docs/api/layers/geohash-layer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# GeohashLayer

::: lonboard.GeohashLayer
options:
inherited_members: true
9 changes: 9 additions & 0 deletions docs/api/layers/h3-hexagon-layer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# H3HexagonLayer

![](../../assets/kontur-h3.jpg)

> Screenshot from [H3 Population](../../examples/kontur_pop) example

::: lonboard.H3HexagonLayer
options:
inherited_members: true
7 changes: 0 additions & 7 deletions docs/api/layers/heatmap-layer.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
# HeatmapLayer

!!! warning
The `HeatmapLayer` is not currently working.

As of Lonboard v0.10, Lonboard upgraded to version 9.0 of the underlying [deck.gl](https://deck.gl/) library. deck.gl [appears to have a bug](https://github.com/visgl/deck.gl/issues/8960#issuecomment-2284791644) with the HeatmapLayer in 9.0, that has not yet been fixed.

Please temporarily downgrade to Lonboard v0.9 if you would like to use the `HeatmapLayer`.

![](../../assets/duckdb-heatmap.jpg)

> Screenshot from [DuckDB Spatial](../../examples/duckdb) example
Expand Down
5 changes: 5 additions & 0 deletions docs/api/layers/s2-layer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# S2Layer

::: lonboard.S2Layer
options:
inherited_members: true
6 changes: 6 additions & 0 deletions docs/api/map.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,9 @@ https://mkdocstrings.github.io/python/usage/configuration/members/#filters
group_by_category: false
show_bases: false
filters:

::: lonboard.types.map.MapKwargs
options:
group_by_category: false
show_if_no_docstring: true
filters:
5 changes: 5 additions & 0 deletions docs/api/traits.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
# lonboard.traits

::: lonboard.traits.A5Accessor
::: lonboard.traits.ArrowTableTrait
::: lonboard.traits.BasemapUrl
::: lonboard.traits.ColorAccessor
::: lonboard.traits.DashArrayAccessor
::: lonboard.traits.FilterValueAccessor
::: lonboard.traits.FloatAccessor
::: lonboard.traits.H3Accessor
::: lonboard.traits.MapHeightTrait
::: lonboard.traits.NormalAccessor
::: lonboard.traits.PointAccessor
::: lonboard.traits.TextAccessor
::: lonboard.traits.TimestampAccessor
::: lonboard.traits.ViewStateTrait
9 changes: 3 additions & 6 deletions docs/api/view.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
# lonboard.view

::: lonboard.view.BaseView
::: lonboard.view.FirstPersonView
::: lonboard.view.GlobeView
::: lonboard.view.MapView
::: lonboard.view.OrbitView
::: lonboard.view.OrthographicView
::: lonboard.view
options:
members_order: source
9 changes: 3 additions & 6 deletions docs/api/view_state.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
# lonboard.view_state

::: lonboard.view_state.BaseViewState
::: lonboard.view_state.MapViewState
::: lonboard.view_state.GlobeViewState
::: lonboard.view_state.FirstPersonViewState
::: lonboard.view_state.OrthographicViewState
::: lonboard.view_state.OrbitViewState
::: lonboard.view_state
options:
members_order: source
1 change: 1 addition & 0 deletions examples/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- [Overture Maps buildings ![](../assets/overture.jpg)](../examples/overture-maps) using [`PolygonLayer`](../api/layers/polygon-layer)
- [Air Traffic Control animation ![](../assets/air-traffic-control.gif)](../examples/air-traffic-control) using [`TripsLayer`](../api/layers/trips-layer)
- [Global boundaries ![](../assets/boundaries.png)](../examples/global-boundaries) using [`PolygonLayer`](../api/layers/polygon-layer)
- [H3 Population Data ![](../assets/kontur-h3.jpg)](../examples/kontur_pop) using [`H3HexagonLayer`](../api/layers/h3-hexagon-layer)
- [U.S. County-to-County Migration ![](../assets/arc-layer-migration-example.gif)](../examples/migration) using [`ArcLayer`](../api/layers/arc-layer) and [`BrushingExtension`](../api/layer-extensions/brushing-extension)
- [Scatterplot with GPU data filtering ![](../assets/data-filter-extension.gif)](../examples/data-filter-extension) using [`ScatterplotLayer`](../api/layers/scatterplot-layer) and [`DataFilterExtension`](../api/layer-extensions/data-filter-extension)
- [Motor Vehicle Crashes in NYC ![](../assets/motor-vehicle-crashes-nyc.jpg)](../examples/map_challenge/1-points) using [`ScatterplotLayer`](../api/layers/scatterplot-layer)
Expand Down
35 changes: 24 additions & 11 deletions lonboard/_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
ScaleControl,
)
from lonboard.layer import BaseLayer
from lonboard.traits import HeightTrait, VariableLengthTuple, ViewStateTrait
from lonboard.traits import MapHeightTrait, VariableLengthTuple, ViewStateTrait
from lonboard.view import BaseView, GlobeView, MapView
from lonboard.view_state import BaseViewState, GlobeViewState, MapViewState

Expand Down Expand Up @@ -49,7 +49,7 @@


class Map(BaseAnyWidget):
"""The top-level class used to display a map in a Jupyter Widget.
"""An interactive Map.

**Example:**

Expand All @@ -73,6 +73,17 @@ class Map(BaseAnyWidget):

m = Map([point_layer, polygon_layer])
```

**Example:** Creating a Map with GlobeView:

```py
import geopandas as gpd
from lonboard import Map
from lonboard.view import GlobeView

# Continuing from above example
m = Map([point_layer, polygon_layer], view=GlobeView())
```
"""

def __init__(
Expand All @@ -95,7 +106,7 @@ def __init__(

Various styles are provided in [`lonboard.basemap`](https://developmentseed.org/lonboard/latest/api/basemap/).

kwargs: Passed on to class variables.
kwargs: Passed on to class variables. For example, you can pass `height=600` to pass that value on to the [`height`][lonboard.Map.height] attribute.

Returns:
A Map object.
Expand Down Expand Up @@ -191,17 +202,20 @@ def on_click(self, callback: Callable, *, remove: bool = False) -> None:
Indicates if a click handler has been registered.
"""

height = HeightTrait().tag(sync=True)
height = MapHeightTrait().tag(sync=True)
"""Height of the map in pixels, or valid CSS height property.

This API is not yet stabilized and may change in the future.
For example, it can be `600` (pixels) or `"75vh"` (75% of the viewport height).

- Type: `int` or `str`
- Default: full height of the containing cell.
"""

layers = VariableLengthTuple(t.Instance(BaseLayer)).tag(
sync=True,
**ipywidgets.widget_serialization,
)
"""One or more `Layer` objects to display on this map.
"""One or more [`Layer`][lonboard.BaseLayer] objects to display on this map.
"""

controls = VariableLengthTuple(
Expand All @@ -221,9 +235,11 @@ def on_click(self, callback: Callable, *, remove: bool = False) -> None:
sync=True,
**ipywidgets.widget_serialization,
)
"""A View instance.
"""The view to use for this map.

Views represent the "camera(s)" (essentially viewport dimensions and projection matrices) that you look at your data with. deck.gl offers multiple view types for both geospatial and non-geospatial use cases. Read the [Views and Projections](https://deck.gl/docs/developer-guide/views) guide for the concept and examples.

See [`lonboard.view`][lonboard.view] for available view types.
"""

@t.validate("view")
Expand Down Expand Up @@ -403,10 +419,7 @@ def basemap_style(self, value: str | CartoStyle) -> None:
parameters = t.Any(allow_none=True, default_value=None).tag(sync=True)
"""GPU parameters to pass to deck.gl.

**This is an advanced API. The vast majority of users should not need to touch this
setting.**

!!! Note
!!! Note "This is an advanced API. The vast majority of users should not need to touch this setting."

The docstring below is copied from upstream deck.gl documentation. Any usage of
`GL` refers to the constants defined in [`@luma.gl/constants`
Expand Down
21 changes: 11 additions & 10 deletions lonboard/_viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,18 @@ def viz( # noqa: PLR0913
"""Plot your data easily.

The goal of this function is to make it simple to get _something_ showing on a map.
For more control over rendering, construct `Map` and `Layer` objects directly.
For more control over rendering, construct [`Map`][lonboard.Map] and
[`Layer`][lonboard.BaseLayer] objects directly.

This function accepts a variety of geospatial inputs:

- GeoPandas `GeoDataFrame`.
- GeoPandas `GeoSeries`.
- GeoPandas [`GeoDataFrame`][geopandas.GeoDataFrame].
- GeoPandas [`GeoSeries`][geopandas.GeoSeries].
- numpy array of Shapely objects.
- Single Shapely object.
- A DuckDB query with a spatial column from DuckDB Spatial.

!!! warning
!!! info

The DuckDB query must be run with
[`duckdb.sql()`](https://duckdb.org/docs/api/python/reference/#duckdb.sql)
Expand Down Expand Up @@ -134,7 +135,7 @@ def viz( # noqa: PLR0913
viz(con.table("spatial_table"))
```

!!! warning
!!! info

DuckDB Spatial does not currently expose coordinate reference system
information, so the user must ensure that data has been reprojected to
Expand All @@ -143,16 +144,16 @@ def viz( # noqa: PLR0913
- Any Python class with a `__geo_interface__` property conforming to the
[Geo Interface protocol](https://gist.github.com/sgillies/2217756).
- `dict` holding GeoJSON-like data.
- pyarrow `Table` with a geometry column marked with a
- pyarrow [`Table`][pyarrow.Table] with a geometry column marked with a
[GeoArrow](https://geoarrow.org/) extension type.
- pyarrow `Array` or `ChunkedArray` marked with a [GeoArrow extension type defined by geoarrow-pyarrow](https://geoarrow.org/geoarrow-python/main/pyarrow.html#geoarrow.pyarrow.GeometryExtensionType).
- Arrow-compatible Array, ChunkedArray, Table, or RecordBatch objects that have associated GeoArrow metadata. An object is "Arrow-compatible" if it implements the [Arrow PyCapsule Interface](https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html) and has either an `__arrow_c_array__` or `__arrow_c_stream__` method. The provided Arrow data must be or have a geometry column marked with a GeoArrow extension type.
- pyarrow [`Array`][pyarrow.Array] or [`ChunkedArray`][pyarrow.ChunkedArray] marked with a [GeoArrow extension type defined by geoarrow-pyarrow][geoarrow.pyarrow.GeometryExtensionType].
- Arrow-compatible Array, ChunkedArray, Table, or RecordBatch objects that have associated GeoArrow metadata. An object is "Arrow-compatible" if it has either an `__arrow_c_array__` or `__arrow_c_stream__` method, that is, it implements the [Arrow PyCapsule Interface](https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html). The provided Arrow data must be or have a geometry column marked with a GeoArrow extension type.

Some examples of these sources include pyogrio's [`open_arrow`][pyogrio.open_arrow], DuckDB Spatial, [GeoArrow-Rust's Python bindings](https://geoarrow.org/geoarrow-rs/python/latest/), [GeoDataFusion database connections](https://github.com/datafusion-contrib/datafusion-geo), and, soon, [GeoPolars DataFrames](https://github.com/geopolars/geopolars).
Some examples of these sources include pyogrio's [`open_arrow`][pyogrio.open_arrow], DuckDB Spatial, [GeoArrow-Rust's Python bindings](https://geoarrow.org/geoarrow-rs/python/latest/), [Sedona DB sessions](https://github.com/apache/sedona-db), [GeoDataFusion database connections](https://github.com/datafusion-contrib/datafusion-geo), and, soon, [GeoPolars DataFrames](https://github.com/geopolars/geopolars).

Alternatively, you can pass a `list` or `tuple` of any of the above inputs.

If you want to easily add more data, to an existing map, you can pass the output of
If you want to easily add more data to an existing map, you can pass the output of
`viz` into [`Map.add_layer`][lonboard.Map.add_layer].

Args:
Expand Down
Loading