From 053841249c229894eced1125b7987dfde31864e6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 17:32:49 +0000 Subject: [PATCH 01/17] Add SKILL.md and map type reference files for keplergl-jupyter Agent-Logs-Url: https://github.com/keplergl/kepler.gl/sessions/b75ca334-0504-44a7-8f53-3418eebc3f65 Co-authored-by: lixun910 <2423887+lixun910@users.noreply.github.com> --- bindings/python/SKILL.md | 254 ++++++++++++++++++ .../python/skill-references/arc-line-map.md | 108 ++++++++ .../skill-references/geojson-polygon-map.md | 150 +++++++++++ .../python/skill-references/h3-hexagon-map.md | 113 ++++++++ bindings/python/skill-references/heatmap.md | 93 +++++++ .../hexbin-aggregation-map.md | 107 ++++++++ bindings/python/skill-references/point-map.md | 102 +++++++ .../skill-references/trip-animation-map.md | 130 +++++++++ 8 files changed, 1057 insertions(+) create mode 100644 bindings/python/SKILL.md create mode 100644 bindings/python/skill-references/arc-line-map.md create mode 100644 bindings/python/skill-references/geojson-polygon-map.md create mode 100644 bindings/python/skill-references/h3-hexagon-map.md create mode 100644 bindings/python/skill-references/heatmap.md create mode 100644 bindings/python/skill-references/hexbin-aggregation-map.md create mode 100644 bindings/python/skill-references/point-map.md create mode 100644 bindings/python/skill-references/trip-animation-map.md diff --git a/bindings/python/SKILL.md b/bindings/python/SKILL.md new file mode 100644 index 0000000000..9745a7d754 --- /dev/null +++ b/bindings/python/SKILL.md @@ -0,0 +1,254 @@ +# Skill: Create Static HTML Maps with keplergl-Jupyter + +## Purpose + +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 + +```bash +pip install keplergl +``` + +**Requirements:** Python >= 3.9 + +**Optional dependencies** (installed automatically with keplergl): +- `pandas` — for DataFrames +- `geopandas` — for GeoDataFrames and reading geospatial files +- `shapely` — for geometry operations (comes with geopandas) + +## Quick Start — Minimal Static Map + +```python +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'] +}) + +map_1 = KeplerGl(height=600, data={'cities': df}) +map_1.save_to_html(file_name='cities_map.html') +``` + +This creates `cities_map.html` — a fully self-contained interactive map. + +## Core API + +### `KeplerGl()` — Create a Map Widget + +```python +KeplerGl( + height=400, # Map height in pixels + data=None, # Dict of {"dataset_name": data_object} + config=None, # Map configuration dict (layers, filters, map state) + mapbox_token="", # Mapbox token (needed only for Mapbox basemap styles) + theme="", # "light", "dark", "base", or "" (default dark) + app_name="kepler.gl", # App name shown in header and HTML +) +``` + +### `.add_data()` — Add a Dataset + +```python +map_1.add_data(data=df, name='my_dataset') +``` + +- `data`: DataFrame, GeoDataFrame, CSV string, GeoJSON dict, or GeoJSON string +- `name`: Dataset identifier — must match `dataId` in config if using a config + +### `.save_to_html()` — Export to Static HTML + +```python +map_1.save_to_html( + file_name='keplergl_map.html', # Output file path + read_only=False, # True = hide side panel + center_map=True, # True = auto-fit map to data bounds + mapbox_token="", # Mapbox token for Mapbox styles + config=None, # Override config (uses current if None) + data=None, # Override data (uses current if None) + theme=None, # Override theme + app_name=None, # Override app name +) +``` + +### `.config` — Get/Set Map Configuration + +```python +# Read current config (after interacting with map in Jupyter) +current_config = map_1.config + +# Apply a config +map_1.config = my_config_dict +``` + +## Supported Data Formats + +| Format | How to Load | +|--------|-------------| +| **pandas DataFrame** | Columns with `latitude`/`longitude` (or similar) for point data | +| **geopandas GeoDataFrame** | Geometry column auto-detected; re-projected to EPSG:4326 | +| **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 and Config + +kepler.gl automatically detects data types and creates layers. You can also provide a `config` dict to control layer types, colors, and visual settings. The most commonly used layer types are: + +| Layer Type | Config `type` Value | 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 (aggregation) | `"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 | + +## Map Type Reference Files + +For detailed examples of creating specific map types with full config, see: + +- [Point Map](skill-references/point-map.md) — Scatter plot of locations from lat/lng +- [GeoJSON / Polygon Map](skill-references/geojson-polygon-map.md) — Polygons, lines from GeoJSON or GeoDataFrame +- [H3 Hexagon Map](skill-references/h3-hexagon-map.md) — H3 spatial index hexagons +- [Arc / Line Map](skill-references/arc-line-map.md) — Origin-destination connections +- [Heatmap](skill-references/heatmap.md) — Density heatmap from points +- [Hexbin Aggregation Map](skill-references/hexbin-aggregation-map.md) — Spatial binning into hexagons +- [Trip Animation Map](skill-references/trip-animation-map.md) — Animated trips along paths + +## Config Structure + +A config dict has this 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' # 'dark-matter', 'positron', 'dark-matter-nolabels', 'positron-nolabels', 'voyager', 'voyager-nolabels' + } + } +} +``` + +### Key Rules for Config + +1. **`dataId` must match the dataset `name`** — Every layer and filter references a dataset by `dataId`. This must match the `name` passed to `add_data()` or the key in the `data` dict. +2. **GeoJSON columns use `_geojson`** — When data is loaded as GeoJSON, the geometry column is internally named `_geojson` in configs. +3. **Save and reuse configs** — Call `map_1.config` after customizing in the UI, then save and reuse: + +```python +# Save config to file +import json +with open('my_config.json', 'w') as f: + json.dump(map_1.config, f) + +# Load config from file +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') +``` + +## Common Patterns + +### Load GeoJSON from file and export + +```python +from keplergl import KeplerGl +import json + +with open('boundaries.geojson', 'r') as f: + geojson = json.load(f) + +map_1 = KeplerGl(data={'boundaries': geojson}) +map_1.save_to_html(file_name='boundaries_map.html', center_map=True) +``` + +### Load GeoDataFrame and export with read-only mode + +```python +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 on one map + +```python +from keplergl import KeplerGl +import pandas as pd + +points_df = pd.read_csv('locations.csv') +routes_df = pd.read_csv('routes.csv') + +map_1 = KeplerGl(data={ + 'locations': points_df, + 'routes': routes_df +}) +map_1.save_to_html(file_name='combined_map.html', center_map=True) +``` + +### Light theme with custom app name + +```python +map_1 = KeplerGl( + data={'data': df}, + theme='light', + app_name='My Dashboard' +) +map_1.save_to_html(file_name='dashboard.html') +``` + +## Basemap Styles + +Free basemap styles (no token needed): +- `dark-matter` — Dark background (default) +- `positron` — Light background +- `voyager` — Colorful with labels +- `dark-matter-nolabels`, `positron-nolabels`, `voyager-nolabels` — Without labels + +Mapbox styles (require `mapbox_token`): +- `dark` — Mapbox Dark +- `light` — Mapbox Light +- `muted` — Mapbox Muted Light +- `muted_night` — Mapbox Muted Night + +## Tips + +- Use `center_map=True` in `save_to_html()` to auto-fit the map bounds to your data. +- Use `read_only=True` to hide the side panel in exported HTML for clean presentations. +- kepler.gl auto-detects column types: columns named `latitude`/`lat`/`lng`/`longitude` are recognized as coordinates. +- H3 hex IDs are auto-detected if a column contains valid H3 strings. +- For large datasets, use `use_arrow=True` when creating the widget for faster serialization. +- The exported HTML is fully standalone — it loads all dependencies from CDN. diff --git a/bindings/python/skill-references/arc-line-map.md b/bindings/python/skill-references/arc-line-map.md new file mode 100644 index 0000000000..c9a17b2be1 --- /dev/null +++ b/bindings/python/skill-references/arc-line-map.md @@ -0,0 +1,108 @@ +# Arc / Line Map + +Show connections between origin and destination points. Arc layers render 3D arcs; line layers render flat lines. + +## Data + +A pandas DataFrame with origin and destination latitude/longitude columns. + +```python +import pandas as pd + +df = pd.DataFrame({ + 'origin_lat': [37.7749, 40.7128, 41.8781], + 'origin_lng': [-122.4194, -74.0060, -87.6298], + 'dest_lat': [34.0522, 37.7749, 40.7128], + 'dest_lng': [-118.2437, -122.4194, -74.0060], + 'origin_city': ['San Francisco', 'New York', 'Chicago'], + 'dest_city': ['Los Angeles', 'San Francisco', 'New York'], + 'flights': [150, 200, 180] +}) +``` + +## Minimal Export + +```python +from keplergl import KeplerGl + +map_1 = KeplerGl(data={'routes': df}) +map_1.save_to_html(file_name='arc_map.html', center_map=True) +``` + +## Arc Layer with Config + +```python +config = { + 'version': 'v1', + 'config': { + 'visState': { + 'layers': [{ + 'type': 'arc', + 'config': { + 'dataId': 'routes', + 'label': 'Flight Routes', + 'isVisible': True, + 'columns': { + 'lat0': 'origin_lat', + 'lng0': 'origin_lng', + 'lat1': 'dest_lat', + 'lng1': 'dest_lng' + }, + 'visConfig': { + 'opacity': 0.8, + 'thickness': 2, + 'targetColor': [255, 203, 153] + }, + 'color': [18, 147, 154] + }, + 'visualChannels': { + 'sizeField': {'name': 'flights', 'type': 'integer'}, + 'sizeScale': 'linear' + } + }], + 'interactionConfig': { + 'tooltip': { + 'enabled': True, + 'fieldsToShow': { + 'routes': ['origin_city', 'dest_city', 'flights'] + } + } + } + }, + 'mapState': { + 'latitude': 38.0, + 'longitude': -97.0, + 'zoom': 4, + 'pitch': 30, + 'bearing': 0, + 'dragRotate': True + }, + 'mapStyle': { + 'styleType': 'dark-matter' + } + } +} + +map_1 = KeplerGl(data={'routes': df}, config=config) +map_1.save_to_html(file_name='arc_map_styled.html') +``` + +## Line Layer (Flat) + +Replace `'type': 'arc'` with `'type': 'line'` in the config. The columns are the same. + +```python +config['config']['visState']['layers'][0]['type'] = 'line' +``` + +## Key Config Fields + +| Field | Description | +|-------|-------------| +| `type` | `'arc'` for 3D arcs, `'line'` for flat lines | +| `columns.lat0` / `columns.lng0` | Origin latitude/longitude column | +| `columns.lat1` / `columns.lng1` | Destination latitude/longitude column | +| `visConfig.thickness` | Line width | +| `visConfig.targetColor` | RGB array for destination end color | +| `color` | RGB array for origin end color | +| `visualChannels.sizeField` | Column to map to line thickness | diff --git a/bindings/python/skill-references/geojson-polygon-map.md b/bindings/python/skill-references/geojson-polygon-map.md new file mode 100644 index 0000000000..9a820cf7a4 --- /dev/null +++ b/bindings/python/skill-references/geojson-polygon-map.md @@ -0,0 +1,150 @@ +# GeoJSON / Polygon Map + +Display polygon, line, or point geometries from GeoJSON or GeoDataFrame data. + +## Data Options + +### Option 1: GeoJSON dict or string + +```python +import json + +with open('boundaries.geojson', 'r') as f: + geojson = json.load(f) + +# geojson can be a FeatureCollection or a single Feature +``` + +### Option 2: GeoDataFrame + +```python +import geopandas as gpd + +gdf = gpd.read_file('shapefile.shp') +# or from a GeoJSON file: +gdf = gpd.read_file('boundaries.geojson') +``` + +### Option 3: Inline GeoJSON + +```python +geojson = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {"name": "Area A", "value": 100}, + "geometry": { + "type": "Polygon", + "coordinates": [[ + [-122.4, 37.8], [-122.4, 37.7], + [-122.3, 37.7], [-122.3, 37.8], + [-122.4, 37.8] + ]] + } + } + ] +} +``` + +## Minimal Export + +```python +from keplergl import KeplerGl + +# From GeoJSON +map_1 = KeplerGl(data={'zones': geojson}) +map_1.save_to_html(file_name='polygon_map.html', center_map=True) + +# From GeoDataFrame +map_1 = KeplerGl(data={'zones': gdf}) +map_1.save_to_html(file_name='polygon_map.html', center_map=True) +``` + +## Export with Config + +**Important:** When data is loaded as GeoJSON, the geometry column in config must be `_geojson`. + +```python +config = { + 'version': 'v1', + 'config': { + 'visState': { + 'layers': [{ + 'type': 'geojson', + 'config': { + 'dataId': 'zones', + 'label': 'Zone Boundaries', + 'isVisible': True, + 'columns': { + 'geojson': '_geojson' + }, + 'visConfig': { + 'opacity': 0.8, + 'filled': True, + 'stroked': True, + 'thickness': 1.5, + 'strokeColor': [255, 255, 255], + 'colorRange': { + 'name': 'Sunrise 8', + 'type': 'sequential', + 'category': 'Uber', + 'colors': [ + '#194266', '#355C7D', '#63617F', '#916681', + '#C06C84', '#D28389', '#E59A8F', '#F8B195' + ] + }, + 'enable3d': False, + 'elevationScale': 5 + } + }, + 'visualChannels': { + 'colorField': {'name': 'value', 'type': 'integer'}, + 'colorScale': 'quantize' + } + }], + 'interactionConfig': { + 'tooltip': { + 'enabled': True, + 'fieldsToShow': { + 'zones': ['name', 'value'] + } + } + } + }, + 'mapStyle': { + 'styleType': 'positron' + } + } +} + +map_1 = KeplerGl(data={'zones': geojson}, config=config, theme='light') +map_1.save_to_html(file_name='polygon_map_styled.html') +``` + +## 3D Extruded Polygons + +Set `enable3d: True` and map a `heightField` to extrude polygons: + +```python +config['config']['visState']['layers'][0]['config']['visConfig']['enable3d'] = True +config['config']['visState']['layers'][0]['config']['visConfig']['elevationScale'] = 10 +config['config']['visState']['layers'][0]['visualChannels']['heightField'] = { + 'name': 'value', 'type': 'integer' +} +config['config']['visState']['layers'][0]['visualChannels']['heightScale'] = 'linear' +``` + +## Key Config Fields for GeoJSON Layer + +| Field | Description | +|-------|-------------| +| `columns.geojson` | Must be `'_geojson'` for GeoJSON data | +| `visConfig.filled` | Fill polygons with color | +| `visConfig.stroked` | Show polygon outlines | +| `visConfig.thickness` | Outline stroke width | +| `visConfig.strokeColor` | RGB array for outline color | +| `visConfig.enable3d` | Extrude polygons in 3D | +| `visConfig.elevationScale` | Height multiplier for 3D | +| `visualChannels.colorField` | Property to map to fill color | +| `visualChannels.heightField` | Property to map to extrusion height | diff --git a/bindings/python/skill-references/h3-hexagon-map.md b/bindings/python/skill-references/h3-hexagon-map.md new file mode 100644 index 0000000000..1222007bbc --- /dev/null +++ b/bindings/python/skill-references/h3-hexagon-map.md @@ -0,0 +1,113 @@ +# H3 Hexagon Map + +Visualize data using [H3 spatial index](https://h3geo.org/) hexagons. Each row has an H3 hex ID and associated values. + +## Data + +A pandas DataFrame with a column containing H3 hex ID strings and value columns. + +```python +import pandas as pd + +df = pd.DataFrame({ + 'hex_id': [ + '89283082c2fffff', '8928308288fffff', '89283082c07ffff', + '89283082817ffff', '89283082c3bffff', '89283082883ffff' + ], + 'value': [64, 73, 65, 74, 66, 50] +}) +``` + +## Minimal Export + +```python +from keplergl import KeplerGl + +map_1 = KeplerGl(data={'hex_data': df}) +map_1.save_to_html(file_name='h3_map.html', center_map=True) +``` + +kepler.gl auto-detects H3 hex ID columns and creates a hexagonId layer. + +## Export with Config (Colored + 3D Extruded) + +```python +config = { + 'version': 'v1', + 'config': { + 'visState': { + 'layers': [{ + 'type': 'hexagonId', + 'config': { + 'dataId': 'hex_data', + 'label': 'H3 Hexagons', + 'isVisible': True, + 'columns': { + 'hex_id': 'hex_id' + }, + 'visConfig': { + 'opacity': 0.8, + 'coverage': 1, + 'enable3d': True, + 'sizeRange': [0, 500], + 'elevationScale': 5, + 'colorRange': { + 'name': 'Sunrise 8', + 'type': 'sequential', + 'category': 'Uber', + 'colors': [ + '#194266', '#355C7D', '#63617F', '#916681', + '#C06C84', '#D28389', '#E59A8F', '#F8B195' + ], + 'reversed': False + } + } + }, + 'visualChannels': { + 'colorField': {'name': 'value', 'type': 'integer'}, + 'colorScale': 'quantize', + 'sizeField': {'name': 'value', 'type': 'integer'}, + 'sizeScale': 'linear', + 'coverageField': None, + 'coverageScale': 'linear' + } + }], + 'interactionConfig': { + 'tooltip': { + 'enabled': True, + 'fieldsToShow': { + 'hex_data': ['hex_id', 'value'] + } + } + } + }, + 'mapState': { + 'latitude': 37.76, + 'longitude': -122.43, + 'zoom': 12, + 'bearing': 2.6, + 'pitch': 37.4, + 'dragRotate': True + }, + 'mapStyle': { + 'styleType': 'dark-matter' + } + } +} + +map_1 = KeplerGl(data={'hex_data': df}, config=config) +map_1.save_to_html(file_name='h3_map_3d.html') +``` + +## Key Config Fields for H3 Layer + +| Field | Description | +|-------|-------------| +| `type` | Must be `'hexagonId'` | +| `columns.hex_id` | Column name containing H3 hex ID strings | +| `visConfig.coverage` | 0 to 1, how much of hex cell is filled | +| `visConfig.enable3d` | Extrude hexagons by value | +| `visConfig.sizeRange` | `[min, max]` height range for 3D | +| `visConfig.elevationScale` | Height multiplier | +| `visualChannels.colorField` | Column to map to color | +| `visualChannels.sizeField` | Column to map to extrusion height | diff --git a/bindings/python/skill-references/heatmap.md b/bindings/python/skill-references/heatmap.md new file mode 100644 index 0000000000..2729cd8ca0 --- /dev/null +++ b/bindings/python/skill-references/heatmap.md @@ -0,0 +1,93 @@ +# Heatmap + +Density heatmap visualization from point data. Points are aggregated into a continuous density surface. + +## Data + +A pandas DataFrame with latitude and longitude columns. Optionally a weight column. + +```python +import pandas as pd +import numpy as np + +np.random.seed(42) +n = 1000 +df = pd.DataFrame({ + 'lat': np.random.normal(37.76, 0.02, n), + 'lng': np.random.normal(-122.42, 0.02, n), + 'magnitude': np.random.uniform(1, 10, n) +}) +``` + +## Minimal Export + +```python +from keplergl import KeplerGl + +map_1 = KeplerGl(data={'events': df}) +map_1.save_to_html(file_name='heatmap.html', center_map=True) +``` + +Note: kepler.gl will auto-detect lat/lng columns but may create a point layer by default. Use a config to force a heatmap layer. + +## Export with Heatmap Config + +```python +config = { + 'version': 'v1', + 'config': { + 'visState': { + 'layers': [{ + 'type': 'heatmap', + 'config': { + 'dataId': 'events', + 'label': 'Event Density', + 'isVisible': True, + 'columns': { + 'lat': 'lat', + 'lng': 'lng' + }, + 'visConfig': { + 'opacity': 0.8, + 'radius': 20, + 'intensity': 1, + 'colorRange': { + 'name': 'Global Warming', + 'type': 'sequential', + 'category': 'Uber', + 'colors': ['#5A1846', '#900C3F', '#C70039', '#E3611C', '#F1920E', '#FFC300'] + } + } + }, + 'visualChannels': { + 'weightField': {'name': 'magnitude', 'type': 'real'}, + 'weightScale': 'linear' + } + }] + }, + 'mapState': { + 'latitude': 37.76, + 'longitude': -122.42, + 'zoom': 13 + }, + 'mapStyle': { + 'styleType': 'dark-matter' + } + } +} + +map_1 = KeplerGl(data={'events': df}, config=config) +map_1.save_to_html(file_name='heatmap_styled.html') +``` + +## Key Config Fields for Heatmap Layer + +| Field | Description | +|-------|-------------| +| `type` | Must be `'heatmap'` | +| `columns.lat` | Latitude column name | +| `columns.lng` | Longitude column name | +| `visConfig.radius` | Influence radius of each point | +| `visConfig.intensity` | Heat intensity multiplier | +| `visConfig.opacity` | 0.0 to 1.0 | +| `visualChannels.weightField` | Column to use as weight (optional) | diff --git a/bindings/python/skill-references/hexbin-aggregation-map.md b/bindings/python/skill-references/hexbin-aggregation-map.md new file mode 100644 index 0000000000..6dce62262b --- /dev/null +++ b/bindings/python/skill-references/hexbin-aggregation-map.md @@ -0,0 +1,107 @@ +# Hexbin Aggregation Map + +Aggregate point data into hexagonal bins. Unlike H3 maps (which use pre-computed H3 IDs), hexbin layers take raw lat/lng points and aggregate them spatially. + +## Data + +A pandas DataFrame with latitude and longitude columns. + +```python +import pandas as pd +import numpy as np + +np.random.seed(42) +n = 5000 +df = pd.DataFrame({ + 'lat': np.random.normal(37.76, 0.05, n), + 'lng': np.random.normal(-122.42, 0.05, n), + 'value': np.random.randint(1, 100, n) +}) +``` + +## Minimal Export + +```python +from keplergl import KeplerGl + +map_1 = KeplerGl(data={'points': df}) +map_1.save_to_html(file_name='hexbin_map.html', center_map=True) +``` + +Note: kepler.gl may create a point layer by default. Use a config to specify a hexagon aggregation layer. + +## Export with Config + +```python +config = { + 'version': 'v1', + 'config': { + 'visState': { + 'layers': [{ + 'type': 'hexagon', + 'config': { + 'dataId': 'points', + 'label': 'Hexbin Density', + 'isVisible': True, + 'columns': { + 'lat': 'lat', + 'lng': 'lng' + }, + 'visConfig': { + 'opacity': 0.8, + 'worldUnitSize': 0.5, + 'resolution': 8, + 'coverage': 1, + 'enable3d': True, + 'sizeRange': [0, 500], + 'elevationScale': 5, + 'colorRange': { + 'name': 'Global Warming', + 'type': 'sequential', + 'category': 'Uber', + 'colors': ['#5A1846', '#900C3F', '#C70039', '#E3611C', '#F1920E', '#FFC300'] + }, + 'colorAggregation': 'average' + } + }, + 'visualChannels': { + 'colorField': {'name': 'value', 'type': 'integer'}, + 'colorScale': 'quantile', + 'sizeField': {'name': 'value', 'type': 'integer'}, + 'sizeScale': 'linear' + } + }] + }, + 'mapState': { + 'latitude': 37.76, + 'longitude': -122.42, + 'zoom': 12, + 'pitch': 40, + 'bearing': 0, + 'dragRotate': True + }, + 'mapStyle': { + 'styleType': 'dark-matter' + } + } +} + +map_1 = KeplerGl(data={'points': df}, config=config) +map_1.save_to_html(file_name='hexbin_map_3d.html') +``` + +## Key Config Fields for Hexbin Layer + +| Field | Description | +|-------|-------------| +| `type` | Must be `'hexagon'` | +| `columns.lat` | Latitude column name | +| `columns.lng` | Longitude column name | +| `visConfig.worldUnitSize` | Hexagon bin radius in km | +| `visConfig.coverage` | 0 to 1, fill ratio of each hex | +| `visConfig.enable3d` | Extrude hexagons by aggregated value | +| `visConfig.sizeRange` | `[min, max]` height range for 3D | +| `visConfig.elevationScale` | Height multiplier | +| `visConfig.colorAggregation` | `'average'`, `'sum'`, `'min'`, `'max'`, `'count'` | +| `visualChannels.colorField` | Column to aggregate for color | +| `visualChannels.sizeField` | Column to aggregate for height | diff --git a/bindings/python/skill-references/point-map.md b/bindings/python/skill-references/point-map.md new file mode 100644 index 0000000000..13e2c4746e --- /dev/null +++ b/bindings/python/skill-references/point-map.md @@ -0,0 +1,102 @@ +# Point Map + +Scatter plot of locations using latitude/longitude columns from a DataFrame. + +## Data + +A pandas DataFrame with numeric latitude and longitude columns. + +```python +import pandas as pd + +df = pd.DataFrame({ + 'name': ['San Francisco', 'Los Angeles', 'New York', 'Chicago', 'Houston'], + 'lat': [37.7749, 34.0522, 40.7128, 41.8781, 29.7604], + 'lng': [-122.4194, -118.2437, -74.0060, -87.6298, -95.3698], + 'population': [884363, 3979576, 8336817, 2693976, 2320268], + 'category': ['West', 'West', 'East', 'Midwest', 'South'] +}) +``` + +## Minimal Export (auto-detected layers) + +```python +from keplergl import KeplerGl + +map_1 = KeplerGl(data={'cities': df}) +map_1.save_to_html(file_name='point_map.html', center_map=True) +``` + +## Export with Config + +```python +config = { + 'version': 'v1', + 'config': { + 'visState': { + 'layers': [{ + 'type': 'point', + 'config': { + 'dataId': 'cities', + 'label': 'City Locations', + 'isVisible': True, + 'columns': { + 'lat': 'lat', + 'lng': 'lng', + 'altitude': None + }, + 'visConfig': { + 'radius': 20, + 'opacity': 0.8, + 'filled': True, + 'colorRange': { + 'name': 'Global Warming', + 'type': 'sequential', + 'category': 'Uber', + 'colors': ['#5A1846', '#900C3F', '#C70039', '#E3611C', '#F1920E', '#FFC300'] + } + } + }, + 'visualChannels': { + 'colorField': {'name': 'population', 'type': 'integer'}, + 'colorScale': 'quantile', + 'sizeField': {'name': 'population', 'type': 'integer'}, + 'sizeScale': 'sqrt' + } + }], + 'interactionConfig': { + 'tooltip': { + 'enabled': True, + 'fieldsToShow': { + 'cities': ['name', 'population', 'category'] + } + } + } + }, + 'mapState': { + 'latitude': 38.0, + 'longitude': -97.0, + 'zoom': 4 + }, + 'mapStyle': { + 'styleType': 'dark-matter' + } + } +} + +map_1 = KeplerGl(data={'cities': df}, config=config) +map_1.save_to_html(file_name='point_map_styled.html') +``` + +## Key Config Fields for Point Layer + +| Field | Description | +|-------|-------------| +| `columns.lat` | Column name for latitude | +| `columns.lng` | Column name for longitude | +| `visConfig.radius` | Point radius in pixels | +| `visConfig.opacity` | 0.0 to 1.0 | +| `visConfig.filled` | Whether points are filled | +| `visualChannels.colorField` | Column to map to color | +| `visualChannels.sizeField` | Column to map to point size | +| `visualChannels.colorScale` | `'quantile'`, `'quantize'`, `'ordinal'` | diff --git a/bindings/python/skill-references/trip-animation-map.md b/bindings/python/skill-references/trip-animation-map.md new file mode 100644 index 0000000000..385be81866 --- /dev/null +++ b/bindings/python/skill-references/trip-animation-map.md @@ -0,0 +1,130 @@ +# Trip Animation Map + +Animate movement along paths over time. Trip layers render GeoJSON LineString geometries with timestamps to create animated visualizations. + +## Data + +Trip data must be GeoJSON with `LineString` or `MultiLineString` geometry where each coordinate has a timestamp. The timestamp is provided as a fourth value in each coordinate `[lng, lat, altitude, timestamp]` or via a properties field. + +```python +trip_geojson = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "vendor": "A", + "trip_id": 1 + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [-122.4194, 37.7749, 0, 1564184363], + [-122.4180, 37.7760, 0, 1564184400], + [-122.4160, 37.7770, 0, 1564184440], + [-122.4140, 37.7780, 0, 1564184480], + [-122.4120, 37.7790, 0, 1564184520] + ] + } + }, + { + "type": "Feature", + "properties": { + "vendor": "B", + "trip_id": 2 + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [-122.4100, 37.7800, 0, 1564184363], + [-122.4120, 37.7810, 0, 1564184400], + [-122.4140, 37.7820, 0, 1564184440], + [-122.4160, 37.7830, 0, 1564184480] + ] + } + } + ] +} +``` + +Timestamps are Unix epoch seconds (or milliseconds). The coordinates format is `[longitude, latitude, altitude, timestamp]`. + +## Minimal Export + +```python +from keplergl import KeplerGl + +map_1 = KeplerGl(data={'trips': trip_geojson}) +map_1.save_to_html(file_name='trip_map.html', center_map=True) +``` + +## Export with Config + +```python +config = { + 'version': 'v1', + 'config': { + 'visState': { + 'layers': [{ + 'type': 'trip', + 'config': { + 'dataId': 'trips', + 'label': 'Vehicle Trips', + 'isVisible': True, + 'columns': { + 'geojson': '_geojson' + }, + 'color': [18, 147, 154], + 'visConfig': { + 'opacity': 0.8, + 'thickness': 3, + 'trailLength': 180, + 'colorRange': { + 'name': 'Global Warming', + 'type': 'sequential', + 'category': 'Uber', + 'colors': ['#5A1846', '#900C3F', '#C70039', '#E3611C', '#F1920E', '#FFC300'] + } + } + }, + 'visualChannels': { + 'colorField': {'name': 'vendor', 'type': 'string'}, + 'colorScale': 'ordinal' + } + }], + 'animationConfig': { + 'currentTime': None, + 'speed': 1 + } + }, + 'mapState': { + 'latitude': 37.78, + 'longitude': -122.42, + 'zoom': 13 + }, + 'mapStyle': { + 'styleType': 'dark-matter' + } + } +} + +map_1 = KeplerGl(data={'trips': trip_geojson}, config=config) +map_1.save_to_html(file_name='trip_map_styled.html') +``` + +## Key Config Fields for Trip Layer + +| Field | Description | +|-------|-------------| +| `type` | Must be `'trip'` | +| `columns.geojson` | Must be `'_geojson'` for GeoJSON data | +| `visConfig.trailLength` | Trail length in seconds | +| `visConfig.thickness` | Line width | +| `animationConfig.speed` | Playback speed multiplier | +| `visualChannels.colorField` | Property to map to color | + +## Notes + +- The animation will auto-play when the HTML is opened in a browser. +- `trailLength` controls how many seconds of trail are visible behind the moving point. +- Coordinates must include timestamps as the 4th value: `[lng, lat, alt, timestamp]`. From 84804a44f3914c417414901db78b591c97ced8cd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 17:46:09 +0000 Subject: [PATCH 02/17] Add AI coding assistant guide section to bindings/python README.md Agent-Logs-Url: https://github.com/keplergl/kepler.gl/sessions/d4f6dcf5-876f-43cc-9faf-1c9cd60bd9fd Co-authored-by: lixun910 <2423887+lixun910@users.noreply.github.com> --- bindings/python/README.md | 90 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/bindings/python/README.md b/bindings/python/README.md index a59ba827ef..174b107c93 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -32,6 +32,96 @@ map.add_data(data=df, name='my_data') map ``` +## Use with AI Coding Assistants + +This package ships with a **[SKILL.md](SKILL.md)** file — an AI-readable reference that teaches coding assistants how to use `keplergl` to create static HTML maps from geospatial data. + +Detailed per-map-type references live in **[skill-references/](skill-references/)**: + +| Reference | Description | +|-----------|-------------| +| [point-map.md](skill-references/point-map.md) | Scatter plot from lat/lng | +| [geojson-polygon-map.md](skill-references/geojson-polygon-map.md) | Polygons & lines from GeoJSON / GeoDataFrame | +| [h3-hexagon-map.md](skill-references/h3-hexagon-map.md) | H3 spatial index hexagons | +| [arc-line-map.md](skill-references/arc-line-map.md) | Origin–destination arcs & lines | +| [heatmap.md](skill-references/heatmap.md) | Density heatmap from points | +| [hexbin-aggregation-map.md](skill-references/hexbin-aggregation-map.md) | Spatial binning into hexagons | +| [trip-animation-map.md](skill-references/trip-animation-map.md) | Animated trips along paths | + +### Setting up with Claude Code + +1. Open your project in **Claude Code** (terminal-based) or **Claude Desktop with Projects**. + +2. Add the skill file to Claude's project knowledge: + - **Claude Code (CLI):** run the slash command below to register the skill: + ``` + /add-skill https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/SKILL.md + ``` + - **Claude Desktop → Projects:** open your Project, click **"Add Content"** in the Project Knowledge section, and paste the raw URL above (or upload the file directly). + +3. *(Optional)* For a specific map type, also add the relevant reference file. For example, to give Claude detailed GeoJSON/polygon guidance: + ``` + /add-skill https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/skill-references/geojson-polygon-map.md + ``` + +4. Start prompting — Claude now knows the `keplergl` API and can generate working code. + +### Example prompt + +> Create a polygon map from a GeoDataFrame. I have a shapefile at `./data/sf_neighborhoods.shp` +> with columns `name` and `population`. Color the polygons by population, use a light theme, +> and save it as a static HTML file I can open in my browser. + +### Example output + +Claude will generate a Python script similar to: + +```python +from keplergl import KeplerGl +import geopandas as gpd + +gdf = gpd.read_file('./data/sf_neighborhoods.shp') + +config = { + 'version': 'v1', + 'config': { + 'visState': { + 'layers': [{ + 'type': 'geojson', + 'config': { + 'dataId': 'neighborhoods', + 'label': 'SF Neighborhoods', + 'isVisible': True, + 'columns': {'geojson': '_geojson'}, + 'visConfig': { + 'opacity': 0.8, + 'filled': True, + 'stroked': True, + 'colorRange': { + 'name': 'Global Warming', + 'type': 'sequential', + 'category': 'Uber', + 'colors': ['#5A1846','#900C3F','#C70039','#E3611C','#F1920E','#FFC300'] + } + } + }, + 'visualChannels': { + 'colorField': {'name': 'population', 'type': 'integer'}, + 'colorScale': 'quantile' + } + }] + }, + 'mapStyle': {'styleType': 'positron'} + } +} + +map_1 = KeplerGl(data={'neighborhoods': gdf}, config=config, theme='light') +map_1.save_to_html(file_name='sf_neighborhoods.html', center_map=True, read_only=True) +print("Map saved to sf_neighborhoods.html") +``` + +Open `sf_neighborhoods.html` in any browser to see the interactive map — no server required. + ## Documentation For full documentation, visit [https://docs.kepler.gl/docs/keplergl-jupyter](https://docs.kepler.gl/docs/keplergl-jupyter). From b576f58a19ad88862d03e20efb4c4b2634be22d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 17:56:49 +0000 Subject: [PATCH 03/17] Rename SKILL.md to CLAUDE.md following Claude Code standard protocol and update README Agent-Logs-Url: https://github.com/keplergl/kepler.gl/sessions/098df374-1773-49d4-8a4c-e13a34df7c47 Co-authored-by: lixun910 <2423887+lixun910@users.noreply.github.com> --- bindings/python/CLAUDE.md | 193 +++++++++++++++++++++++++++++ bindings/python/README.md | 26 ++-- bindings/python/SKILL.md | 254 -------------------------------------- 3 files changed, 207 insertions(+), 266 deletions(-) create mode 100644 bindings/python/CLAUDE.md delete mode 100644 bindings/python/SKILL.md diff --git a/bindings/python/CLAUDE.md b/bindings/python/CLAUDE.md new file mode 100644 index 0000000000..d556ba1693 --- /dev/null +++ b/bindings/python/CLAUDE.md @@ -0,0 +1,193 @@ +# CLAUDE.md — keplergl Python Package + +> Project instructions for Claude Code. This file is automatically loaded when Claude Code operates in this directory. + +## Project Overview + +`keplergl` is a Python package for creating 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. + +- **Package:** `keplergl` on PyPI +- **Python:** >= 3.9 +- **Dependencies:** `pandas`, `geopandas`, `shapely` (installed automatically) + +## Install & Build + +```bash +pip install keplergl +``` + +No build step needed — this is a pure Python package with a bundled Jupyter widget. + +## Key API + +- `KeplerGl(height, data, config, theme, app_name)` — create a map widget +- `.add_data(data, name)` — add a dataset (DataFrame, GeoDataFrame, CSV string, GeoJSON dict/string, WKT) +- `.save_to_html(file_name, read_only, center_map)` — export to standalone HTML +- `.config` — read or set the map configuration dict + +### Constructor Parameters + +| 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) | +| `theme` | str | "" | `"light"`, `"dark"`, `"base"`, or `""` (default dark) | +| `app_name` | str | "kepler.gl" | App name in header and HTML title | + +### save_to_html Parameters + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `file_name` | str | required | Output file path | +| `read_only` | bool | False | True = hide side panel | +| `center_map` | bool | True | True = auto-fit map to data bounds | + +## Conventions + +- **`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. +- kepler.gl auto-detects column types: columns named `latitude`/`lat`/`lng`/`longitude` are recognized as coordinates. +- H3 hex IDs are auto-detected if a column contains valid H3 strings. +- Use `center_map=True` in `save_to_html()` to auto-fit map bounds to data. +- Use `read_only=True` to hide the side panel in exported HTML. +- The exported HTML is fully standalone — loads all dependencies from CDN. + +## Supported Data Formats + +| Format | How to Load | +|--------|-------------| +| pandas DataFrame | Columns with `lat`/`lng` (or similar) for point data | +| geopandas GeoDataFrame | Geometry column auto-detected; re-projected to EPSG:4326 | +| 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 + +```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 + +Free (no token needed): `dark-matter`, `positron`, `voyager`, `dark-matter-nolabels`, `positron-nolabels`, `voyager-nolabels` + +Mapbox (require `mapbox_token`): `dark`, `light`, `muted`, `muted_night` + +## Map Type Reference Files + +For detailed per-layer-type examples with full config, see files in `skill-references/`: + +- [point-map.md](skill-references/point-map.md) — Scatter plot from lat/lng +- [geojson-polygon-map.md](skill-references/geojson-polygon-map.md) — Polygons, lines from GeoJSON or GeoDataFrame +- [h3-hexagon-map.md](skill-references/h3-hexagon-map.md) — H3 spatial index hexagons +- [arc-line-map.md](skill-references/arc-line-map.md) — Origin-destination connections +- [heatmap.md](skill-references/heatmap.md) — Density heatmap from points +- [hexbin-aggregation-map.md](skill-references/hexbin-aggregation-map.md) — Spatial binning into hexagons +- [trip-animation-map.md](skill-references/trip-animation-map.md) — Animated trips along paths + +## Common Patterns + +### Quick point map from DataFrame + +```python +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'] +}) +map_1 = KeplerGl(height=600, data={'cities': df}) +map_1.save_to_html(file_name='cities_map.html') +``` + +### GeoDataFrame from shapefile + +```python +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) +``` + +### GeoJSON from file + +```python +from keplergl import KeplerGl +import json + +with open('boundaries.geojson', 'r') as f: + geojson = json.load(f) +map_1 = KeplerGl(data={'boundaries': geojson}) +map_1.save_to_html(file_name='boundaries_map.html', center_map=True) +``` + +### Multiple datasets + +```python +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 + +```python +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') +``` diff --git a/bindings/python/README.md b/bindings/python/README.md index 174b107c93..7562b98e7e 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -34,7 +34,7 @@ map ## Use with AI Coding Assistants -This package ships with a **[SKILL.md](SKILL.md)** file — an AI-readable reference that teaches coding assistants how to use `keplergl` to create static HTML maps from geospatial data. +This package ships with a **[CLAUDE.md](CLAUDE.md)** file — a project instructions file following the [Claude Code CLAUDE.md convention](https://docs.anthropic.com/en/docs/claude-code/memory). Claude Code automatically loads this file when operating in the `bindings/python/` directory, giving it full knowledge of the `keplergl` API. Detailed per-map-type references live in **[skill-references/](skill-references/)**: @@ -50,21 +50,23 @@ Detailed per-map-type references live in **[skill-references/](skill-references/ ### Setting up with Claude Code -1. Open your project in **Claude Code** (terminal-based) or **Claude Desktop with Projects**. +1. **Clone or download** the `CLAUDE.md` file (and optionally the `skill-references/` folder) into your project directory: -2. Add the skill file to Claude's project knowledge: - - **Claude Code (CLI):** run the slash command below to register the skill: - ``` - /add-skill https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/SKILL.md - ``` - - **Claude Desktop → Projects:** open your Project, click **"Add Content"** in the Project Knowledge section, and paste the raw URL above (or upload the file directly). - -3. *(Optional)* For a specific map type, also add the relevant reference file. For example, to give Claude detailed GeoJSON/polygon guidance: + ```bash + # Copy CLAUDE.md into your project root + curl -o CLAUDE.md https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/CLAUDE.md ``` - /add-skill https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/skill-references/geojson-polygon-map.md + + Claude Code automatically discovers and loads any `CLAUDE.md` file in the working directory or its parent directories — no manual registration is needed. + +2. *(Optional)* For detailed map-type guidance, also copy the reference files: + + ```bash + mkdir -p skill-references + curl -o skill-references/geojson-polygon-map.md https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/skill-references/geojson-polygon-map.md ``` -4. Start prompting — Claude now knows the `keplergl` API and can generate working code. +3. **Start Claude Code** in your project directory and begin prompting — Claude now knows the `keplergl` API and can generate working map code. ### Example prompt diff --git a/bindings/python/SKILL.md b/bindings/python/SKILL.md deleted file mode 100644 index 9745a7d754..0000000000 --- a/bindings/python/SKILL.md +++ /dev/null @@ -1,254 +0,0 @@ -# Skill: Create Static HTML Maps with keplergl-Jupyter - -## Purpose - -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 - -```bash -pip install keplergl -``` - -**Requirements:** Python >= 3.9 - -**Optional dependencies** (installed automatically with keplergl): -- `pandas` — for DataFrames -- `geopandas` — for GeoDataFrames and reading geospatial files -- `shapely` — for geometry operations (comes with geopandas) - -## Quick Start — Minimal Static Map - -```python -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'] -}) - -map_1 = KeplerGl(height=600, data={'cities': df}) -map_1.save_to_html(file_name='cities_map.html') -``` - -This creates `cities_map.html` — a fully self-contained interactive map. - -## Core API - -### `KeplerGl()` — Create a Map Widget - -```python -KeplerGl( - height=400, # Map height in pixels - data=None, # Dict of {"dataset_name": data_object} - config=None, # Map configuration dict (layers, filters, map state) - mapbox_token="", # Mapbox token (needed only for Mapbox basemap styles) - theme="", # "light", "dark", "base", or "" (default dark) - app_name="kepler.gl", # App name shown in header and HTML <title> -) -``` - -### `.add_data()` — Add a Dataset - -```python -map_1.add_data(data=df, name='my_dataset') -``` - -- `data`: DataFrame, GeoDataFrame, CSV string, GeoJSON dict, or GeoJSON string -- `name`: Dataset identifier — must match `dataId` in config if using a config - -### `.save_to_html()` — Export to Static HTML - -```python -map_1.save_to_html( - file_name='keplergl_map.html', # Output file path - read_only=False, # True = hide side panel - center_map=True, # True = auto-fit map to data bounds - mapbox_token="", # Mapbox token for Mapbox styles - config=None, # Override config (uses current if None) - data=None, # Override data (uses current if None) - theme=None, # Override theme - app_name=None, # Override app name -) -``` - -### `.config` — Get/Set Map Configuration - -```python -# Read current config (after interacting with map in Jupyter) -current_config = map_1.config - -# Apply a config -map_1.config = my_config_dict -``` - -## Supported Data Formats - -| Format | How to Load | -|--------|-------------| -| **pandas DataFrame** | Columns with `latitude`/`longitude` (or similar) for point data | -| **geopandas GeoDataFrame** | Geometry column auto-detected; re-projected to EPSG:4326 | -| **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 and Config - -kepler.gl automatically detects data types and creates layers. You can also provide a `config` dict to control layer types, colors, and visual settings. The most commonly used layer types are: - -| Layer Type | Config `type` Value | 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 (aggregation) | `"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 | - -## Map Type Reference Files - -For detailed examples of creating specific map types with full config, see: - -- [Point Map](skill-references/point-map.md) — Scatter plot of locations from lat/lng -- [GeoJSON / Polygon Map](skill-references/geojson-polygon-map.md) — Polygons, lines from GeoJSON or GeoDataFrame -- [H3 Hexagon Map](skill-references/h3-hexagon-map.md) — H3 spatial index hexagons -- [Arc / Line Map](skill-references/arc-line-map.md) — Origin-destination connections -- [Heatmap](skill-references/heatmap.md) — Density heatmap from points -- [Hexbin Aggregation Map](skill-references/hexbin-aggregation-map.md) — Spatial binning into hexagons -- [Trip Animation Map](skill-references/trip-animation-map.md) — Animated trips along paths - -## Config Structure - -A config dict has this 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' # 'dark-matter', 'positron', 'dark-matter-nolabels', 'positron-nolabels', 'voyager', 'voyager-nolabels' - } - } -} -``` - -### Key Rules for Config - -1. **`dataId` must match the dataset `name`** — Every layer and filter references a dataset by `dataId`. This must match the `name` passed to `add_data()` or the key in the `data` dict. -2. **GeoJSON columns use `_geojson`** — When data is loaded as GeoJSON, the geometry column is internally named `_geojson` in configs. -3. **Save and reuse configs** — Call `map_1.config` after customizing in the UI, then save and reuse: - -```python -# Save config to file -import json -with open('my_config.json', 'w') as f: - json.dump(map_1.config, f) - -# Load config from file -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') -``` - -## Common Patterns - -### Load GeoJSON from file and export - -```python -from keplergl import KeplerGl -import json - -with open('boundaries.geojson', 'r') as f: - geojson = json.load(f) - -map_1 = KeplerGl(data={'boundaries': geojson}) -map_1.save_to_html(file_name='boundaries_map.html', center_map=True) -``` - -### Load GeoDataFrame and export with read-only mode - -```python -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 on one map - -```python -from keplergl import KeplerGl -import pandas as pd - -points_df = pd.read_csv('locations.csv') -routes_df = pd.read_csv('routes.csv') - -map_1 = KeplerGl(data={ - 'locations': points_df, - 'routes': routes_df -}) -map_1.save_to_html(file_name='combined_map.html', center_map=True) -``` - -### Light theme with custom app name - -```python -map_1 = KeplerGl( - data={'data': df}, - theme='light', - app_name='My Dashboard' -) -map_1.save_to_html(file_name='dashboard.html') -``` - -## Basemap Styles - -Free basemap styles (no token needed): -- `dark-matter` — Dark background (default) -- `positron` — Light background -- `voyager` — Colorful with labels -- `dark-matter-nolabels`, `positron-nolabels`, `voyager-nolabels` — Without labels - -Mapbox styles (require `mapbox_token`): -- `dark` — Mapbox Dark -- `light` — Mapbox Light -- `muted` — Mapbox Muted Light -- `muted_night` — Mapbox Muted Night - -## Tips - -- Use `center_map=True` in `save_to_html()` to auto-fit the map bounds to your data. -- Use `read_only=True` to hide the side panel in exported HTML for clean presentations. -- kepler.gl auto-detects column types: columns named `latitude`/`lat`/`lng`/`longitude` are recognized as coordinates. -- H3 hex IDs are auto-detected if a column contains valid H3 strings. -- For large datasets, use `use_arrow=True` when creating the widget for faster serialization. -- The exported HTML is fully standalone — it loads all dependencies from CDN. From d41befde4e7589b90f6b2c9879f712e3f749cd24 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 17:58:17 +0000 Subject: [PATCH 04/17] Plan: rewrite SKILL.md with YAML frontmatter per official Claude Code skills protocol Agent-Logs-Url: https://github.com/keplergl/kepler.gl/sessions/098df374-1773-49d4-8a4c-e13a34df7c47 Co-authored-by: lixun910 <2423887+lixun910@users.noreply.github.com> --- bindings/python/{CLAUDE.md => SKILL.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename bindings/python/{CLAUDE.md => SKILL.md} (100%) diff --git a/bindings/python/CLAUDE.md b/bindings/python/SKILL.md similarity index 100% rename from bindings/python/CLAUDE.md rename to bindings/python/SKILL.md From fc7a268d3b4a67d62aeb6b09b6e89275dec3928c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 18:00:05 +0000 Subject: [PATCH 05/17] Rewrite SKILL.md with YAML frontmatter per Claude Code skills protocol and update README Agent-Logs-Url: https://github.com/keplergl/kepler.gl/sessions/098df374-1773-49d4-8a4c-e13a34df7c47 Co-authored-by: lixun910 <2423887+lixun910@users.noreply.github.com> --- bindings/python/README.md | 62 ++++++++++++++++++++++++-------- bindings/python/SKILL.md | 75 +++++++++++++++++++++------------------ 2 files changed, 88 insertions(+), 49 deletions(-) diff --git a/bindings/python/README.md b/bindings/python/README.md index 7562b98e7e..e6a2a9b2bf 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -34,9 +34,9 @@ map ## Use with AI Coding Assistants -This package ships with a **[CLAUDE.md](CLAUDE.md)** file — a project instructions file following the [Claude Code CLAUDE.md convention](https://docs.anthropic.com/en/docs/claude-code/memory). Claude Code automatically loads this file when operating in the `bindings/python/` directory, giving it full knowledge of the `keplergl` API. +This package ships with a **[SKILL.md](SKILL.md)** file — a [Claude Code skill](https://code.claude.com/docs/en/skills) that teaches Claude how to create interactive HTML maps using the `keplergl` API. Claude automatically discovers the skill and uses it when you ask about map visualization. -Detailed per-map-type references live in **[skill-references/](skill-references/)**: +Detailed per-map-type references (supporting files loaded by Claude only when needed): | Reference | Description | |-----------|-------------| @@ -50,23 +50,49 @@ Detailed per-map-type references live in **[skill-references/](skill-references/ ### Setting up with Claude Code -1. **Clone or download** the `CLAUDE.md` file (and optionally the `skill-references/` folder) into your project directory: +Claude Code skills are stored as directories containing a `SKILL.md` file. You can install this skill at the **personal** level (available across all projects) or the **project** level (shared with your team via git). - ```bash - # Copy CLAUDE.md into your project root - curl -o CLAUDE.md https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/CLAUDE.md - ``` +**Option A — Personal skill** (available in all your projects): - Claude Code automatically discovers and loads any `CLAUDE.md` file in the working directory or its parent directories — no manual registration is needed. +```bash +# Create the skill directory +mkdir -p ~/.claude/skills/keplergl-map -2. *(Optional)* For detailed map-type guidance, also copy the reference files: +# Download SKILL.md and supporting reference files +curl -o ~/.claude/skills/keplergl-map/SKILL.md \ + https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/SKILL.md - ```bash - mkdir -p skill-references - curl -o skill-references/geojson-polygon-map.md https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/skill-references/geojson-polygon-map.md - ``` +# (Optional) Download detailed map-type references +mkdir -p ~/.claude/skills/keplergl-map/skill-references +for ref in point-map geojson-polygon-map h3-hexagon-map arc-line-map heatmap hexbin-aggregation-map trip-animation-map; do + curl -o ~/.claude/skills/keplergl-map/skill-references/${ref}.md \ + https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/skill-references/${ref}.md +done +``` + +**Option B — Project skill** (shared with your team via git): + +```bash +# Create the skill directory in your project +mkdir -p .claude/skills/keplergl-map -3. **Start Claude Code** in your project directory and begin prompting — Claude now knows the `keplergl` API and can generate working map code. +# Download SKILL.md and supporting reference files +curl -o .claude/skills/keplergl-map/SKILL.md \ + https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/SKILL.md + +# (Optional) Download detailed map-type references +mkdir -p .claude/skills/keplergl-map/skill-references +for ref in point-map geojson-polygon-map h3-hexagon-map arc-line-map heatmap hexbin-aggregation-map trip-animation-map; do + curl -o .claude/skills/keplergl-map/skill-references/${ref}.md \ + https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/skill-references/${ref}.md +done + +# Commit to share with your team +git add .claude/skills/keplergl-map +git commit -m "Add keplergl map skill for Claude Code" +``` + +Once installed, Claude Code automatically discovers the skill — no restart needed. Claude will use it whenever you ask about creating maps or visualizing geospatial data, or you can invoke it directly with `/keplergl-map`. ### Example prompt @@ -76,7 +102,13 @@ Detailed per-map-type references live in **[skill-references/](skill-references/ ### Example output -Claude will generate a Python script similar to: +Claude will generate a Python script and run it, producing output like: + +``` +Map saved to sf_neighborhoods.html +``` + +The generated script will look similar to: ```python from keplergl import KeplerGl diff --git a/bindings/python/SKILL.md b/bindings/python/SKILL.md index d556ba1693..d5ee706e42 100644 --- a/bindings/python/SKILL.md +++ b/bindings/python/SKILL.md @@ -1,31 +1,31 @@ -# CLAUDE.md — keplergl Python Package +--- +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. +--- -> Project instructions for Claude Code. This file is automatically loaded when Claude Code operates in this directory. +# Create Maps with keplergl -## Project Overview +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. -`keplergl` is a Python package for creating 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. - -- **Package:** `keplergl` on PyPI -- **Python:** >= 3.9 -- **Dependencies:** `pandas`, `geopandas`, `shapely` (installed automatically) - -## Install & Build +## Installation ```bash pip install keplergl ``` -No build step needed — this is a pure Python package with a bundled Jupyter widget. +Requirements: Python >= 3.9. Dependencies (`pandas`, `geopandas`, `shapely`) are installed automatically. -## Key API +## Instructions -- `KeplerGl(height, data, config, theme, app_name)` — create a map widget -- `.add_data(data, name)` — add a dataset (DataFrame, GeoDataFrame, CSV string, GeoJSON dict/string, WKT) -- `.save_to_html(file_name, read_only, center_map)` — export to standalone HTML -- `.config` — read or set the map configuration dict +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 +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 -### Constructor Parameters +## API Reference + +### `KeplerGl(height, data, config, theme, app_name)` | Parameter | Type | Default | Description | |-----------|------|---------|-------------| @@ -36,7 +36,12 @@ No build step needed — this is a pure Python package with a bundled Jupyter wi | `theme` | str | "" | `"light"`, `"dark"`, `"base"`, or `""` (default dark) | | `app_name` | str | "kepler.gl" | App name in header and HTML title | -### save_to_html Parameters +### `.add_data(data, name)` + +- `data`: DataFrame, GeoDataFrame, CSV string, GeoJSON dict, or GeoJSON string +- `name`: Dataset identifier — must match `dataId` in config if using a config + +### `.save_to_html(file_name, read_only, center_map)` | Parameter | Type | Default | Description | |-----------|------|---------|-------------| @@ -44,15 +49,17 @@ No build step needed — this is a pure Python package with a bundled Jupyter wi | `read_only` | bool | False | True = hide side panel | | `center_map` | bool | True | True = auto-fit map to data bounds | -## Conventions +### `.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. -- kepler.gl auto-detects column types: columns named `latitude`/`lat`/`lng`/`longitude` are recognized as coordinates. +- 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` in `save_to_html()` to auto-fit map bounds to data. -- Use `read_only=True` to hide the side panel in exported HTML. -- The exported HTML is fully standalone — loads all dependencies from CDN. +- Use `center_map=True` to auto-fit map bounds. Use `read_only=True` to hide the side panel. ## Supported Data Formats @@ -116,21 +123,21 @@ Free (no token needed): `dark-matter`, `positron`, `voyager`, `dark-matter-nolab Mapbox (require `mapbox_token`): `dark`, `light`, `muted`, `muted_night` -## Map Type Reference Files +## Additional Resources -For detailed per-layer-type examples with full config, see files in `skill-references/`: +For detailed per-layer-type examples with full config, see supporting files: -- [point-map.md](skill-references/point-map.md) — Scatter plot from lat/lng -- [geojson-polygon-map.md](skill-references/geojson-polygon-map.md) — Polygons, lines from GeoJSON or GeoDataFrame -- [h3-hexagon-map.md](skill-references/h3-hexagon-map.md) — H3 spatial index hexagons -- [arc-line-map.md](skill-references/arc-line-map.md) — Origin-destination connections -- [heatmap.md](skill-references/heatmap.md) — Density heatmap from points -- [hexbin-aggregation-map.md](skill-references/hexbin-aggregation-map.md) — Spatial binning into hexagons -- [trip-animation-map.md](skill-references/trip-animation-map.md) — Animated trips along paths +- [Point Map](skill-references/point-map.md) — Scatter plot from lat/lng +- [GeoJSON / Polygon Map](skill-references/geojson-polygon-map.md) — Polygons, lines from GeoJSON or GeoDataFrame +- [H3 Hexagon Map](skill-references/h3-hexagon-map.md) — H3 spatial index hexagons +- [Arc / Line Map](skill-references/arc-line-map.md) — Origin-destination connections +- [Heatmap](skill-references/heatmap.md) — Density heatmap from points +- [Hexbin Aggregation Map](skill-references/hexbin-aggregation-map.md) — Spatial binning into hexagons +- [Trip Animation Map](skill-references/trip-animation-map.md) — Animated trips along paths -## Common Patterns +## Examples -### Quick point map from DataFrame +### Point map from DataFrame ```python from keplergl import KeplerGl From 0a2da21c7aee8ac687ba3c6ea820b75c866bddab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 19:17:36 +0000 Subject: [PATCH 06/17] Make skill-references always installed alongside SKILL.md, remove optional step Agent-Logs-Url: https://github.com/keplergl/kepler.gl/sessions/b1583189-44ad-47cf-947d-453f2fcfd433 Co-authored-by: lixun910 <2423887+lixun910@users.noreply.github.com> --- bindings/python/README.md | 45 +++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/bindings/python/README.md b/bindings/python/README.md index e6a2a9b2bf..274c58bf25 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -55,39 +55,34 @@ Claude Code skills are stored as directories containing a `SKILL.md` file. You c **Option A — Personal skill** (available in all your projects): ```bash -# Create the skill directory -mkdir -p ~/.claude/skills/keplergl-map - -# Download SKILL.md and supporting reference files -curl -o ~/.claude/skills/keplergl-map/SKILL.md \ - https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/SKILL.md - -# (Optional) Download detailed map-type references +BASE=https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python mkdir -p ~/.claude/skills/keplergl-map/skill-references -for ref in point-map geojson-polygon-map h3-hexagon-map arc-line-map heatmap hexbin-aggregation-map trip-animation-map; do - curl -o ~/.claude/skills/keplergl-map/skill-references/${ref}.md \ - https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/skill-references/${ref}.md -done + +curl -o ~/.claude/skills/keplergl-map/SKILL.md $BASE/SKILL.md +curl -o ~/.claude/skills/keplergl-map/skill-references/point-map.md $BASE/skill-references/point-map.md +curl -o ~/.claude/skills/keplergl-map/skill-references/geojson-polygon-map.md $BASE/skill-references/geojson-polygon-map.md +curl -o ~/.claude/skills/keplergl-map/skill-references/h3-hexagon-map.md $BASE/skill-references/h3-hexagon-map.md +curl -o ~/.claude/skills/keplergl-map/skill-references/arc-line-map.md $BASE/skill-references/arc-line-map.md +curl -o ~/.claude/skills/keplergl-map/skill-references/heatmap.md $BASE/skill-references/heatmap.md +curl -o ~/.claude/skills/keplergl-map/skill-references/hexbin-aggregation-map.md $BASE/skill-references/hexbin-aggregation-map.md +curl -o ~/.claude/skills/keplergl-map/skill-references/trip-animation-map.md $BASE/skill-references/trip-animation-map.md ``` **Option B — Project skill** (shared with your team via git): ```bash -# Create the skill directory in your project -mkdir -p .claude/skills/keplergl-map - -# Download SKILL.md and supporting reference files -curl -o .claude/skills/keplergl-map/SKILL.md \ - https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/SKILL.md - -# (Optional) Download detailed map-type references +BASE=https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python mkdir -p .claude/skills/keplergl-map/skill-references -for ref in point-map geojson-polygon-map h3-hexagon-map arc-line-map heatmap hexbin-aggregation-map trip-animation-map; do - curl -o .claude/skills/keplergl-map/skill-references/${ref}.md \ - https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python/skill-references/${ref}.md -done -# Commit to share with your team +curl -o .claude/skills/keplergl-map/SKILL.md $BASE/SKILL.md +curl -o .claude/skills/keplergl-map/skill-references/point-map.md $BASE/skill-references/point-map.md +curl -o .claude/skills/keplergl-map/skill-references/geojson-polygon-map.md $BASE/skill-references/geojson-polygon-map.md +curl -o .claude/skills/keplergl-map/skill-references/h3-hexagon-map.md $BASE/skill-references/h3-hexagon-map.md +curl -o .claude/skills/keplergl-map/skill-references/arc-line-map.md $BASE/skill-references/arc-line-map.md +curl -o .claude/skills/keplergl-map/skill-references/heatmap.md $BASE/skill-references/heatmap.md +curl -o .claude/skills/keplergl-map/skill-references/hexbin-aggregation-map.md $BASE/skill-references/hexbin-aggregation-map.md +curl -o .claude/skills/keplergl-map/skill-references/trip-animation-map.md $BASE/skill-references/trip-animation-map.md + git add .claude/skills/keplergl-map git commit -m "Add keplergl map skill for Claude Code" ``` From 0b8b47949f82a3e3fd539335db1e4173e41de8e9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 21:16:51 +0000 Subject: [PATCH 07/17] Document default quantile+vibrant color guidance and custom-break workflow in SKILL Agent-Logs-Url: https://github.com/keplergl/kepler.gl/sessions/55d2a3b1-1d4e-4eaa-af09-c8f5f26ad2b9 Co-authored-by: lixun910 <2423887+lixun910@users.noreply.github.com> --- bindings/python/SKILL.md | 43 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/bindings/python/SKILL.md b/bindings/python/SKILL.md index d5ee706e42..43927a6f9a 100644 --- a/bindings/python/SKILL.md +++ b/bindings/python/SKILL.md @@ -19,7 +19,7 @@ Requirements: Python >= 3.9. Dependencies (`pandas`, `geopandas`, `shapely`) are 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 +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 @@ -60,6 +60,9 @@ Read or set the map configuration dict. Use `map.config` after customizing in Ju - 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. ## Supported Data Formats @@ -137,7 +140,7 @@ For detailed per-layer-type examples with full config, see supporting files: ## Examples -### Point map from DataFrame +### Point map from DataFrame (quantile + vibrant palette) ```python from keplergl import KeplerGl @@ -146,9 +149,41 @@ 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'] + 'name': ['San Francisco', 'Los Angeles', 'New York'], + 'value': [15, 42, 27] }) -map_1 = KeplerGl(height=600, data={'cities': df}) + +config = { + 'version': 'v1', + 'config': { + 'visState': { + 'layers': [{ + 'type': 'point', + 'config': { + 'dataId': 'cities', + 'label': 'Cities', + 'isVisible': True, + 'columns': {'lat': 'lat', 'lng': 'lng'}, + 'visConfig': { + 'radius': 10, + 'colorRange': { + 'name': 'Global Warming', + 'type': 'sequential', + 'category': 'Uber', + 'colors': ['#5A1846', '#900C3F', '#C70039', '#E3611C', '#F1920E', '#FFC300'] + } + } + }, + 'visualChannels': { + 'colorField': {'name': 'value', 'type': 'integer'}, + 'colorScale': 'quantile' + } + }] + } + } +} + +map_1 = KeplerGl(height=600, data={'cities': df}, config=config) map_1.save_to_html(file_name='cities_map.html') ``` From df6f5d8eb4a23d18951ed71f91929205016b61ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 21:46:33 +0000 Subject: [PATCH 08/17] Address PR review thread feedback for Python skill docs Agent-Logs-Url: https://github.com/keplergl/kepler.gl/sessions/d4300622-677a-4b98-81ca-4cd43ad5e5c5 Co-authored-by: lixun910 <2423887+lixun910@users.noreply.github.com> --- bindings/python/README.md | 10 +++++----- bindings/python/SKILL.md | 14 +++++++++++--- bindings/python/skill-references/point-map.md | 2 +- .../python/skill-references/trip-animation-map.md | 2 +- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/bindings/python/README.md b/bindings/python/README.md index 274c58bf25..bff76c6ffb 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -34,7 +34,7 @@ map ## Use with AI Coding Assistants -This package ships with a **[SKILL.md](SKILL.md)** file — a [Claude Code skill](https://code.claude.com/docs/en/skills) that teaches Claude how to create interactive HTML maps using the `keplergl` API. Claude automatically discovers the skill and uses it when you ask about map visualization. +This package ships with a **[SKILL.md](SKILL.md)** file — a [Claude Code skill](https://code.claude.com/docs/en/skills) that teaches Claude how to create interactive HTML maps using the `keplergl` API. Claude automatically discovers and uses the skill once it is installed in a Claude skills directory. Detailed per-map-type references (supporting files loaded by Claude only when needed): @@ -55,7 +55,7 @@ Claude Code skills are stored as directories containing a `SKILL.md` file. You c **Option A — Personal skill** (available in all your projects): ```bash -BASE=https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python +BASE=https://raw.githubusercontent.com/keplergl/kepler.gl/main/bindings/python mkdir -p ~/.claude/skills/keplergl-map/skill-references curl -o ~/.claude/skills/keplergl-map/SKILL.md $BASE/SKILL.md @@ -71,7 +71,7 @@ curl -o ~/.claude/skills/keplergl-map/skill-references/trip-animation-map.md **Option B — Project skill** (shared with your team via git): ```bash -BASE=https://raw.githubusercontent.com/keplergl/kepler.gl/master/bindings/python +BASE=https://raw.githubusercontent.com/keplergl/kepler.gl/main/bindings/python mkdir -p .claude/skills/keplergl-map/skill-references curl -o .claude/skills/keplergl-map/SKILL.md $BASE/SKILL.md @@ -100,7 +100,7 @@ Once installed, Claude Code automatically discovers the skill — no restart nee Claude will generate a Python script and run it, producing output like: ``` -Map saved to sf_neighborhoods.html +Map saved to sf_neighborhoods.html! ``` The generated script will look similar to: @@ -146,7 +146,7 @@ config = { map_1 = KeplerGl(data={'neighborhoods': gdf}, config=config, theme='light') map_1.save_to_html(file_name='sf_neighborhoods.html', center_map=True, read_only=True) -print("Map saved to sf_neighborhoods.html") +print("Map saved to sf_neighborhoods.html!") ``` Open `sf_neighborhoods.html` in any browser to see the interactive map — no server required. diff --git a/bindings/python/SKILL.md b/bindings/python/SKILL.md index 43927a6f9a..c541f3ac0d 100644 --- a/bindings/python/SKILL.md +++ b/bindings/python/SKILL.md @@ -25,7 +25,7 @@ Requirements: Python >= 3.9. Dependencies (`pandas`, `geopandas`, `shapely`) are ## API Reference -### `KeplerGl(height, data, config, theme, app_name)` +### `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 | |-----------|------|---------|-------------| @@ -33,6 +33,8 @@ Requirements: Python >= 3.9. Dependencies (`pandas`, `geopandas`, `shapely`) are | `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 | @@ -41,13 +43,19 @@ Requirements: Python >= 3.9. Dependencies (`pandas`, `geopandas`, `shapely`) are - `data`: DataFrame, GeoDataFrame, CSV string, GeoJSON dict, or GeoJSON string - `name`: Dataset identifier — must match `dataId` in config if using a config -### `.save_to_html(file_name, read_only, center_map)` +### `.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 | required | 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` @@ -69,7 +77,7 @@ Read or set the map configuration dict. Use `map.config` after customizing in Ju | Format | How to Load | |--------|-------------| | pandas DataFrame | Columns with `lat`/`lng` (or similar) for point data | -| geopandas GeoDataFrame | Geometry column auto-detected; re-projected to EPSG:4326 | +| 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 | diff --git a/bindings/python/skill-references/point-map.md b/bindings/python/skill-references/point-map.md index 13e2c4746e..57662c286d 100644 --- a/bindings/python/skill-references/point-map.md +++ b/bindings/python/skill-references/point-map.md @@ -99,4 +99,4 @@ map_1.save_to_html(file_name='point_map_styled.html') | `visConfig.filled` | Whether points are filled | | `visualChannels.colorField` | Column to map to color | | `visualChannels.sizeField` | Column to map to point size | -| `visualChannels.colorScale` | `'quantile'`, `'quantize'`, `'ordinal'` | +| `visualChannels.colorScale` | Common options: `'quantile'`, `'quantize'`, `'ordinal'` (and `'custom'` for color channels in supported contexts) | diff --git a/bindings/python/skill-references/trip-animation-map.md b/bindings/python/skill-references/trip-animation-map.md index 385be81866..cfab1fc8a5 100644 --- a/bindings/python/skill-references/trip-animation-map.md +++ b/bindings/python/skill-references/trip-animation-map.md @@ -4,7 +4,7 @@ Animate movement along paths over time. Trip layers render GeoJSON LineString ge ## Data -Trip data must be GeoJSON with `LineString` or `MultiLineString` geometry where each coordinate has a timestamp. The timestamp is provided as a fourth value in each coordinate `[lng, lat, altitude, timestamp]` or via a properties field. +Trip data must be GeoJSON with `LineString` or `MultiLineString` geometry where each coordinate has a timestamp as the fourth value: `[lng, lat, altitude, timestamp]`. ```python trip_geojson = { From 476f8341d80e9c8d551a3f6f569616cc42d30e33 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 21:54:51 +0000 Subject: [PATCH 09/17] docs(python): add Codex skill setup and packaging guidance Agent-Logs-Url: https://github.com/keplergl/kepler.gl/sessions/dca4bbac-713d-451b-9f10-0a76a069c50c Co-authored-by: lixun910 <2423887+lixun910@users.noreply.github.com> --- bindings/python/README.md | 78 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/bindings/python/README.md b/bindings/python/README.md index bff76c6ffb..aa05ee2100 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -34,7 +34,10 @@ map ## Use with AI Coding Assistants -This package ships with a **[SKILL.md](SKILL.md)** file — a [Claude Code skill](https://code.claude.com/docs/en/skills) that teaches Claude how to create interactive HTML maps using the `keplergl` API. Claude automatically discovers and uses the skill once it is installed in a Claude skills directory. +This package ships with a **[SKILL.md](SKILL.md)** file for AI assistants to generate `keplergl` map scripts and exports consistently. + +- [Claude Code](https://code.claude.com/docs/en/skills): install under `~/.claude/skills` or `.claude/skills` +- [Codex](https://developers.openai.com/codex/): install under `~/.agents/skills` or `.agents/skills` Detailed per-map-type references (supporting files loaded by Claude only when needed): @@ -89,6 +92,79 @@ git commit -m "Add keplergl map skill for Claude Code" Once installed, Claude Code automatically discovers the skill — no restart needed. Claude will use it whenever you ask about creating maps or visualizing geospatial data, or you can invoke it directly with `/keplergl-map`. +### Setting up with Codex + +Codex skills are stored as directories containing a `SKILL.md` file. + +**Option A — Personal skill** (available in all your projects): + +```bash +BASE=https://raw.githubusercontent.com/keplergl/kepler.gl/main/bindings/python +mkdir -p ~/.agents/skills/keplergl-map/skill-references + +curl -o ~/.agents/skills/keplergl-map/SKILL.md $BASE/SKILL.md +curl -o ~/.agents/skills/keplergl-map/skill-references/point-map.md $BASE/skill-references/point-map.md +curl -o ~/.agents/skills/keplergl-map/skill-references/geojson-polygon-map.md $BASE/skill-references/geojson-polygon-map.md +curl -o ~/.agents/skills/keplergl-map/skill-references/h3-hexagon-map.md $BASE/skill-references/h3-hexagon-map.md +curl -o ~/.agents/skills/keplergl-map/skill-references/arc-line-map.md $BASE/skill-references/arc-line-map.md +curl -o ~/.agents/skills/keplergl-map/skill-references/heatmap.md $BASE/skill-references/heatmap.md +curl -o ~/.agents/skills/keplergl-map/skill-references/hexbin-aggregation-map.md $BASE/skill-references/hexbin-aggregation-map.md +curl -o ~/.agents/skills/keplergl-map/skill-references/trip-animation-map.md $BASE/skill-references/trip-animation-map.md +``` + +**Option B — Project skill** (shared with your team via git): + +```bash +BASE=https://raw.githubusercontent.com/keplergl/kepler.gl/main/bindings/python +mkdir -p .agents/skills/keplergl-map/skill-references + +curl -o .agents/skills/keplergl-map/SKILL.md $BASE/SKILL.md +curl -o .agents/skills/keplergl-map/skill-references/point-map.md $BASE/skill-references/point-map.md +curl -o .agents/skills/keplergl-map/skill-references/geojson-polygon-map.md $BASE/skill-references/geojson-polygon-map.md +curl -o .agents/skills/keplergl-map/skill-references/h3-hexagon-map.md $BASE/skill-references/h3-hexagon-map.md +curl -o .agents/skills/keplergl-map/skill-references/arc-line-map.md $BASE/skill-references/arc-line-map.md +curl -o .agents/skills/keplergl-map/skill-references/heatmap.md $BASE/skill-references/heatmap.md +curl -o .agents/skills/keplergl-map/skill-references/hexbin-aggregation-map.md $BASE/skill-references/hexbin-aggregation-map.md +curl -o .agents/skills/keplergl-map/skill-references/trip-animation-map.md $BASE/skill-references/trip-animation-map.md + +git add .agents/skills/keplergl-map +git commit -m "Add keplergl map skill for Codex" +``` + +> Codex skill metadata requires both `name` and `description` in the SKILL frontmatter. +> If your local copy is missing `name`, add: +> +> ```yaml +> --- +> name: keplergl-map +> description: Create interactive map visualizations and export standalone HTML with keplergl. +> --- +> ``` + +#### Optional Codex app metadata and icon assets + +You can add Codex app metadata at `agents/openai.yaml` in the skill folder and use the kepler.gl icon as skill assets: + +```bash +mkdir -p .agents/skills/keplergl-map/agents/assets +curl -o .agents/skills/keplergl-map/agents/assets/kepler-gl-icon.png \ + https://raw.githubusercontent.com/keplergl/kepler.gl/main/website/src/static/favicon.png +``` + +```yaml +interface: + display_name: "keplergl map" + short_description: "Create interactive keplergl HTML maps from tabular and geospatial data" + icon_small: "./agents/assets/kepler-gl-icon.png" + icon_large: "./agents/assets/kepler-gl-icon.png" + brand_color: "#2FA7F4" +``` + +#### Easiest distribution for Codex users + +For easy installation across teams, package this skill as a **Codex plugin** (recommended). Plugins can bundle skills, metadata (`agents/openai.yaml`), assets, and optional integrations in one installable package. +For local experimentation only, users can also install curated skills with `$skill-installer`. + ### Example prompt > Create a polygon map from a GeoDataFrame. I have a shapefile at `./data/sf_neighborhoods.shp` From f7579b2a6d22340da3dd37f9c71d9b76bb0468cd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 21:57:10 +0000 Subject: [PATCH 10/17] docs(python): document Codex skill setup and distribution in README Agent-Logs-Url: https://github.com/keplergl/kepler.gl/sessions/dca4bbac-713d-451b-9f10-0a76a069c50c Co-authored-by: lixun910 <2423887+lixun910@users.noreply.github.com> --- bindings/python/README.md | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/bindings/python/README.md b/bindings/python/README.md index aa05ee2100..e8452a937d 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -112,6 +112,11 @@ curl -o ~/.agents/skills/keplergl-map/skill-references/hexbin-aggregation-map.md curl -o ~/.agents/skills/keplergl-map/skill-references/trip-animation-map.md $BASE/skill-references/trip-animation-map.md ``` +Then verify `~/.agents/skills/keplergl-map/SKILL.md` frontmatter includes both: + +- `name: keplergl-map` +- `description: ...` + **Option B — Project skill** (shared with your team via git): ```bash @@ -131,39 +136,46 @@ git add .agents/skills/keplergl-map git commit -m "Add keplergl map skill for Codex" ``` -> Codex skill metadata requires both `name` and `description` in the SKILL frontmatter. -> If your local copy is missing `name`, add: -> -> ```yaml -> --- -> name: keplergl-map -> description: Create interactive map visualizations and export standalone HTML with keplergl. -> --- -> ``` +Then verify `.agents/skills/keplergl-map/SKILL.md` frontmatter includes both: + +- `name: keplergl-map` +- `description: ...` #### Optional Codex app metadata and icon assets -You can add Codex app metadata at `agents/openai.yaml` in the skill folder and use the kepler.gl icon as skill assets: +Inside each Codex skill folder, add metadata at `agents/openai.yaml` and assets under `agents/assets/`. +That means: + +- Personal install: `~/.agents/skills/keplergl-map/agents/openai.yaml` +- Project install: `.agents/skills/keplergl-map/agents/openai.yaml` + +Example asset setup: ```bash +# Project-level mkdir -p .agents/skills/keplergl-map/agents/assets curl -o .agents/skills/keplergl-map/agents/assets/kepler-gl-icon.png \ https://raw.githubusercontent.com/keplergl/kepler.gl/main/website/src/static/favicon.png + +# Personal-level +mkdir -p ~/.agents/skills/keplergl-map/agents/assets +curl -o ~/.agents/skills/keplergl-map/agents/assets/kepler-gl-icon.png \ + https://raw.githubusercontent.com/keplergl/kepler.gl/main/website/src/static/favicon.png ``` ```yaml interface: display_name: "keplergl map" short_description: "Create interactive keplergl HTML maps from tabular and geospatial data" - icon_small: "./agents/assets/kepler-gl-icon.png" - icon_large: "./agents/assets/kepler-gl-icon.png" + icon_small: "./assets/kepler-gl-icon.png" + icon_large: "./assets/kepler-gl-icon.png" brand_color: "#2FA7F4" ``` #### Easiest distribution for Codex users For easy installation across teams, package this skill as a **Codex plugin** (recommended). Plugins can bundle skills, metadata (`agents/openai.yaml`), assets, and optional integrations in one installable package. -For local experimentation only, users can also install curated skills with `$skill-installer`. +For local experimentation only, Codex users can install curated skills with the built-in `$skill-installer` command (see https://developers.openai.com/codex/skills). ### Example prompt From 2a14fc7c39a4f906ef29c43b43fb73bd7c4bd9d2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 22:03:10 +0000 Subject: [PATCH 11/17] fix: add name to SKILL.md frontmatter, complete add_data/save_to_html API docs Agent-Logs-Url: https://github.com/keplergl/kepler.gl/sessions/3e282275-ba68-458f-a020-3816c8f0b74b Co-authored-by: lixun910 <2423887+lixun910@users.noreply.github.com> --- bindings/python/SKILL.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bindings/python/SKILL.md b/bindings/python/SKILL.md index c541f3ac0d..da49089f14 100644 --- a/bindings/python/SKILL.md +++ b/bindings/python/SKILL.md @@ -1,4 +1,5 @@ --- +name: keplergl-map 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. --- @@ -38,16 +39,17 @@ Requirements: Python >= 3.9. Dependencies (`pandas`, `geopandas`, `shapely`) are | `theme` | str | "" | `"light"`, `"dark"`, `"base"`, or `""` (default dark) | | `app_name` | str | "kepler.gl" | App name in header and HTML title | -### `.add_data(data, name)` +### `.add_data(data, name="data", use_arrow=None)` - `data`: DataFrame, GeoDataFrame, CSV string, GeoJSON dict, or GeoJSON string -- `name`: Dataset identifier — must match `dataId` in config if using a config +- `name`: Dataset identifier (default `"data"`) — must match `dataId` in config if using a config +- `use_arrow`: bool or None — when `True`, serialize DataFrames as Arrow IPC; when `None`, uses the widget-level `use_arrow` setting ### `.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 | required | Output file path | +| `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 | From 756ee4ad96fdea11c30124550bdff2d35b803a94 Mon Sep 17 00:00:00 2001 From: Xun Li <lixun910@gmail.com> Date: Sat, 9 May 2026 15:55:44 -0700 Subject: [PATCH 12/17] docs(python): update SKILL.md with kepler.gl-jupyter requirements and visualChannels guidance - Added requirement for `kepler.gl-jupyter >= 0.4.0` to ensure compatibility with examples. - Clarified that `colorField`, `colorScale`, `sizeField`, and `heightField` should be placed under `visualChannels` instead of `config` to avoid silent errors. - Included a new example for creating a GeoJSON choropleth map using visual channels. --- bindings/python/SKILL.md | 55 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/bindings/python/SKILL.md b/bindings/python/SKILL.md index c541f3ac0d..0bc51c8ced 100644 --- a/bindings/python/SKILL.md +++ b/bindings/python/SKILL.md @@ -12,7 +12,7 @@ Use the `keplergl` Python package to create standalone, interactive HTML map fil pip install keplergl ``` -Requirements: Python >= 3.9. Dependencies (`pandas`, `geopandas`, `shapely`) are installed automatically. +Requires `kepler.gl-jupyter >= 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 @@ -65,6 +65,7 @@ Read or set the map configuration dict. Use `map.config` after customizing in Ju - **`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. @@ -218,6 +219,58 @@ map_1 = KeplerGl(data={'boundaries': geojson}) map_1.save_to_html(file_name='boundaries_map.html', center_map=True) ``` +### GeoJSON choropleth colored by a numeric attribute + +Load with geopandas, then put the field-to-channel mappings under `visualChannels` (not `config`): + +```python +import geopandas as gpd +from keplergl import KeplerGl + +gdf = gpd.read_file('natregimes.geojson') + +config = { + 'version': 'v1', + 'config': { + 'visState': { + 'layers': [{ + 'id': 'hr60_layer', + 'type': 'geojson', + 'config': { + 'dataId': 'natregimes', + 'label': 'HR60', + 'columns': {'geojson': '_geojson'}, + 'isVisible': True, + 'visConfig': { + 'opacity': 0.8, + 'stroked': True, + 'filled': True, + 'thickness': 0.2, + 'strokeColor': [80, 80, 80], + 'colorRange': { + 'name': 'Global Warming', + 'type': 'sequential', + 'category': 'Uber', + 'colors': ['#5A1846', '#900C3F', '#C70039', + '#E3611C', '#F1920E', '#FFC300'] + } + } + }, + 'visualChannels': { + 'colorField': {'name': 'HR60', 'type': 'real'}, + 'colorScale': 'quantile' + } + }] + }, + 'mapState': {'latitude': 39.5, 'longitude': -98.35, 'zoom': 3.5} + } +} + +map_1 = KeplerGl(height=600, config=config) +map_1.add_data(data=gdf, name='natregimes') +map_1.save_to_html(file_name='natregimes_map.html') +``` + ### Multiple datasets ```python From 789a1923d54b36e22b40f1fee626628cc37831f8 Mon Sep 17 00:00:00 2001 From: Xun Li <lixun910@gmail.com> Date: Sat, 9 May 2026 16:37:10 -0700 Subject: [PATCH 13/17] Add keplergl-map skill files for Python integration - Introduced `cursor-plugin.json` and `plugin.json` to define the keplergl-map skill for interactive map visualizations. - Updated `README.md` to include references to the new summary panel feature. - Added SVG icons for the skill in both small and large sizes. - Created `summary-panel.md` to document the overlay feature for exported HTML maps. - Added `openai.yaml` for agent configuration related to the keplergl-map skill. --- bindings/python/README.md | 5 + bindings/python/SKILL.md | 117 +++--------------- bindings/python/agents/assets/icon-large.svg | 6 + bindings/python/agents/assets/icon-small.svg | 6 + bindings/python/agents/openai.yaml | 13 ++ bindings/python/cursor-plugin.json | 38 ++++++ bindings/python/plugin.json | 28 +++++ .../python/skill-references/summary-panel.md | 99 +++++++++++++++ 8 files changed, 209 insertions(+), 103 deletions(-) create mode 100644 bindings/python/agents/assets/icon-large.svg create mode 100644 bindings/python/agents/assets/icon-small.svg create mode 100644 bindings/python/agents/openai.yaml create mode 100644 bindings/python/cursor-plugin.json create mode 100644 bindings/python/plugin.json create mode 100644 bindings/python/skill-references/summary-panel.md diff --git a/bindings/python/README.md b/bindings/python/README.md index e8452a937d..7128b306ab 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -50,6 +50,7 @@ Detailed per-map-type references (supporting files loaded by Claude only when ne | [heatmap.md](skill-references/heatmap.md) | Density heatmap from points | | [hexbin-aggregation-map.md](skill-references/hexbin-aggregation-map.md) | Spatial binning into hexagons | | [trip-animation-map.md](skill-references/trip-animation-map.md) | Animated trips along paths | +| [summary-panel.md](skill-references/summary-panel.md) | SampleMapPanel-style info overlay in exported HTML | ### Setting up with Claude Code @@ -69,6 +70,7 @@ curl -o ~/.claude/skills/keplergl-map/skill-references/arc-line-map.md curl -o ~/.claude/skills/keplergl-map/skill-references/heatmap.md $BASE/skill-references/heatmap.md curl -o ~/.claude/skills/keplergl-map/skill-references/hexbin-aggregation-map.md $BASE/skill-references/hexbin-aggregation-map.md curl -o ~/.claude/skills/keplergl-map/skill-references/trip-animation-map.md $BASE/skill-references/trip-animation-map.md +curl -o ~/.claude/skills/keplergl-map/skill-references/summary-panel.md $BASE/skill-references/summary-panel.md ``` **Option B — Project skill** (shared with your team via git): @@ -85,6 +87,7 @@ curl -o .claude/skills/keplergl-map/skill-references/arc-line-map.md $ curl -o .claude/skills/keplergl-map/skill-references/heatmap.md $BASE/skill-references/heatmap.md curl -o .claude/skills/keplergl-map/skill-references/hexbin-aggregation-map.md $BASE/skill-references/hexbin-aggregation-map.md curl -o .claude/skills/keplergl-map/skill-references/trip-animation-map.md $BASE/skill-references/trip-animation-map.md +curl -o .claude/skills/keplergl-map/skill-references/summary-panel.md $BASE/skill-references/summary-panel.md git add .claude/skills/keplergl-map git commit -m "Add keplergl map skill for Claude Code" @@ -110,6 +113,7 @@ curl -o ~/.agents/skills/keplergl-map/skill-references/arc-line-map.md curl -o ~/.agents/skills/keplergl-map/skill-references/heatmap.md $BASE/skill-references/heatmap.md curl -o ~/.agents/skills/keplergl-map/skill-references/hexbin-aggregation-map.md $BASE/skill-references/hexbin-aggregation-map.md curl -o ~/.agents/skills/keplergl-map/skill-references/trip-animation-map.md $BASE/skill-references/trip-animation-map.md +curl -o ~/.agents/skills/keplergl-map/skill-references/summary-panel.md $BASE/skill-references/summary-panel.md ``` Then verify `~/.agents/skills/keplergl-map/SKILL.md` frontmatter includes both: @@ -131,6 +135,7 @@ curl -o .agents/skills/keplergl-map/skill-references/arc-line-map.md $ curl -o .agents/skills/keplergl-map/skill-references/heatmap.md $BASE/skill-references/heatmap.md curl -o .agents/skills/keplergl-map/skill-references/hexbin-aggregation-map.md $BASE/skill-references/hexbin-aggregation-map.md curl -o .agents/skills/keplergl-map/skill-references/trip-animation-map.md $BASE/skill-references/trip-animation-map.md +curl -o .agents/skills/keplergl-map/skill-references/summary-panel.md $BASE/skill-references/summary-panel.md git add .agents/skills/keplergl-map git commit -m "Add keplergl map skill for Codex" diff --git a/bindings/python/SKILL.md b/bindings/python/SKILL.md index 420a41cdfa..0ffa7a26fe 100644 --- a/bindings/python/SKILL.md +++ b/bindings/python/SKILL.md @@ -39,20 +39,19 @@ Requires `kepler.gl-jupyter >= 0.4.0`. Earlier versions use a different widget/s | `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)` +### `.add_data(data, name)` - `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`: bool or None — when `True`, serialize DataFrames as Arrow IPC; when `None`, uses the widget-level `use_arrow` setting +- `name`: Dataset identifier — must match `dataId` in config if using a config -### `.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)` +### `.save_to_html(file_name="keplergl_map.html", data=None, config=None, read_only=True, 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 | +| `file_name` | str | required | 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 | +| `read_only` | bool | True | 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 | @@ -74,6 +73,7 @@ Read or set the map configuration dict. Use `map.config` after customizing in Ju - 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](skill-references/summary-panel.md). ## Supported Data Formats @@ -148,10 +148,15 @@ For detailed per-layer-type examples with full config, see supporting files: - [Heatmap](skill-references/heatmap.md) — Density heatmap from points - [Hexbin Aggregation Map](skill-references/hexbin-aggregation-map.md) — Spatial binning into hexagons - [Trip Animation Map](skill-references/trip-animation-map.md) — Animated trips along paths +- [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) ## Examples -### Point map from DataFrame (quantile + vibrant palette) +For full config examples per layer type, see: +- [Point Map](skill-references/point-map.md) — includes quantile color + vibrant palette config +- [GeoJSON / Polygon Map](skill-references/geojson-polygon-map.md) — includes choropleth config with `visualChannels` + +### Quick start (auto-detected layers, no config needed) ```python from keplergl import KeplerGl @@ -164,38 +169,8 @@ df = pd.DataFrame({ 'value': [15, 42, 27] }) -config = { - 'version': 'v1', - 'config': { - 'visState': { - 'layers': [{ - 'type': 'point', - 'config': { - 'dataId': 'cities', - 'label': 'Cities', - 'isVisible': True, - 'columns': {'lat': 'lat', 'lng': 'lng'}, - 'visConfig': { - 'radius': 10, - 'colorRange': { - 'name': 'Global Warming', - 'type': 'sequential', - 'category': 'Uber', - 'colors': ['#5A1846', '#900C3F', '#C70039', '#E3611C', '#F1920E', '#FFC300'] - } - } - }, - 'visualChannels': { - 'colorField': {'name': 'value', 'type': 'integer'}, - 'colorScale': 'quantile' - } - }] - } - } -} - -map_1 = KeplerGl(height=600, data={'cities': df}, config=config) -map_1.save_to_html(file_name='cities_map.html') +map_1 = KeplerGl(data={'cities': df}) +map_1.save_to_html(file_name='cities_map.html', center_map=True) ``` ### GeoDataFrame from shapefile @@ -209,70 +184,6 @@ map_1 = KeplerGl(data={'regions': gdf}) map_1.save_to_html(file_name='regions_map.html', read_only=True, center_map=True) ``` -### GeoJSON from file - -```python -from keplergl import KeplerGl -import json - -with open('boundaries.geojson', 'r') as f: - geojson = json.load(f) -map_1 = KeplerGl(data={'boundaries': geojson}) -map_1.save_to_html(file_name='boundaries_map.html', center_map=True) -``` - -### GeoJSON choropleth colored by a numeric attribute - -Load with geopandas, then put the field-to-channel mappings under `visualChannels` (not `config`): - -```python -import geopandas as gpd -from keplergl import KeplerGl - -gdf = gpd.read_file('natregimes.geojson') - -config = { - 'version': 'v1', - 'config': { - 'visState': { - 'layers': [{ - 'id': 'hr60_layer', - 'type': 'geojson', - 'config': { - 'dataId': 'natregimes', - 'label': 'HR60', - 'columns': {'geojson': '_geojson'}, - 'isVisible': True, - 'visConfig': { - 'opacity': 0.8, - 'stroked': True, - 'filled': True, - 'thickness': 0.2, - 'strokeColor': [80, 80, 80], - 'colorRange': { - 'name': 'Global Warming', - 'type': 'sequential', - 'category': 'Uber', - 'colors': ['#5A1846', '#900C3F', '#C70039', - '#E3611C', '#F1920E', '#FFC300'] - } - } - }, - 'visualChannels': { - 'colorField': {'name': 'HR60', 'type': 'real'}, - 'colorScale': 'quantile' - } - }] - }, - 'mapState': {'latitude': 39.5, 'longitude': -98.35, 'zoom': 3.5} - } -} - -map_1 = KeplerGl(height=600, config=config) -map_1.add_data(data=gdf, name='natregimes') -map_1.save_to_html(file_name='natregimes_map.html') -``` - ### Multiple datasets ```python diff --git a/bindings/python/agents/assets/icon-large.svg b/bindings/python/agents/assets/icon-large.svg new file mode 100644 index 0000000000..ab469458b3 --- /dev/null +++ b/bindings/python/agents/assets/icon-large.svg @@ -0,0 +1,6 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128"> + <g transform="translate(64, 24) rotate(45)"> + <rect fill="#535C6C" x="0" y="20" width="40" height="40"/> + <rect fill="#1FBAD6" x="20" y="0" width="40" height="40"/> + </g> +</svg> diff --git a/bindings/python/agents/assets/icon-small.svg b/bindings/python/agents/assets/icon-small.svg new file mode 100644 index 0000000000..00d4e63f1b --- /dev/null +++ b/bindings/python/agents/assets/icon-small.svg @@ -0,0 +1,6 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> + <g transform="translate(16, 6) rotate(45)"> + <rect fill="#535C6C" x="0" y="5" width="10" height="10"/> + <rect fill="#1FBAD6" x="5" y="0" width="10" height="10"/> + </g> +</svg> diff --git a/bindings/python/agents/openai.yaml b/bindings/python/agents/openai.yaml new file mode 100644 index 0000000000..14de76a339 --- /dev/null +++ b/bindings/python/agents/openai.yaml @@ -0,0 +1,13 @@ +interface: + display_name: "kepler.gl Map" + short_description: "Create interactive map visualizations with kepler.gl" + icon_small: "./assets/icon-small.svg" + icon_large: "./assets/icon-large.svg" + brand_color: "#1FBAD6" + default_prompt: "Create an interactive map visualization" + +policy: + allow_implicit_invocation: true + +dependencies: + tools: [] diff --git a/bindings/python/cursor-plugin.json b/bindings/python/cursor-plugin.json new file mode 100644 index 0000000000..fcbe464201 --- /dev/null +++ b/bindings/python/cursor-plugin.json @@ -0,0 +1,38 @@ +{ + "name": "keplergl-map", + "displayName": "kepler.gl Map", + "version": "1.0.0", + "description": "Create interactive map visualizations and export to standalone HTML using the keplergl Python package.", + "publisher": "keplergl", + "icon": "agents/assets/icon-large.svg", + "categories": ["Data Science", "Visualization", "Maps"], + "keywords": [ + "maps", + "geospatial", + "visualization", + "kepler.gl", + "GeoJSON", + "DataFrame", + "HTML", + "interactive", + "choropleth", + "heatmap" + ], + "homepage": "https://kepler.gl", + "repository": { + "type": "git", + "url": "https://github.com/keplergl/kepler.gl", + "directory": "bindings/python" + }, + "license": "MIT", + "skills": [ + { + "path": "SKILL.md", + "name": "keplergl-map", + "description": "Create interactive map visualizations and export to standalone HTML using the keplergl Python package." + } + ], + "engines": { + "cursor": ">=1.0.0" + } +} diff --git a/bindings/python/plugin.json b/bindings/python/plugin.json new file mode 100644 index 0000000000..144e9f1483 --- /dev/null +++ b/bindings/python/plugin.json @@ -0,0 +1,28 @@ +{ + "name": "keplergl-map", + "version": "1.0.0", + "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.", + "author": "kepler.gl", + "license": "MIT", + "homepage": "https://kepler.gl", + "repository": { + "type": "git", + "url": "https://github.com/keplergl/kepler.gl", + "directory": "bindings/python" + }, + "skills": [ + { + "path": "./SKILL.md" + } + ], + "keywords": [ + "maps", + "geospatial", + "visualization", + "kepler.gl", + "GeoJSON", + "DataFrame", + "HTML", + "interactive" + ] +} diff --git a/bindings/python/skill-references/summary-panel.md b/bindings/python/skill-references/summary-panel.md new file mode 100644 index 0000000000..28a6be1386 --- /dev/null +++ b/bindings/python/skill-references/summary-panel.md @@ -0,0 +1,99 @@ +# Summary Panel Overlay (SampleMapPanel-style) + +Add a small informational panel on top of a kepler.gl map exported via `save_to_html()` — useful for summary stats, legends of custom classifications, model metadata, or LISA / cluster counts. + +## Why not a real React component? + +The `SampleMapPanel` used in the kepler.gl demo app is part of that demo's source and is **not** exposed by the UMD bundle loaded in the standalone HTML export. The pragmatic equivalent is to inject an HTML/CSS overlay into the exported file after calling `save_to_html()`. + +## Positioning + +The map's zoom/rotate/split/legend control column sits against the right edge of the viewport and is roughly 40–50px wide. Place the panel at `right: 56px` (or `left: 66px` to sit next to the side panel toggle) so it does not block those controls. + +| Placement | CSS | +|-----------|-----| +| Top-right, clear of map controls | `top: 16px; right: 56px;` | +| Top-left, clear of side-panel toggle | `top: 16px; left: 66px;` | +| Bottom-left, above attribution | `bottom: 36px; left: 16px;` | + +## Pattern + +1. Call `map.save_to_html(file_name='out.html')` as usual. +2. Build a small `<div>` + `<style>` snippet in Python. +3. Read the file, insert the snippet just before `</body>`, write it back. + +## Example: LISA category counts overlay + +```python +from collections import Counter + +# Assume gdf has columns: LISA_LABEL (str), and `labels` / `colors` come from +# a pygeoda LISA result (lm.lisa_labels(), lm.lisa_colors()). +order = ["High-High", "Low-Low", "Low-High", "High-Low", "Not significant"] +label_to_color = {labels[i]: colors[i] for i in range(len(labels))} +counts = Counter(gdf["LISA_LABEL"]) +total = sum(counts.values()) +sig = sum(n for lbl, n in counts.items() if lbl != "Not significant") + +rows = "" +for lbl in order: + n = counts.get(lbl, 0) + pct = 100 * n / total if total else 0 + swatch = label_to_color.get(lbl, "#888") + rows += ( + f'<div class="lisa-row">' + f'<span class="lisa-swatch" style="background:{swatch}"></span>' + f'<span class="lisa-label">{lbl}</span>' + f'<span class="lisa-count">{n:,} ({pct:.1f}%)</span>' + f'</div>' + ) + +panel_html = f""" +<div id="sample-map-panel"> + <div class="smp-header">Local Moran's I — HR60</div> + <div class="smp-sub">Queen contiguity · 999 permutations · p ≤ 0.05</div> + <div class="smp-divider"></div> + <div class="smp-stats"> + <div class="smp-stat"><span class="smp-stat-num">{total:,}</span><span class="smp-stat-lbl">Counties</span></div> + <div class="smp-stat"><span class="smp-stat-num">{sig:,}</span><span class="smp-stat-lbl">Significant</span></div> + <div class="smp-stat"><span class="smp-stat-num">{100*sig/total:.1f}%</span><span class="smp-stat-lbl">Sig. share</span></div> + </div> + <div class="smp-divider"></div> + <div class="smp-rows">{rows}</div> +</div> +<style> + #sample-map-panel {{ + position: absolute; top: 16px; right: 56px; z-index: 1000; + width: 280px; padding: 14px 16px; + background: rgba(36, 39, 48, 0.92); color: #f7f7f7; + font-family: ff-clan-web-pro, 'Helvetica Neue', Helvetica, sans-serif; + font-size: 12px; border-radius: 4px; + box-shadow: 0 2px 8px rgba(0,0,0,0.4); + }} + #sample-map-panel .smp-header {{ font-size: 14px; font-weight: 600; margin-bottom: 2px; }} + #sample-map-panel .smp-sub {{ font-size: 11px; color: #a0a7b4; }} + #sample-map-panel .smp-divider {{ height: 1px; background: #3a3f4b; margin: 10px 0; }} + #sample-map-panel .smp-stats {{ display: flex; justify-content: space-between; }} + #sample-map-panel .smp-stat {{ display: flex; flex-direction: column; }} + #sample-map-panel .smp-stat-num {{ font-size: 16px; font-weight: 600; color: #fff; }} + #sample-map-panel .smp-stat-lbl {{ font-size: 10px; color: #a0a7b4; text-transform: uppercase; letter-spacing: 0.5px; }} + #sample-map-panel .lisa-row {{ display: flex; align-items: center; padding: 3px 0; }} + #sample-map-panel .lisa-swatch {{ width: 12px; height: 12px; margin-right: 8px; border-radius: 2px; flex: 0 0 auto; }} + #sample-map-panel .lisa-label {{ flex: 1; color: #e0e2e7; }} + #sample-map-panel .lisa-count {{ color: #a0a7b4; font-variant-numeric: tabular-nums; }} +</style> +""" + +with open("out.html", "r", encoding="utf-8") as f: + html = f.read() +html = html.replace("</body>", panel_html + "\n</body>") +with open("out.html", "w", encoding="utf-8") as f: + f.write(html) +``` + +## Notes + +- Use `position: absolute` (not `fixed`) so the panel stays inside the map container when the page is scrolled. +- For a light-theme map (`theme='light'`), swap the background to `rgba(255,255,255,0.95)` and text colors to dark greys. +- Keep the width ≤ 320px to avoid overlapping the kepler.gl side panel when it is open. +- This overlay is static HTML — it does not react to filter state or layer toggles. For dynamic behavior, subscribe to the redux store on `window` in the exported JS. From ede9f377a885520080297bafebcb738f4d681a56 Mon Sep 17 00:00:00 2001 From: Xun Li <lixun910@gmail.com> Date: Sat, 9 May 2026 18:50:40 -0700 Subject: [PATCH 14/17] Update README.md and SKILL.md for keplergl-map skill - Expanded the setup instructions in README.md to include Codex alongside Claude Code. - Modified the `.save_to_html` method in SKILL.md to set `read_only` to false by default, enhancing usability. --- bindings/python/README.md | 181 ++------------------------------------ bindings/python/SKILL.md | 2 +- 2 files changed, 7 insertions(+), 176 deletions(-) diff --git a/bindings/python/README.md b/bindings/python/README.md index 7128b306ab..4c9db2a711 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -52,141 +52,18 @@ Detailed per-map-type references (supporting files loaded by Claude only when ne | [trip-animation-map.md](skill-references/trip-animation-map.md) | Animated trips along paths | | [summary-panel.md](skill-references/summary-panel.md) | SampleMapPanel-style info overlay in exported HTML | -### Setting up with Claude Code +### Setting up with Claude Code or Codex -Claude Code skills are stored as directories containing a `SKILL.md` file. You can install this skill at the **personal** level (available across all projects) or the **project** level (shared with your team via git). +The easiest way to get started is to simply prompt your AI agent: -**Option A — Personal skill** (available in all your projects): +> Help me installing keplergl-map skill from github kepler.gl repo -```bash -BASE=https://raw.githubusercontent.com/keplergl/kepler.gl/main/bindings/python -mkdir -p ~/.claude/skills/keplergl-map/skill-references - -curl -o ~/.claude/skills/keplergl-map/SKILL.md $BASE/SKILL.md -curl -o ~/.claude/skills/keplergl-map/skill-references/point-map.md $BASE/skill-references/point-map.md -curl -o ~/.claude/skills/keplergl-map/skill-references/geojson-polygon-map.md $BASE/skill-references/geojson-polygon-map.md -curl -o ~/.claude/skills/keplergl-map/skill-references/h3-hexagon-map.md $BASE/skill-references/h3-hexagon-map.md -curl -o ~/.claude/skills/keplergl-map/skill-references/arc-line-map.md $BASE/skill-references/arc-line-map.md -curl -o ~/.claude/skills/keplergl-map/skill-references/heatmap.md $BASE/skill-references/heatmap.md -curl -o ~/.claude/skills/keplergl-map/skill-references/hexbin-aggregation-map.md $BASE/skill-references/hexbin-aggregation-map.md -curl -o ~/.claude/skills/keplergl-map/skill-references/trip-animation-map.md $BASE/skill-references/trip-animation-map.md -curl -o ~/.claude/skills/keplergl-map/skill-references/summary-panel.md $BASE/skill-references/summary-panel.md -``` - -**Option B — Project skill** (shared with your team via git): - -```bash -BASE=https://raw.githubusercontent.com/keplergl/kepler.gl/main/bindings/python -mkdir -p .claude/skills/keplergl-map/skill-references - -curl -o .claude/skills/keplergl-map/SKILL.md $BASE/SKILL.md -curl -o .claude/skills/keplergl-map/skill-references/point-map.md $BASE/skill-references/point-map.md -curl -o .claude/skills/keplergl-map/skill-references/geojson-polygon-map.md $BASE/skill-references/geojson-polygon-map.md -curl -o .claude/skills/keplergl-map/skill-references/h3-hexagon-map.md $BASE/skill-references/h3-hexagon-map.md -curl -o .claude/skills/keplergl-map/skill-references/arc-line-map.md $BASE/skill-references/arc-line-map.md -curl -o .claude/skills/keplergl-map/skill-references/heatmap.md $BASE/skill-references/heatmap.md -curl -o .claude/skills/keplergl-map/skill-references/hexbin-aggregation-map.md $BASE/skill-references/hexbin-aggregation-map.md -curl -o .claude/skills/keplergl-map/skill-references/trip-animation-map.md $BASE/skill-references/trip-animation-map.md -curl -o .claude/skills/keplergl-map/skill-references/summary-panel.md $BASE/skill-references/summary-panel.md - -git add .claude/skills/keplergl-map -git commit -m "Add keplergl map skill for Claude Code" -``` - -Once installed, Claude Code automatically discovers the skill — no restart needed. Claude will use it whenever you ask about creating maps or visualizing geospatial data, or you can invoke it directly with `/keplergl-map`. - -### Setting up with Codex - -Codex skills are stored as directories containing a `SKILL.md` file. - -**Option A — Personal skill** (available in all your projects): - -```bash -BASE=https://raw.githubusercontent.com/keplergl/kepler.gl/main/bindings/python -mkdir -p ~/.agents/skills/keplergl-map/skill-references - -curl -o ~/.agents/skills/keplergl-map/SKILL.md $BASE/SKILL.md -curl -o ~/.agents/skills/keplergl-map/skill-references/point-map.md $BASE/skill-references/point-map.md -curl -o ~/.agents/skills/keplergl-map/skill-references/geojson-polygon-map.md $BASE/skill-references/geojson-polygon-map.md -curl -o ~/.agents/skills/keplergl-map/skill-references/h3-hexagon-map.md $BASE/skill-references/h3-hexagon-map.md -curl -o ~/.agents/skills/keplergl-map/skill-references/arc-line-map.md $BASE/skill-references/arc-line-map.md -curl -o ~/.agents/skills/keplergl-map/skill-references/heatmap.md $BASE/skill-references/heatmap.md -curl -o ~/.agents/skills/keplergl-map/skill-references/hexbin-aggregation-map.md $BASE/skill-references/hexbin-aggregation-map.md -curl -o ~/.agents/skills/keplergl-map/skill-references/trip-animation-map.md $BASE/skill-references/trip-animation-map.md -curl -o ~/.agents/skills/keplergl-map/skill-references/summary-panel.md $BASE/skill-references/summary-panel.md -``` - -Then verify `~/.agents/skills/keplergl-map/SKILL.md` frontmatter includes both: - -- `name: keplergl-map` -- `description: ...` - -**Option B — Project skill** (shared with your team via git): - -```bash -BASE=https://raw.githubusercontent.com/keplergl/kepler.gl/main/bindings/python -mkdir -p .agents/skills/keplergl-map/skill-references - -curl -o .agents/skills/keplergl-map/SKILL.md $BASE/SKILL.md -curl -o .agents/skills/keplergl-map/skill-references/point-map.md $BASE/skill-references/point-map.md -curl -o .agents/skills/keplergl-map/skill-references/geojson-polygon-map.md $BASE/skill-references/geojson-polygon-map.md -curl -o .agents/skills/keplergl-map/skill-references/h3-hexagon-map.md $BASE/skill-references/h3-hexagon-map.md -curl -o .agents/skills/keplergl-map/skill-references/arc-line-map.md $BASE/skill-references/arc-line-map.md -curl -o .agents/skills/keplergl-map/skill-references/heatmap.md $BASE/skill-references/heatmap.md -curl -o .agents/skills/keplergl-map/skill-references/hexbin-aggregation-map.md $BASE/skill-references/hexbin-aggregation-map.md -curl -o .agents/skills/keplergl-map/skill-references/trip-animation-map.md $BASE/skill-references/trip-animation-map.md -curl -o .agents/skills/keplergl-map/skill-references/summary-panel.md $BASE/skill-references/summary-panel.md - -git add .agents/skills/keplergl-map -git commit -m "Add keplergl map skill for Codex" -``` - -Then verify `.agents/skills/keplergl-map/SKILL.md` frontmatter includes both: - -- `name: keplergl-map` -- `description: ...` - -#### Optional Codex app metadata and icon assets - -Inside each Codex skill folder, add metadata at `agents/openai.yaml` and assets under `agents/assets/`. -That means: - -- Personal install: `~/.agents/skills/keplergl-map/agents/openai.yaml` -- Project install: `.agents/skills/keplergl-map/agents/openai.yaml` - -Example asset setup: - -```bash -# Project-level -mkdir -p .agents/skills/keplergl-map/agents/assets -curl -o .agents/skills/keplergl-map/agents/assets/kepler-gl-icon.png \ - https://raw.githubusercontent.com/keplergl/kepler.gl/main/website/src/static/favicon.png - -# Personal-level -mkdir -p ~/.agents/skills/keplergl-map/agents/assets -curl -o ~/.agents/skills/keplergl-map/agents/assets/kepler-gl-icon.png \ - https://raw.githubusercontent.com/keplergl/kepler.gl/main/website/src/static/favicon.png -``` - -```yaml -interface: - display_name: "keplergl map" - short_description: "Create interactive keplergl HTML maps from tabular and geospatial data" - icon_small: "./assets/kepler-gl-icon.png" - icon_large: "./assets/kepler-gl-icon.png" - brand_color: "#2FA7F4" -``` - -#### Easiest distribution for Codex users - -For easy installation across teams, package this skill as a **Codex plugin** (recommended). Plugins can bundle skills, metadata (`agents/openai.yaml`), assets, and optional integrations in one installable package. -For local experimentation only, Codex users can install curated skills with the built-in `$skill-installer` command (see https://developers.openai.com/codex/skills). +The agent will locate the skill file and set it up for you automatically. ### Example prompt -> Create a polygon map from a GeoDataFrame. I have a shapefile at `./data/sf_neighborhoods.shp` +> create a dark theme map showing distribution of HR60 from the natregimes.geojson . I have a shapefile at `./data/natregimes.shp` > with columns `name` and `population`. Color the polygons by population, use a light theme, -> and save it as a static HTML file I can open in my browser. ### Example output @@ -196,53 +73,7 @@ Claude will generate a Python script and run it, producing output like: Map saved to sf_neighborhoods.html! ``` -The generated script will look similar to: - -```python -from keplergl import KeplerGl -import geopandas as gpd - -gdf = gpd.read_file('./data/sf_neighborhoods.shp') - -config = { - 'version': 'v1', - 'config': { - 'visState': { - 'layers': [{ - 'type': 'geojson', - 'config': { - 'dataId': 'neighborhoods', - 'label': 'SF Neighborhoods', - 'isVisible': True, - 'columns': {'geojson': '_geojson'}, - 'visConfig': { - 'opacity': 0.8, - 'filled': True, - 'stroked': True, - 'colorRange': { - 'name': 'Global Warming', - 'type': 'sequential', - 'category': 'Uber', - 'colors': ['#5A1846','#900C3F','#C70039','#E3611C','#F1920E','#FFC300'] - } - } - }, - 'visualChannels': { - 'colorField': {'name': 'population', 'type': 'integer'}, - 'colorScale': 'quantile' - } - }] - }, - 'mapStyle': {'styleType': 'positron'} - } -} - -map_1 = KeplerGl(data={'neighborhoods': gdf}, config=config, theme='light') -map_1.save_to_html(file_name='sf_neighborhoods.html', center_map=True, read_only=True) -print("Map saved to sf_neighborhoods.html!") -``` - -Open `sf_neighborhoods.html` in any browser to see the interactive map — no server required. +You can ask claude or codex to open the html in any browser to see the interactive map — no server required. ## Documentation diff --git a/bindings/python/SKILL.md b/bindings/python/SKILL.md index 0ffa7a26fe..7e5855bc9a 100644 --- a/bindings/python/SKILL.md +++ b/bindings/python/SKILL.md @@ -44,7 +44,7 @@ Requires `kepler.gl-jupyter >= 0.4.0`. Earlier versions use a different widget/s - `data`: DataFrame, GeoDataFrame, CSV string, GeoJSON dict, or GeoJSON string - `name`: Dataset identifier — must match `dataId` in config if using a config -### `.save_to_html(file_name="keplergl_map.html", data=None, config=None, read_only=True, center_map=True, mapbox_token="", json_encoder=str, app_name=None, theme=None)` +### `.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 | |-----------|------|---------|-------------| From 90c91ab0c863fb6555d5348292feaee1c56df5ee Mon Sep 17 00:00:00 2001 From: Xun Li <lixun910@gmail.com> Date: Sat, 9 May 2026 19:17:23 -0700 Subject: [PATCH 15/17] Remove deprecated keplergl-map skill files and assets - Deleted `cursor-plugin.json`, `plugin.json`, and `SKILL.md` as they are no longer needed. - Removed associated SVG icons and YAML configuration for the OpenAI agent. - Cleaned up the README.md by removing references to the now-removed files and features. --- .github/workflows/publish-skill.yml | 72 +++++++++++++++++++ bindings/python/README.md | 41 +---------- skill/README.md | 35 +++++++++ {bindings/python => skill}/SKILL.md | 2 +- .../agents/assets/icon-large.svg | 0 .../agents/assets/icon-small.svg | 0 {bindings/python => skill}/agents/openai.yaml | 0 {bindings/python => skill}/cursor-plugin.json | 4 +- {bindings/python => skill}/plugin.json | 2 +- .../skill-references/arc-line-map.md | 0 .../skill-references/geojson-polygon-map.md | 0 .../skill-references/h3-hexagon-map.md | 0 .../skill-references/heatmap.md | 0 .../hexbin-aggregation-map.md | 0 .../skill-references/point-map.md | 0 .../skill-references/summary-panel.md | 0 .../skill-references/trip-animation-map.md | 0 17 files changed, 112 insertions(+), 44 deletions(-) create mode 100644 .github/workflows/publish-skill.yml create mode 100644 skill/README.md rename {bindings/python => skill}/SKILL.md (99%) rename {bindings/python => skill}/agents/assets/icon-large.svg (100%) rename {bindings/python => skill}/agents/assets/icon-small.svg (100%) rename {bindings/python => skill}/agents/openai.yaml (100%) rename {bindings/python => skill}/cursor-plugin.json (94%) rename {bindings/python => skill}/plugin.json (96%) rename {bindings/python => skill}/skill-references/arc-line-map.md (100%) rename {bindings/python => skill}/skill-references/geojson-polygon-map.md (100%) rename {bindings/python => skill}/skill-references/h3-hexagon-map.md (100%) rename {bindings/python => skill}/skill-references/heatmap.md (100%) rename {bindings/python => skill}/skill-references/hexbin-aggregation-map.md (100%) rename {bindings/python => skill}/skill-references/point-map.md (100%) rename {bindings/python => skill}/skill-references/summary-panel.md (100%) rename {bindings/python => skill}/skill-references/trip-animation-map.md (100%) diff --git a/.github/workflows/publish-skill.yml b/.github/workflows/publish-skill.yml new file mode 100644 index 0000000000..dbeef3d2cd --- /dev/null +++ b/.github/workflows/publish-skill.yml @@ -0,0 +1,72 @@ +name: Publish kepler.gl Skill + +on: + push: + paths: + - 'skill/SKILL.md' + - 'skill/skill-references/**' + - 'skill/agents/**' + - 'skill/plugin.json' + tags: + - 'skill-v*' + workflow_dispatch: + +jobs: + build-skills: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + + - name: Read skill version + id: version + run: | + VERSION=$(python3 -c "import json; print(json.load(open('skill/plugin.json'))['version'])") + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Build Claude skill package + run: | + STAGING=$(mktemp -d) + SKILL_DIR="$STAGING/kepler.gl" + mkdir -p "$SKILL_DIR" + + cp skill/SKILL.md "$SKILL_DIR/SKILL.md" + cp -r skill/skill-references "$SKILL_DIR/skill-references" + + (cd "$STAGING" && zip -r "$OLDPWD/kepler.gl-claude.zip" kepler.gl) + rm -rf "$STAGING" + + - name: Build Codex skill package + run: | + STAGING=$(mktemp -d) + SKILL_DIR="$STAGING/kepler.gl" + mkdir -p "$SKILL_DIR" + + cp skill/SKILL.md "$SKILL_DIR/SKILL.md" + cp -r skill/skill-references "$SKILL_DIR/skill-references" + cp -r skill/agents "$SKILL_DIR/agents" + + (cd "$STAGING" && zip -r "$OLDPWD/kepler.gl-codex.zip" kepler.gl) + rm -rf "$STAGING" + + - name: Upload Claude skill artifact + uses: actions/upload-artifact@v4 + with: + name: kepler.gl-claude-v${{ steps.version.outputs.version }} + path: kepler.gl-claude.zip + + - name: Upload Codex skill artifact + uses: actions/upload-artifact@v4 + with: + name: kepler.gl-codex-v${{ steps.version.outputs.version }} + path: kepler.gl-codex.zip + + - name: Attach packages to release + if: startsWith(github.ref, 'refs/tags/skill-v') + uses: softprops/action-gh-release@v2 + with: + files: | + kepler.gl-claude.zip + kepler.gl-codex.zip diff --git a/bindings/python/README.md b/bindings/python/README.md index 4c9db2a711..0c1c1c3684 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -34,46 +34,7 @@ map ## Use with AI Coding Assistants -This package ships with a **[SKILL.md](SKILL.md)** file for AI assistants to generate `keplergl` map scripts and exports consistently. - -- [Claude Code](https://code.claude.com/docs/en/skills): install under `~/.claude/skills` or `.claude/skills` -- [Codex](https://developers.openai.com/codex/): install under `~/.agents/skills` or `.agents/skills` - -Detailed per-map-type references (supporting files loaded by Claude only when needed): - -| Reference | Description | -|-----------|-------------| -| [point-map.md](skill-references/point-map.md) | Scatter plot from lat/lng | -| [geojson-polygon-map.md](skill-references/geojson-polygon-map.md) | Polygons & lines from GeoJSON / GeoDataFrame | -| [h3-hexagon-map.md](skill-references/h3-hexagon-map.md) | H3 spatial index hexagons | -| [arc-line-map.md](skill-references/arc-line-map.md) | Origin–destination arcs & lines | -| [heatmap.md](skill-references/heatmap.md) | Density heatmap from points | -| [hexbin-aggregation-map.md](skill-references/hexbin-aggregation-map.md) | Spatial binning into hexagons | -| [trip-animation-map.md](skill-references/trip-animation-map.md) | Animated trips along paths | -| [summary-panel.md](skill-references/summary-panel.md) | SampleMapPanel-style info overlay in exported HTML | - -### Setting up with Claude Code or Codex - -The easiest way to get started is to simply prompt your AI agent: - -> Help me installing keplergl-map skill from github kepler.gl repo - -The agent will locate the skill file and set it up for you automatically. - -### Example prompt - -> create a dark theme map showing distribution of HR60 from the natregimes.geojson . I have a shapefile at `./data/natregimes.shp` -> with columns `name` and `population`. Color the polygons by population, use a light theme, - -### Example output - -Claude will generate a Python script and run it, producing output like: - -``` -Map saved to sf_neighborhoods.html! -``` - -You can ask claude or codex to open the html in any browser to see the interactive map — no server required. +This package works with AI coding assistants via the **kepler.gl skill**. See [`skill/`](../../skill/) for installation instructions and reference documentation. ## Documentation diff --git a/skill/README.md b/skill/README.md new file mode 100644 index 0000000000..d24405c135 --- /dev/null +++ b/skill/README.md @@ -0,0 +1,35 @@ +# kepler.gl Skill for AI Coding Assistants + +This directory contains the **kepler.gl** agent skill — a set of instructions and references that help AI coding assistants (Claude Code, Codex, Cursor, etc.) generate `keplergl` map scripts and exports consistently. + +## Skill References + +| Reference | Description | +|-----------|-------------| +| [point-map.md](skill-references/point-map.md) | Scatter plot from lat/lng | +| [geojson-polygon-map.md](skill-references/geojson-polygon-map.md) | Polygons & lines from GeoJSON / GeoDataFrame | +| [h3-hexagon-map.md](skill-references/h3-hexagon-map.md) | H3 spatial index hexagons | +| [arc-line-map.md](skill-references/arc-line-map.md) | Origin–destination arcs & lines | +| [heatmap.md](skill-references/heatmap.md) | Density heatmap from points | +| [hexbin-aggregation-map.md](skill-references/hexbin-aggregation-map.md) | Spatial binning into hexagons | +| [trip-animation-map.md](skill-references/trip-animation-map.md) | Animated trips along paths | +| [summary-panel.md](skill-references/summary-panel.md) | SampleMapPanel-style info overlay in exported HTML | + +## Quick Setup via AI Agent + +The easiest way to get started is to prompt your AI agent: + +> Help me install the kepler.gl skill from the kepler.gl GitHub repo + +The agent will locate the skill file and set it up for you automatically. + +## Example Prompt + +> Create a map showing distribution of HR60 from natregimes.geojson. +> Color the polygons by population, use a light theme. + +The agent will generate a Python script and run it, producing a standalone HTML map file you can open in any browser. + +## Versioning + +The skill version is tracked in [`plugin.json`](plugin.json). Releases use the tag pattern `skill-v*` (e.g. `skill-v1.0.0`). diff --git a/bindings/python/SKILL.md b/skill/SKILL.md similarity index 99% rename from bindings/python/SKILL.md rename to skill/SKILL.md index 7e5855bc9a..b53ac57fdd 100644 --- a/bindings/python/SKILL.md +++ b/skill/SKILL.md @@ -1,5 +1,5 @@ --- -name: keplergl-map +name: kepler.gl 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. --- diff --git a/bindings/python/agents/assets/icon-large.svg b/skill/agents/assets/icon-large.svg similarity index 100% rename from bindings/python/agents/assets/icon-large.svg rename to skill/agents/assets/icon-large.svg diff --git a/bindings/python/agents/assets/icon-small.svg b/skill/agents/assets/icon-small.svg similarity index 100% rename from bindings/python/agents/assets/icon-small.svg rename to skill/agents/assets/icon-small.svg diff --git a/bindings/python/agents/openai.yaml b/skill/agents/openai.yaml similarity index 100% rename from bindings/python/agents/openai.yaml rename to skill/agents/openai.yaml diff --git a/bindings/python/cursor-plugin.json b/skill/cursor-plugin.json similarity index 94% rename from bindings/python/cursor-plugin.json rename to skill/cursor-plugin.json index fcbe464201..dd58b1e796 100644 --- a/bindings/python/cursor-plugin.json +++ b/skill/cursor-plugin.json @@ -1,5 +1,5 @@ { - "name": "keplergl-map", + "name": "kepler.gl", "displayName": "kepler.gl Map", "version": "1.0.0", "description": "Create interactive map visualizations and export to standalone HTML using the keplergl Python package.", @@ -28,7 +28,7 @@ "skills": [ { "path": "SKILL.md", - "name": "keplergl-map", + "name": "kepler.gl", "description": "Create interactive map visualizations and export to standalone HTML using the keplergl Python package." } ], diff --git a/bindings/python/plugin.json b/skill/plugin.json similarity index 96% rename from bindings/python/plugin.json rename to skill/plugin.json index 144e9f1483..4c7ed25a8e 100644 --- a/bindings/python/plugin.json +++ b/skill/plugin.json @@ -1,5 +1,5 @@ { - "name": "keplergl-map", + "name": "kepler.gl", "version": "1.0.0", "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.", "author": "kepler.gl", diff --git a/bindings/python/skill-references/arc-line-map.md b/skill/skill-references/arc-line-map.md similarity index 100% rename from bindings/python/skill-references/arc-line-map.md rename to skill/skill-references/arc-line-map.md diff --git a/bindings/python/skill-references/geojson-polygon-map.md b/skill/skill-references/geojson-polygon-map.md similarity index 100% rename from bindings/python/skill-references/geojson-polygon-map.md rename to skill/skill-references/geojson-polygon-map.md diff --git a/bindings/python/skill-references/h3-hexagon-map.md b/skill/skill-references/h3-hexagon-map.md similarity index 100% rename from bindings/python/skill-references/h3-hexagon-map.md rename to skill/skill-references/h3-hexagon-map.md diff --git a/bindings/python/skill-references/heatmap.md b/skill/skill-references/heatmap.md similarity index 100% rename from bindings/python/skill-references/heatmap.md rename to skill/skill-references/heatmap.md diff --git a/bindings/python/skill-references/hexbin-aggregation-map.md b/skill/skill-references/hexbin-aggregation-map.md similarity index 100% rename from bindings/python/skill-references/hexbin-aggregation-map.md rename to skill/skill-references/hexbin-aggregation-map.md diff --git a/bindings/python/skill-references/point-map.md b/skill/skill-references/point-map.md similarity index 100% rename from bindings/python/skill-references/point-map.md rename to skill/skill-references/point-map.md diff --git a/bindings/python/skill-references/summary-panel.md b/skill/skill-references/summary-panel.md similarity index 100% rename from bindings/python/skill-references/summary-panel.md rename to skill/skill-references/summary-panel.md diff --git a/bindings/python/skill-references/trip-animation-map.md b/skill/skill-references/trip-animation-map.md similarity index 100% rename from bindings/python/skill-references/trip-animation-map.md rename to skill/skill-references/trip-animation-map.md From 35a8d3fd4a0045c4563d844d243bcffc07fdd5a7 Mon Sep 17 00:00:00 2001 From: Xun Li <lixun910@gmail.com> Date: Sat, 9 May 2026 19:21:34 -0700 Subject: [PATCH 16/17] Update publish workflow and plugin version - Added a step to build and zip the source package for the kepler.gl skill in the GitHub Actions workflow. - Updated the version in `plugin.json` from 1.0.0 to 0.0.1 to reflect a new release. --- .github/workflows/publish-skill.yml | 5 +++++ skill/plugin.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-skill.yml b/.github/workflows/publish-skill.yml index dbeef3d2cd..bd10b1bcf2 100644 --- a/.github/workflows/publish-skill.yml +++ b/.github/workflows/publish-skill.yml @@ -63,6 +63,10 @@ jobs: name: kepler.gl-codex-v${{ steps.version.outputs.version }} path: kepler.gl-codex.zip + - name: Build source package + run: | + zip -r kepler.gl-skill-source.zip skill/ + - name: Attach packages to release if: startsWith(github.ref, 'refs/tags/skill-v') uses: softprops/action-gh-release@v2 @@ -70,3 +74,4 @@ jobs: files: | kepler.gl-claude.zip kepler.gl-codex.zip + kepler.gl-skill-source.zip diff --git a/skill/plugin.json b/skill/plugin.json index 4c7ed25a8e..9153f8ab53 100644 --- a/skill/plugin.json +++ b/skill/plugin.json @@ -1,6 +1,6 @@ { "name": "kepler.gl", - "version": "1.0.0", + "version": "0.0.1", "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.", "author": "kepler.gl", "license": "MIT", From f8af8554593cd80802a6c41547173ceec6706b45 Mon Sep 17 00:00:00 2001 From: Xun Li <lixun910@gmail.com> Date: Sat, 9 May 2026 19:26:03 -0700 Subject: [PATCH 17/17] Update README.md to include direct download links for kepler.gl skill - Added specific download links for the kepler.gl skill for Claude, Codex, and source versions. - Clarified installation instructions for better user guidance. --- skill/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/skill/README.md b/skill/README.md index d24405c135..72f489d47b 100644 --- a/skill/README.md +++ b/skill/README.md @@ -19,7 +19,13 @@ This directory contains the **kepler.gl** agent skill — a set of instructions The easiest way to get started is to prompt your AI agent: -> Help me install the kepler.gl skill from the kepler.gl GitHub repo +> Help me install the kepler.gl skill from + +- claude: https://github.com/keplergl/kepler.gl/releases/download/skill-v0.0.1/kepler.gl-claude.zip +- codex: https://github.com/keplergl/kepler.gl/releases/download/skill-v0.0.1/kepler.gl-codex.zip +- source: https://github.com/keplergl/kepler.gl/releases/download/skill-v0.0.1/kepler.gl-skill-source.zip + +or just "kepler.gl github repo" The agent will locate the skill file and set it up for you automatically.