Skip to main content

keplergl-python

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.

Source facts

Repository
keplergl/kepler.gl
Last source activity
October 26, 2026 at 05:51
Detected SKILL.md language
English
Stars
12,080
Forks
1,962

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
9 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
keplergl-python
description
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.
# 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. ## Two surfaces kepler.gl reaches an AI assistant two ways, and this plugin ships a skill for each: | Surface | What it does | Skill | |---------|--------------|-------| | `keplergl` Python package | Builds a map from data and exports standalone HTML | this skill (`keplergl-python`) | | Live kepler.gl app over `map.*` commands | Mutates a map that is already open in the app | `kepler-gl-app` | Use this skill when the deliverable is an HTML file or a Python script. Use `kepler-gl-app` when the user is looking at a running map and asks you to change it. The two are not interchangeable — the same layer can need a different type string on each surface (see [Layer Types](#layer-types)). ## Installation ```bash 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 1. Import `KeplerGl` from `keplergl` 2. Load data as a DataFrame, GeoDataFrame, GeoJSON dict, or CSV string 3. Create a map with `KeplerGl(data={'name': data_object})` 4. 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) 5. Export with `map.save_to_html(file_name='output.html', center_map=True)` 6. 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](references/summary-panel.md). ## Color Encoding Numeric fields are colored with `visualChannels.colorScale` (`quantile` when the user does not ask for a scale) and a `visConfig.colorRange`. Categorical fields use `colorScale: 'ordinal'`. <!-- shared:palettes --> Use these palettes when the user does not specify colors. The hex values work unchanged on both surfaces. - Sequential green: `#ffffcc #c2e699 #78c679 #31a354 #006837 #004529` - Sequential red: `#fee5d9 #fcae91 #fb6a4a #de2d26 #a50f15` - Categorical (Tableau 10): `#1f77b4 #ff7f0e #2ca02c #d62728 #9467bd #8c564b` - Vibrant (the built-in `Global Warming` range): `#5A1846 #900C3F #C70039 #E3611C #F1920E #FFC300` For continuous data use a sequential or diverging palette; for categorical data use distinct hues. Do not reuse one palette for two encodings on the same map. <!-- /shared:palettes --> `colorRange` is an object (`name`, `type`, `category`, `colors`), not a list of hex strings — see [Point Map](references/point-map.md) for a complete example. ## 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 <!-- shared:layer-types --> **Layer types differ between the two surfaces.** A `keplergl` Python `config` names the layer in `type`; an MCP `map.add-layer` call names it in `layerType`. The same spatial concept often uses a different string, and the H3/hexbin pair is the classic trap: | Concept | Python config `type` | MCP `layerType` | |---------|----------------------|-----------------| | Point | `point` | `point` | | Cluster | `cluster` | `cluster` | | Heatmap | `heatmap` | `heatmap` | | Hexbin (binned from points) | `hexagon` | `hexagon` | | H3 hexagon (precomputed H3 ids) | `hexagonId` | `h3` | | Grid (binned from points) | — | `grid` | | S2 | `s2` | `s2` | | Arc (great-circle origin→destination) | `arc` | `arc` | | Flow (weighted origin→destination) | — | `flow` | | Flow field (streamlines over a vector field) | — | `flowField` | | Line (polylines) | `line` | `line` | | GeoJSON / polygon | `geojson` | `geojson` | | Trip (path + timestamp animation) | `trip` | `trip` | | Icon | `icon` | — | - **`hexagonId` is not `hexagon`.** In a Python config, `hexagonId` is H3 and `hexagon` is hexbin. Over MCP, H3 is `h3` and `hexagon` is still hexbin. Writing `hexagon` for H3 ids gives an empty or wrong layer on both surfaces. - `—` means the surface has no layer type for that concept; do not substitute another one silently. - MCP layer types are validated against an enum, so an unknown string is rejected rather than silently guessed. - **`flow` is not `flowField`.** `flow` joins an origin point pair to a destination point pair; `flowField` draws animated streamlines through one vector per sample point (wind, currents, downhill terrain). <!-- /shared:layer-types --> In a Python config, `columns` carries the field names each layer type needs (`hex_id` for `hexagonId`, `geojson: '_geojson'` for `geojson`, …) and `visualChannels` carries the field-to-channel mappings (`colorField`, `sizeField`, `heightField`, …). The per-layer references below show both. ## Time animation and trips <!-- shared:trip --> Trip layers animate themselves. A trip needs an `id` shared across its waypoints, `latitude` / `longitude`, and a `timestamp`. - **Never attach a separate time filter to a trip layer** — `map.add-time-filter` over MCP, or a `timeRange` filter in a Python config. The trip animation and the filter fight over the same clock. - MCP `map.add-layer` expects epoch **milliseconds** in `timestamp`. A Python trip GeoJSON carries the timestamp as the fourth coordinate value, `[lng, lat, altitude, timestamp]` (epoch seconds or milliseconds); convert the same data to milliseconds before handing it to MCP `map.add-layer`. - Densify sparse origin→destination data — interpolate longitude, latitude, and timestamp together — or the trips teleport between waypoints. <!-- /shared:trip --> See [Trip Animation Map](references/trip-animation-map.md) for a full config. ## Config Structure ```python 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 <!-- shared:basemaps --> Basemap ids are the same on both surfaces, but each surface takes them differently. Python sets `config.config.mapStyle.styleType`; MCP calls `map.set-basemap` with `{"styleType": ...}`. | `styleType` | Needs a Mapbox token | |-------------|:--------------------:| | `dark-matter` | no | | `positron` | no | | `voyager` | no | | `no_map` | no | | `satellite` | yes | | `dark` | yes | | `light` | yes | | `muted` | yes | | `muted_night` | yes | - Without a Mapbox token only `dark-matter`, `positron`, `voyager`, and `no_map` render. - `map.set-basemap` rejects any id outside this table (its schema is an enum). A Python `mapStyle.styleType` is passed straight to kepler.gl, so it also accepts custom styles declared in `mapStyle.mapStyles`. - The `dark-matter-nolabels`, `positron-nolabels`, and `voyager-nolabels` ids no longer exist in kepler.gl; hide basemap labels with `mapStyle.visibleLayerGroups.label` instead. - If a requested style name is ambiguous, ask the user rather than guessing. <!-- /shared:basemaps --> For the Mapbox styles, pass `mapbox_token=` to `KeplerGl(...)` or to `save_to_html(...)`; the token is embedded in the exported HTML. ## Additional Resources For detailed per-layer-type examples with full config, see supporting files:
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub