# lonboard: GPU map rendering inside a Jupyter notebook

> lonboard puts deck.gl in a notebook, so you can render millions of geospatial features interactively from a GeoDataFrame or a GeoParquet file. The Python side is thin and mostly about Arrow, and the JavaScript side is a full React and WebGL application that happens to live in a widget.

**developmentseed/lonboard** — Fast, interactive geospatial data visualization in Jupyter.

- Repository: https://github.com/developmentseed/lonboard
- Website: https://developmentseed.org/lonboard
- Stars: 972 · Forks: 56
- Language: Python
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/developmentseed-lonboard

## Three million points, one notebook cell

The headline example in the readme is the whole pitch in a sentence: three million points rendered from a GeoDataFrame in JupyterLab.

That number is the point. Plotting libraries that draw into a notebook image have to push every feature through a Python loop and then rasterise the result, which turns an interactive exploration into a coffee break. A GPU based renderer draws the same features in the browser, so the cost of three million points is a data transfer rather than a computation.

The technology underneath is described in the same paragraph: GeoArrow, GeoParquet, and GPU based map rendering through deck.gl. The first two are the data formats that make the approach work. GeoArrow is a specification for putting geometry into Arrow arrays, and GeoParquet is the Parquet equivalent, storing geometry in the same columnar file format as everything else in the dataset. The advantage is that a geometry column becomes ordinary Arrow data, so it can be transferred to the browser as Arrow rather than being serialised to GeoJSON and parsed there, and it can be filtered with the same machinery as the rest of the table.

The readme also explains the name, which is a small pleasure. This is a new binding to the deck.gl geospatial data visualisation library, and a deck is the part of a skateboard you ride on, so a fast geospatial skateboard is a lonboard. The logo illustration is credited to an artist by name.

So the project is a Jupyter widget that wraps deck.gl, and the interesting engineering is in how a Python object becomes a WebGL scene without a copy step in between.

## Install and the shortest possible program

Installation is the standard pair.

```bash
pip install lonboard
```

```bash
conda install -c conda-forge lonboard
```

The conda route exists because the project depends on compiled geospatial components and conda-forge handles that more reliably than pip for some platforms. The package is also published to PyPI, and there is a Binder badge, a Google Colab badge and a JupyterLite badge, so you can try it in a browser with no local setup at all.

The shortest program is four lines.

```py
import geopandas as gpd
from lonboard import viz

gdf = gpd.GeoDataFrame(...)
viz(gdf)
```

The viz function inspects the geometry type and delegates to one of three layer classes, a scatterplot layer for points, a path layer for lines and a polygon layer for areas. That delegation is the honest scope of the convenience: one call covers the three cases that cover most data, and for anything else, and for any control over styling, you construct the layer yourself and read the documentation and the examples.

The examples directory is worth browsing before you write anything, because it is where the library shows what it can do that a plotting function cannot. There are notebooks for air traffic control, moving vessel tracks, a clicked point callback, a column layer, a categorical and a general data filter extension, DuckDB integration, a geocoder control, a geohash layer, global boundaries, interleaved labels, internet speeds, a Kontur population dataset, linked maps, a migration notebook, North American roads, Overture GeoParquet and Overture maps, raster tiles served as Cloud Optimised GeoTIFF over HTTP, and raster tiles served as PMTiles.

That last group is the one that changes the shape of a project. A notebook that streams raster tiles from a remote server can visualise a dataset that would never fit in memory.

## The Python side is thin, and the JavaScript side is not

The dependency lists are the clearest statement of the architecture, and they divide neatly.

On the Python side the manifest requires anywidget, which is the framework for Jupyter widgets that carry their own JavaScript and CSS, ipywidgets, traitlets, numpy, pyproj, typing extensions, and three packages from the Arrow Rust ecosystem named arro3 for core, io and compute, plus geoarrow-rust-core. Python 3.11 or later is required, and the project classifies itself as beta.

That is a short list, and the key detail is the presence of the Rust Arrow packages. The Python side is not doing geometry processing in Python, it is converting Arrow data into a form the browser can consume, and doing that conversion in Rust is why the data transfer is fast enough to make the approach worthwhile.

The JavaScript side is where the application lives, and the list is long. There is the full set of deck.gl packages at version 9.4, from core and layers through the geo layers, the aggregation layers, the mesh layers, the extensions, the maplibre integration, the React bindings and the widgets. There is luma.gl, the rendering layer beneath deck.gl. There is Apache Arrow in JavaScript, geoarrow-js and the deck.gl GeoArrow extension, and parquet-wasm for reading Parquet in the browser. MapLibre GL is the map renderer, react-map-gl is the React binding, React 19 with zustand for state, framer-motion for animation, and a component library in HeroUI.

So the widget is a full single page application. That is the price of the feature set, and it is worth knowing before you install, because a notebook cell that mounts a web mapping stack has a startup cost and a memory cost that a matplotlib figure does not.

## Optional extras for the things most people do not need

The optional dependency groups are the clearest statement of what the core does and does not do.

There is a group for GeoPandas, with GeoPandas itself, pandas, pyarrow and shapely. There is a group for a command line interface, with click, pyogrio and shapely. There is a group for MovingPandas, for animated tracks, and a group for GeoTIFF support, with an async GeoTIFF reader and morecantile for tile handling. The GeoTIFF extras are marked as requiring Python 3.11 or later.

What the core does not require is the most telling part. GeoPandas is not a dependency, and neither is pandas. The core needs Arrow, and it needs a geometry type that it can convert, but the library does not require you to arrive with a GeoDataFrame. The viz call in the readme takes one because that is the common case, not because it is the only supported input.

That is a deliberate design decision, and it is the one that separates lonboard from a plotting helper for GeoPandas. A library that required GeoPandas would be a library about GeoDataFrames. This one is about Arrow tables with geometry columns, which is the format that survives a trip through Parquet, through DuckDB and through a remote server without being converted into something else first.

The optional extras are therefore best read as a menu of the ecosystems the library can join, rather than as a list of things you need. A team whose pipeline already produces GeoParquet needs none of them.

## Notebooks, Marimo and the Jupyter ecosystem

The project lives in the Jupyter ecosystem and takes its optional deployment targets seriously.

The badges cover PyPI, conda-forge, Binder, Google Colab and JupyterLite, and the last of those is a browser-based Jupyter distribution that the project publishes its own documentation build into, so the getting started notebook runs with no server and no install. The examples directory includes a marimo folder, which is a sensible inclusion for a library whose output is a live widget rather than a printed result.

The repository layout reflects the split between the two languages. There is a Python package directory, a source directory for the TypeScript side, a build script written in JavaScript, a package manifest with pnpm workspaces, a Biome configuration for the JavaScript, a Vitest configuration for the JavaScript tests, a TypeScript configuration, and a Python packaging configuration with a uv lock file and a pre-commit configuration.

The build script and the workspace setup are the mechanism for shipping a widget. A Jupyter widget is a Python class plus a JavaScript bundle plus a matching model, and getting the three versions to agree is the fiddly part of the whole exercise. Doing it in a workspace with a build step means the JavaScript is compiled and bundled once, and the Python side loads the artefact.

The examples also include a migration notebook, which is a useful signal about how the API has moved, and a map challenge directory, which is the kind of small comparative exercise that tells you more about a library's behaviour than a feature list does.

## Maintenance, and the fact that this is a beta

The project is moving quickly and the versions say so.

Version 0.15.0 came out in March 2026, 0.16.0 in April and 0.17.0 in October, with the last push on 2026-10-19. The repository is not archived, and the release is published to both PyPI and conda-forge, with a changelog in the repository and a developer document for building from source.

The fast version movement is worth understanding rather than worrying about. A library that binds a fast moving JavaScript dependency, here the deck.gl 9.4 line, has to move with it, and the JavaScript dependencies sit in caret ranges over a compatible deck.gl line.

The package metadata classifies the project as beta, at development status four, and says so plainly rather than hiding it in a release note. For a library whose whole value is that it is the bridge between two fast moving ecosystems, that is the honest label.

The licence is MIT and the package is maintained by DevelopmentSeed, the company behind the project, with the homepage and documentation on their site and the author named in the metadata.

If you are evaluating it, the two things to check first are the data path and the layer coverage. The data path is whether your data is already Arrow or Parquet, because that is what the performance rests on, and the layer coverage is whether your geometry type is one of the three the convenience function handles or one of the specialised layers in the examples, since a polygon layer is a different piece of work from a scatterplot layer and a mesh layer is different again.

## Conclusion

lonboard suits a data scientist who needs to look at a large geospatial dataset interactively inside a notebook rather than export a picture, and its Arrow and GeoParquet foundations mean it can handle data that a plotting library would choke on. It is a poor fit if you need a production map application, since the widget is a notebook citizen, and a poor fit if your data is small, since the setup cost of a web mapping stack is only worth paying for volume. Before you adopt it, check that your data is already Arrow or Parquet backed, since converting a plain pandas frame is where the time goes, and read the examples for the layer type you need, because the one-line viz call only covers points, paths and polygons.

## FAQ

### How do I install lonboard?

With pip install lonboard, or with conda install -c conda-forge lonboard, which is the more reliable route for the compiled geospatial dependencies. The package is also available through Binder, Google Colab and a published JupyterLite build, so you can try it in a browser with no local setup.

### Do I need GeoPandas to use lonboard?

No. GeoPandas is an optional extra, not a core dependency, because the library works on Arrow data with geometry columns rather than on GeoDataFrames specifically. The convenience viz function accepts a GeoDataFrame because that is the common case, and the optional geopandas group installs GeoPandas, pandas, pyarrow and shapely if you need it.

### What does the lonboard viz function do?

It inspects the geometry type and delegates to a scatterplot layer for points, a path layer for lines or a polygon layer for areas, which is what the readme means by the simplest rendering. For more control over the styling or for other layer types, you construct the layer classes yourself and follow the documentation and examples.

### How much data can lonboard render?

The readme's headline example renders three million points from a GeoDataFrame in JupyterLab, using GeoArrow and GeoParquet with GPU based rendering through deck.gl. The examples also include streaming raster tiles from remote servers as Cloud Optimised GeoTIFF or PMTiles, so the size of what you can explore is not limited to what fits in memory.

## Sources

- [developmentseed/lonboard on GitHub](https://github.com/developmentseed/lonboard)
- [License: MIT](https://github.com/developmentseed/lonboard/blob/main/LICENSE)
- [Project website](https://developmentseed.org/lonboard)
- [README](https://github.com/developmentseed/lonboard/blob/main/README.md)
- [Releases](https://github.com/developmentseed/lonboard/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/developmentseed-lonboard
