Create Maps with keplergl
Use the keplergl Python package to create standalone, interactive HTML map files from geospatial data. The exported HTML loads kepler.gl from CDN — no JavaScript build or server is needed. The resulting .html file can be opened directly in any browser.
Installation
pip install keplergl
Requires keplergl >= 0.4.0. Earlier versions use a different widget/serialization API and the examples in this skill will not work. Requirements: Python >= 3.9. Dependencies (pandas, geopandas, shapely) are installed automatically.
Instructions
- Import
KeplerGl from keplergl
- Load data as a DataFrame, GeoDataFrame, GeoJSON dict, or CSV string
- Create a map with
KeplerGl(data={'name': data_object})
- Optionally configure layers, colors, and map state via a
config dict (default to quantile color scale and a vibrant palette for quantitative color encoding when the user does not specify)
- Export with
map.save_to_html(file_name='output.html', center_map=True)
- The output HTML is fully standalone — open it in any browser
API Reference
KeplerGl(data=None, config=None, height=400, mapbox_token="", use_arrow=False, show_docs=False, theme="", app_name="kepler.gl", **kwargs)
| Parameter |
Type |
Default |
Description |
height |
int |
400 |
Map height in pixels |
data |
dict |
None |
{"dataset_name": data_object} |
config |
dict |
None |
Map configuration (layers, filters, map state) |
mapbox_token |
str |
"" |
Mapbox token (only for Mapbox basemap styles) |
use_arrow |
bool |
False |
Serialize DataFrames as Arrow IPC (more compact, preserves types) |
show_docs |
bool |
False |
Deprecated (kept for compatibility) |
theme |
str |
"" |
"light", "dark", "base", or "" (default dark) |
app_name |
str |
"kepler.gl" |
App name in header and HTML title |
.add_data(data, name="data", use_arrow=None)
data: DataFrame, GeoDataFrame, CSV string, GeoJSON dict, or GeoJSON string
name: Dataset identifier (default: "data") — must match dataId in config if using a config
use_arrow: If True, serialize this DataFrame as Arrow IPC. If None (default), falls back to the widget-level use_arrow setting. Has no effect on GeoDataFrames.
.save_to_html(file_name="keplergl_map.html", data=None, config=None, read_only=False, center_map=True, mapbox_token="", json_encoder=str, app_name=None, theme=None)
| Parameter |
Type |
Default |
Description |
file_name |
str |
"keplergl_map.html" |
Output file path |
data |
dict |
None |
Data override for export (uses current widget data when None) |
config |
dict |
None |
Config override for export (uses current widget config when None) |
read_only |
bool |
False |
True = hide side panel |
center_map |
bool |
True |
True = auto-fit map to data bounds |
mapbox_token |
str |
"" |
Mapbox token override for export |
json_encoder |
callable |
str |
Fallback encoder for non-JSON-native values in GeoDataFrames |
app_name |
str |
None |
App name override for export title/header |
theme |
str |
None |
Theme override for export ("light", "dark", "base", or "") |
.config
Read or set the map configuration dict. Use map.config after customizing in Jupyter UI, then save and reuse.
Key Rules
dataId must match the dataset name — every layer and filter references a dataset by dataId; this must match the key in the data dict or the name passed to add_data().
- GeoJSON columns use
_geojson — when data is loaded as GeoJSON, the geometry column is internally named _geojson in configs.
colorField / colorScale / sizeField / heightField etc. belong under visualChannels, NOT under config. Putting them under config is silently ignored — the layer will render but the "Color Based On (field)" input shows empty. The layer object must have two siblings: config (for dataId, columns, visConfig, …) and visualChannels (for all field-to-channel mappings).
- Columns named
latitude/lat/lng/longitude are auto-detected as coordinates.
- H3 hex IDs are auto-detected if a column contains valid H3 strings.
- Use
center_map=True to auto-fit map bounds. Use read_only=True to hide the side panel.
- For numeric color encoding, if the user does not specify a color scale, use
visualChannels.colorScale: 'quantile'.
- For numeric color encoding, if the user does not specify a palette, use a vibrant sequential/diverging palette (for example,
colorRange.name: 'Global Warming').
- If the user asks for custom class breaks, compute breakpoints in Python first (for example with
pygeoda), add a derived classified/bin column to the dataset, and map colors using that derived field.
- No
SampleMapPanel in standalone exports. The SampleMapPanel React component lives in the kepler.gl demo app, not in the UMD bundle used by save_to_html(). To show a summary/legend overlay, inject an HTML+CSS <div> into the exported file (position it at right: 56px or left: 66px so it doesn't block map controls). See Summary Panel Overlay.
Supported Data Formats
| Format |
How to Load |
| pandas DataFrame |
Columns with lat/lng (or similar) for point data |
| geopandas GeoDataFrame |
Geometry column auto-detected. Interactive widget serialization uses GeoArrow (no CRS reprojection); HTML export path re-projects to EPSG:4326 when needed. |
| CSV string |
Raw CSV text with lat/lng or geometry columns |
| GeoJSON dict |
Feature or FeatureCollection as Python dict |
| GeoJSON string |
JSON string of GeoJSON |
| WKT in DataFrame |
DataFrame column containing WKT geometry strings |
Layer Types
| Layer Type |
Config type |
Typical Data |
| Point |
"point" |
DataFrame with lat/lng columns |
| Arc |
"arc" |
DataFrame with origin/destination lat/lng |
| Line |
"line" |
DataFrame with origin/destination lat/lng |
| Hexbin |
"hexagon" |
DataFrame with lat/lng (aggregated spatially) |
| Heatmap |
"heatmap" |
DataFrame with lat/lng |
| H3 Hexagon |
"hexagonId" |
DataFrame with H3 hex ID column |
| GeoJSON / Polygon |
"geojson" |
GeoJSON or GeoDataFrame with polygon/line geometries |
| Cluster |
"cluster" |
DataFrame with lat/lng |
| Icon |
"icon" |
DataFrame with lat/lng |
| Trip |
"trip" |
GeoJSON with LineString + timestamps |
| S2 |
"s2" |
DataFrame with S2 token column |
Config Structure
config = {
'version': 'v1',
'config': {
'visState': {
'layers': [...], # Layer definitions
'filters': [...], # Data filters
'interactionConfig': {}, # Tooltips, brush, geocoder
'splitMaps': [], # Split map views
'layerBlending': 'normal' # 'normal', 'additive', 'subtractive'
},
'mapState': {
'latitude': 37.76,
'longitude': -122.4,
'zoom': 11,
'bearing': 0,
'pitch': 0,
'dragRotate': False,
'isSplit': False
},
'mapStyle': {
'styleType': 'dark-matter'
}
}
}
Basemap Styles
Free (no token needed): dark-matter, positron, voyager, dark-matter-nolabels, positron-nolabels, voyager-nolabels
Mapbox (require mapbox_token): dark, light, muted, muted_night
Additional Resources
For detailed per-layer-type examples with full config, see supporting files:
- Point Map — Scatter plot from lat/lng
- GeoJSON / Polygon Map — Polygons, lines from GeoJSON or GeoDataFrame
- H3 Hexagon Map — H3 spatial index hexagons
- Arc / Line Map — Origin-destination connections
- Heatmap — Density heatmap from points
- Hexbin Aggregation Map — Spatial binning into hexagons
- Trip Animation Map — Animated trips along paths
- Summary Panel Overlay — Inject a SampleMapPanel-style info overlay into the exported HTML (for LISA/cluster counts, model summaries, custom legends)
Examples
For full config examples per layer type, see:
- Point Map — includes quantile color + vibrant palette config
- GeoJSON / Polygon Map — includes choropleth config with
visualChannels
Quick start (auto-detected layers, no config needed)
from keplergl import KeplerGl
import pandas as pd
df = pd.DataFrame({
'lat': [37.7749, 34.0522, 40.7128],
'lng': [-122.4194, -118.2437, -74.0060],
'name': ['San Francisco', 'Los Angeles', 'New York'],
'value': [15, 42, 27]
})
map_1 = KeplerGl(data={'cities': df})
map_1.save_to_html(file_name='cities_map.html', center_map=True)
GeoDataFrame from shapefile
from keplergl import KeplerGl
import geopandas as gpd
gdf = gpd.read_file('shapefile.shp')
map_1 = KeplerGl(data={'regions': gdf})
map_1.save_to_html(file_name='regions_map.html', read_only=True, center_map=True)
Multiple datasets
map_1 = KeplerGl(data={
'locations': points_df,
'routes': routes_df
})
map_1.save_to_html(file_name='combined_map.html', center_map=True)
Save and reuse config
import json
# Save
with open('my_config.json', 'w') as f:
json.dump(map_1.config, f)
# Load
with open('my_config.json', 'r') as f:
config = json.load(f)
map_2 = KeplerGl(data={'data_1': df}, config=config)
map_2.save_to_html(file_name='map.html')
1---2name: kepler-gl3description: Create interactive map visualizations and export to standalone HTML using the keplergl Python package. Use when the user wants to create maps, visualize geospatial data, plot locations on a map, or generate HTML map files from DataFrames, GeoDataFrames, GeoJSON, or CSV data with coordinates.4---56# Create Maps with keplergl78Use the `keplergl` Python package to create standalone, interactive HTML map files from geospatial data. The exported HTML loads kepler.gl from CDN — no JavaScript build or server is needed. The resulting `.html` file can be opened directly in any browser.910## Installation1112```bash13pip install keplergl14```1516Requires `keplergl >= 0.4.0`. Earlier versions use a different widget/serialization API and the examples in this skill will not work. Requirements: Python >= 3.9. Dependencies (`pandas`, `geopandas`, `shapely`) are installed automatically.1718## Instructions19201. Import `KeplerGl` from `keplergl`212. Load data as a DataFrame, GeoDataFrame, GeoJSON dict, or CSV string223. Create a map with `KeplerGl(data={'name': data_object})`234. Optionally configure layers, colors, and map state via a `config` dict (default to quantile color scale and a vibrant palette for quantitative color encoding when the user does not specify)245. Export with `map.save_to_html(file_name='output.html', center_map=True)`256. The output HTML is fully standalone — open it in any browser2627## API Reference2829### `KeplerGl(data=None, config=None, height=400, mapbox_token="", use_arrow=False, show_docs=False, theme="", app_name="kepler.gl", **kwargs)`3031| Parameter | Type | Default | Description |32|-----------|------|---------|-------------|33| `height` | int | 400 | Map height in pixels |34| `data` | dict | None | `{"dataset_name": data_object}` |35| `config` | dict | None | Map configuration (layers, filters, map state) |36| `mapbox_token` | str | "" | Mapbox token (only for Mapbox basemap styles) |37| `use_arrow` | bool | False | Serialize DataFrames as Arrow IPC (more compact, preserves types) |38| `show_docs` | bool | False | Deprecated (kept for compatibility) |39| `theme` | str | "" | `"light"`, `"dark"`, `"base"`, or `""` (default dark) |40| `app_name` | str | "kepler.gl" | App name in header and HTML title |4142### `.add_data(data, name="data", use_arrow=None)`4344- `data`: DataFrame, GeoDataFrame, CSV string, GeoJSON dict, or GeoJSON string45- `name`: Dataset identifier (default: `"data"`) — must match `dataId` in config if using a config46- `use_arrow`: If `True`, serialize this DataFrame as Arrow IPC. If `None` (default), falls back to the widget-level `use_arrow` setting. Has no effect on GeoDataFrames.4748### `.save_to_html(file_name="keplergl_map.html", data=None, config=None, read_only=False, center_map=True, mapbox_token="", json_encoder=str, app_name=None, theme=None)`4950| Parameter | Type | Default | Description |51|-----------|------|---------|-------------|52| `file_name` | str | `"keplergl_map.html"` | Output file path |53| `data` | dict | None | Data override for export (uses current widget data when None) |54| `config` | dict | None | Config override for export (uses current widget config when None) |55| `read_only` | bool | False | True = hide side panel |56| `center_map` | bool | True | True = auto-fit map to data bounds |57| `mapbox_token` | str | "" | Mapbox token override for export |58| `json_encoder` | callable | str | Fallback encoder for non-JSON-native values in GeoDataFrames |59| `app_name` | str | None | App name override for export title/header |60| `theme` | str | None | Theme override for export (`"light"`, `"dark"`, `"base"`, or `""`) |6162### `.config`6364Read or set the map configuration dict. Use `map.config` after customizing in Jupyter UI, then save and reuse.6566## Key Rules6768- **`dataId` must match the dataset `name`** — every layer and filter references a dataset by `dataId`; this must match the key in the `data` dict or the `name` passed to `add_data()`.69- **GeoJSON columns use `_geojson`** — when data is loaded as GeoJSON, the geometry column is internally named `_geojson` in configs.70- **`colorField` / `colorScale` / `sizeField` / `heightField` etc. belong under `visualChannels`, NOT under `config`.** Putting them under `config` is silently ignored — the layer will render but the "Color Based On (field)" input shows empty. The layer object must have two siblings: `config` (for `dataId`, `columns`, `visConfig`, …) and `visualChannels` (for all field-to-channel mappings).71- Columns named `latitude`/`lat`/`lng`/`longitude` are auto-detected as coordinates.72- H3 hex IDs are auto-detected if a column contains valid H3 strings.73- Use `center_map=True` to auto-fit map bounds. Use `read_only=True` to hide the side panel.74- For numeric color encoding, if the user does not specify a color scale, use `visualChannels.colorScale: 'quantile'`.75- For numeric color encoding, if the user does not specify a palette, use a vibrant sequential/diverging palette (for example, `colorRange.name: 'Global Warming'`).76- If the user asks for custom class breaks, compute breakpoints in Python first (for example with `pygeoda`), add a derived classified/bin column to the dataset, and map colors using that derived field.77- **No `SampleMapPanel` in standalone exports.** The `SampleMapPanel` React component lives in the kepler.gl demo app, not in the UMD bundle used by `save_to_html()`. To show a summary/legend overlay, inject an HTML+CSS `<div>` into the exported file (position it at `right: 56px` or `left: 66px` so it doesn't block map controls). See [Summary Panel Overlay](skill-references/summary-panel.md).7879## Supported Data Formats8081| Format | How to Load |82|--------|-------------|83| pandas DataFrame | Columns with `lat`/`lng` (or similar) for point data |84| geopandas GeoDataFrame | Geometry column auto-detected. Interactive widget serialization uses GeoArrow (no CRS reprojection); HTML export path re-projects to EPSG:4326 when needed. |85| CSV string | Raw CSV text with lat/lng or geometry columns |86| GeoJSON dict | `Feature` or `FeatureCollection` as Python dict |87| GeoJSON string | JSON string of GeoJSON |88| WKT in DataFrame | DataFrame column containing WKT geometry strings |8990## Layer Types9192| Layer Type | Config `type` | Typical Data |93|------------|---------------|--------------|94| Point | `"point"` | DataFrame with lat/lng columns |95| Arc | `"arc"` | DataFrame with origin/destination lat/lng |96| Line | `"line"` | DataFrame with origin/destination lat/lng |97| Hexbin | `"hexagon"` | DataFrame with lat/lng (aggregated spatially) |98| Heatmap | `"heatmap"` | DataFrame with lat/lng |99| H3 Hexagon | `"hexagonId"` | DataFrame with H3 hex ID column |100| GeoJSON / Polygon | `"geojson"` | GeoJSON or GeoDataFrame with polygon/line geometries |101| Cluster | `"cluster"` | DataFrame with lat/lng |102| Icon | `"icon"` | DataFrame with lat/lng |103| Trip | `"trip"` | GeoJSON with LineString + timestamps |104| S2 | `"s2"` | DataFrame with S2 token column |105106## Config Structure107108```python109config = {110 'version': 'v1',111 'config': {112 'visState': {113 'layers': [...], # Layer definitions114 'filters': [...], # Data filters115 'interactionConfig': {}, # Tooltips, brush, geocoder116 'splitMaps': [], # Split map views117 'layerBlending': 'normal' # 'normal', 'additive', 'subtractive'118 },119 'mapState': {120 'latitude': 37.76,121 'longitude': -122.4,122 'zoom': 11,123 'bearing': 0,124 'pitch': 0,125 'dragRotate': False,126 'isSplit': False127 },128 'mapStyle': {129 'styleType': 'dark-matter'130 }131 }132}133```134135## Basemap Styles136137Free (no token needed): `dark-matter`, `positron`, `voyager`, `dark-matter-nolabels`, `positron-nolabels`, `voyager-nolabels`138139Mapbox (require `mapbox_token`): `dark`, `light`, `muted`, `muted_night`140141## Additional Resources142143For detailed per-layer-type examples with full config, see supporting files:144145- [Point Map](skill-references/point-map.md) — Scatter plot from lat/lng146- [GeoJSON / Polygon Map](skill-references/geojson-polygon-map.md) — Polygons, lines from GeoJSON or GeoDataFrame147- [H3 Hexagon Map](skill-references/h3-hexagon-map.md) — H3 spatial index hexagons148- [Arc / Line Map](skill-references/arc-line-map.md) — Origin-destination connections149- [Heatmap](skill-references/heatmap.md) — Density heatmap from points150- [Hexbin Aggregation Map](skill-references/hexbin-aggregation-map.md) — Spatial binning into hexagons151- [Trip Animation Map](skill-references/trip-animation-map.md) — Animated trips along paths152- [Summary Panel Overlay](skill-references/summary-panel.md) — Inject a SampleMapPanel-style info overlay into the exported HTML (for LISA/cluster counts, model summaries, custom legends)153154## Examples155156For full config examples per layer type, see:157- [Point Map](skill-references/point-map.md) — includes quantile color + vibrant palette config158- [GeoJSON / Polygon Map](skill-references/geojson-polygon-map.md) — includes choropleth config with `visualChannels`159160### Quick start (auto-detected layers, no config needed)161162```python163from keplergl import KeplerGl164import pandas as pd165166df = pd.DataFrame({167 'lat': [37.7749, 34.0522, 40.7128],168 'lng': [-122.4194, -118.2437, -74.0060],169 'name': ['San Francisco', 'Los Angeles', 'New York'],170 'value': [15, 42, 27]171})172173map_1 = KeplerGl(data={'cities': df})174map_1.save_to_html(file_name='cities_map.html', center_map=True)175```176177### GeoDataFrame from shapefile178179```python180from keplergl import KeplerGl181import geopandas as gpd182183gdf = gpd.read_file('shapefile.shp')184map_1 = KeplerGl(data={'regions': gdf})185map_1.save_to_html(file_name='regions_map.html', read_only=True, center_map=True)186```187188### Multiple datasets189190```python191map_1 = KeplerGl(data={192 'locations': points_df,193 'routes': routes_df194})195map_1.save_to_html(file_name='combined_map.html', center_map=True)196```197198### Save and reuse config199200```python201import json202# Save203with open('my_config.json', 'w') as f:204 json.dump(map_1.config, f)205# Load206with open('my_config.json', 'r') as f:207 config = json.load(f)208map_2 = KeplerGl(data={'data_1': df}, config=config)209map_2.save_to_html(file_name='map.html')210```