@kepler.gl/mcp 0.0.0-stage → 3.3.0-alpha.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/commands/add-column-command.js +192 -0
- package/dist/cjs/commands/basemap-command.js +80 -0
- package/dist/cjs/commands/boundary-command.js +81 -0
- package/dist/cjs/commands/command-wrappers.js +122 -0
- package/dist/cjs/commands/dataset-context-command.js +87 -0
- package/dist/cjs/commands/index.js +230 -0
- package/dist/cjs/commands/layer-creation-command.js +534 -0
- package/dist/cjs/commands/layer-style-command.js +136 -0
- package/dist/cjs/commands/load-data-command.js +270 -0
- package/dist/cjs/commands/save-data-command.js +118 -0
- package/dist/cjs/commands/split-view-command.js +261 -0
- package/dist/cjs/commands/table-command.js +199 -0
- package/dist/cjs/commands/time-filter-command.js +289 -0
- package/dist/cjs/commands/toggle-time-filter-command.js +155 -0
- package/dist/cjs/commands/types.js +6 -0
- package/dist/cjs/commands/utils.js +536 -0
- package/dist/cjs/commands/view-mode-command.js +94 -0
- package/dist/cjs/index.js +39 -0
- package/dist/cjs/map-contract.js +6 -0
- package/dist/cjs/server/bridge-server.js +435 -0
- package/dist/cjs/server/index.js +31 -0
- package/dist/cjs/server/stdio.js +201 -0
- package/dist/cjs/types.js +30 -0
- package/dist/commands/add-column-command.d.ts +4 -0
- package/dist/commands/basemap-command.d.ts +4 -0
- package/dist/commands/boundary-command.d.ts +4 -0
- package/dist/commands/command-wrappers.d.ts +52 -0
- package/dist/commands/dataset-context-command.d.ts +4 -0
- package/dist/commands/index.d.ts +31 -0
- package/dist/commands/layer-creation-command.d.ts +8 -0
- package/dist/commands/layer-style-command.d.ts +4 -0
- package/dist/commands/load-data-command.d.ts +4 -0
- package/dist/commands/save-data-command.d.ts +4 -0
- package/dist/commands/split-view-command.d.ts +4 -0
- package/dist/commands/table-command.d.ts +4 -0
- package/dist/commands/time-filter-command.d.ts +13 -0
- package/dist/commands/toggle-time-filter-command.d.ts +4 -0
- package/dist/commands/types.d.ts +132 -0
- package/dist/commands/utils.d.ts +138 -0
- package/dist/commands/view-mode-command.d.ts +4 -0
- package/dist/esm/commands/add-column-command.js +185 -0
- package/dist/esm/commands/basemap-command.js +72 -0
- package/dist/esm/commands/boundary-command.js +73 -0
- package/dist/esm/commands/command-wrappers.js +114 -0
- package/dist/esm/commands/dataset-context-command.js +79 -0
- package/dist/esm/commands/index.js +51 -0
- package/dist/esm/commands/layer-creation-command.js +527 -0
- package/dist/esm/commands/layer-style-command.js +130 -0
- package/dist/esm/commands/load-data-command.js +264 -0
- package/dist/esm/commands/save-data-command.js +112 -0
- package/dist/esm/commands/split-view-command.js +255 -0
- package/dist/esm/commands/table-command.js +194 -0
- package/dist/esm/commands/time-filter-command.js +280 -0
- package/dist/esm/commands/toggle-time-filter-command.js +147 -0
- package/dist/esm/commands/types.js +3 -0
- package/dist/esm/commands/utils.js +521 -0
- package/dist/esm/commands/view-mode-command.js +86 -0
- package/dist/esm/index.js +20 -0
- package/dist/esm/map-contract.js +3 -0
- package/dist/esm/server/bridge-server.js +428 -0
- package/dist/esm/server/index.js +13 -0
- package/dist/esm/server/stdio.js +198 -0
- package/dist/esm/types.js +24 -0
- package/dist/index.d.ts +15 -0
- package/dist/map-contract.d.ts +31 -0
- package/dist/server/bridge-server.d.ts +53 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/stdio.d.ts +2 -0
- package/dist/types.d.ts +24 -0
- package/package.json +84 -4
- package/skill/kepler/SKILL.md +438 -0
- package/skill/kepler/skill.yaml +4 -0
- package/README.md +0 -3
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
# Kepler map management
|
|
2
|
+
|
|
3
|
+
Create and configure kepler.gl map layers from datasets, animate them over
|
|
4
|
+
time, compare them side by side, change the basemap, update layer colors,
|
|
5
|
+
load data from a URL, add columns to existing datasets, and read the current
|
|
6
|
+
map boundary. These are direct map-mutation operations on the kepler.gl map
|
|
7
|
+
surface.
|
|
8
|
+
|
|
9
|
+
## When to use
|
|
10
|
+
|
|
11
|
+
Use this skill when the user wants to create or reconfigure map layers, change
|
|
12
|
+
the appearance or configuration of the map itself, animate data over time, or
|
|
13
|
+
compare layers side by side. Examples:
|
|
14
|
+
- "Show the shops as points colored by visits"
|
|
15
|
+
- "Create a heatmap of the pickup locations"
|
|
16
|
+
- "Animate the trips over time"
|
|
17
|
+
- "Compare the 2019 and 2024 layers side by side"
|
|
18
|
+
- "Change the basemap to dark/satellite/positron"
|
|
19
|
+
- "Update the color of the layer to blue"
|
|
20
|
+
- "Load data from this URL"
|
|
21
|
+
- "Add a new variable / column to an existing dataset"
|
|
22
|
+
- "Change a column's type (e.g. make NOSOUTH a string)"
|
|
23
|
+
- "Rename a column on the existing dataset"
|
|
24
|
+
- "What's the current map boundary/extent?"
|
|
25
|
+
|
|
26
|
+
Do NOT use this skill for analysis beyond map mutation — spatial statistics,
|
|
27
|
+
spatial filtering, colocation, boundary fetching, or charts. Those belong to
|
|
28
|
+
your host's analysis skills (e.g. a spatial-analysis skill, a filter skill),
|
|
29
|
+
not this one.
|
|
30
|
+
|
|
31
|
+
## Commands at a glance
|
|
32
|
+
|
|
33
|
+
| command | purpose |
|
|
34
|
+
| ------------------------ | -------------------------------------------------------------- |
|
|
35
|
+
| `map.add-layer` | Add a layer (point, flow, flowField, h3, arc, trip, hexagon, grid, cluster, heatmap, geojson, line, s2) to the map. |
|
|
36
|
+
| `map.add-time-filter` | Animate a NON-trip layer over a TIMESTAMP/DATE column. |
|
|
37
|
+
| `map.toggle-time-filter` | Show/hide the enlarged time controller at the bottom of the map. |
|
|
38
|
+
| `map.split-view` | Enable/disable dual-map comparison. |
|
|
39
|
+
| `map.update-layer-color` | Update an existing layer's color palette. |
|
|
40
|
+
| `map.set-basemap` | Change the basemap style. |
|
|
41
|
+
| `map.set-view-mode` | Switch between the flat 2D map, the tilted 3D map and the 3D globe. |
|
|
42
|
+
| `map.load-data` | Load data from a URL (no layer — call `map.add-layer`). |
|
|
43
|
+
| `map.save-data` | Save a DuckDB table as a map dataset (no layer — call `map.add-layer`). |
|
|
44
|
+
| `map.add-column` | Add a column to an EXISTING dataset in place (copy or expression). |
|
|
45
|
+
| `map.create-table` | Create a NEW dataset via SQL (original untouched — call `map.add-layer`). |
|
|
46
|
+
| `map.get-dataset-context` | List all loaded datasets with their fields and layer configurations. |
|
|
47
|
+
| `map.get-boundary` | Read the current map view's bounding box. |
|
|
48
|
+
|
|
49
|
+
Every command takes a `commandId` (one of the table above) and an `input`
|
|
50
|
+
object. Issue commands through your host's map-command dispatcher — the exact
|
|
51
|
+
envelope is described in your host's tool listing. In the kepler.gl demo
|
|
52
|
+
assistant the dispatcher is the `executeApi` tool with
|
|
53
|
+
`apiName: "executeCommand"` and `args: { commandId, input }`.
|
|
54
|
+
|
|
55
|
+
## Picking a layer type
|
|
56
|
+
|
|
57
|
+
- Individual locations → `point` (`cluster` for zoom-out density, `heatmap`
|
|
58
|
+
for continuous intensity).
|
|
59
|
+
- Density without precomputed cells → `grid` or `hexagon` (auto-binned).
|
|
60
|
+
- Pre-aggregated H3 cells → `h3`.
|
|
61
|
+
- Origin→destination flows → `flow` (a dedicated flow layer with locations, clustering and weighted edges; requires two point lat/lng pairs — source + destination). Fall back to `arc` only when you want straight great-circle lines between points. For weighted flows optionally pass the magnitude column as `countColumn`.
|
|
62
|
+
- Vector fields — wind, currents, downhill terrain flow → `flowField`, animated streamlines over one vector per sample point. This is **not** `flow`: `flow` joins an origin pair to a destination pair, `flowField` draws many short lines through a field of vectors.
|
|
63
|
+
|
|
64
|
+
- Polylines / polygons → `line` / `geojson`. A decoded GEOMETRY `geom`
|
|
65
|
+
column can be rendered directly with `geojson` — do NOT convert it to
|
|
66
|
+
lat/lon columns or build an intermediate table. For geojson datasets use
|
|
67
|
+
geometryColumn `"_geojson"`.
|
|
68
|
+
- Time-animated movement → `trip`.
|
|
69
|
+
|
|
70
|
+
## Color configuration
|
|
71
|
+
|
|
72
|
+
- `colorBy` is case-sensitive and must match the column name exactly.
|
|
73
|
+
- `simpleColor` is the string `"[r, g, b]"` (e.g. `"[255, 64, 0]"`), not an array.
|
|
74
|
+
It is a single static color for ALL features — NEVER use it for data-driven
|
|
75
|
+
coloring; use `colorBy` + `colorType` + `colorMap` for that.
|
|
76
|
+
- For continuous data use `colorType: "breaks"` with a `colorMap` whose last
|
|
77
|
+
entry has `value: null` (the color for the highest bucket):
|
|
78
|
+
`[{value: 0, color: "#fee5d9"}, {value: 50, color: "#fcae91"}, {value: 100, color: "#fb6a4a"}, {value: null, color: "#de2d26"}]`
|
|
79
|
+
- For categorical data use `colorType: "unique"`:
|
|
80
|
+
`[{value: "retail", color: "#1f77b4"}, {value: "restaurant", color: "#ff7f0e"}]`
|
|
81
|
+
HARD RULE: the `value`s MUST be the actual unique values in the column.
|
|
82
|
+
Classify the variable with your host's classification tool first and use the
|
|
83
|
+
returned unique values verbatim. NEVER guess category labels from the column
|
|
84
|
+
name — `map.add-layer` rejects values that do not exist in the data.
|
|
85
|
+
- For `heatmap`, only `colorRange` (from `colorMap`) takes effect.
|
|
86
|
+
- To compute the break values, classify the variable with your host's
|
|
87
|
+
classification tool first and pass the returned breaks as the `colorMap`
|
|
88
|
+
values.
|
|
89
|
+
- Generate ColorBrewer-style palettes automatically when the user does not
|
|
90
|
+
specify colors.
|
|
91
|
+
|
|
92
|
+
### Suggested palettes
|
|
93
|
+
|
|
94
|
+
- Sequential green: `#ffffcc #c2e699 #78c679 #31a354 #006837 #004529`
|
|
95
|
+
- Sequential red: `#fee5d9 #fcae91 #fb6a4a #de2d26 #a50f15`
|
|
96
|
+
- Categorical (Tableau 10): `#1f77b4 #ff7f0e #2ca02c #d62728 #9467bd #8c564b`
|
|
97
|
+
|
|
98
|
+
## Workflow
|
|
99
|
+
|
|
100
|
+
### 1. Create a layer (`map.add-layer`)
|
|
101
|
+
|
|
102
|
+
Point layer colored by a continuous variable:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"datasetName": "shops",
|
|
107
|
+
"layerType": "point",
|
|
108
|
+
"layerName": "Shops by visits",
|
|
109
|
+
"colorBy": "visits",
|
|
110
|
+
"colorType": "breaks",
|
|
111
|
+
"colorMap": [
|
|
112
|
+
{"value": 3, "color": "#ffffcc"},
|
|
113
|
+
{"value": 10, "color": "#c2e699"},
|
|
114
|
+
{"value": 25, "color": "#78c679"},
|
|
115
|
+
{"value": 50, "color": "#31a354"},
|
|
116
|
+
{"value": 100, "color": "#006837"},
|
|
117
|
+
{"value": null, "color": "#004529"}
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Point layer with a uniform color:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"datasetName": "pizza_stores",
|
|
127
|
+
"layerType": "point",
|
|
128
|
+
"layerName": "Pizza stores",
|
|
129
|
+
"simpleColor": "[255, 64, 0]"
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
H3 layer:
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"datasetName": "visits_h3_r9",
|
|
138
|
+
"layerType": "h3",
|
|
139
|
+
"layerName": "Visits density (h3 r9)",
|
|
140
|
+
"colorBy": "visit_count",
|
|
141
|
+
"colorType": "breaks",
|
|
142
|
+
"colorMap": [
|
|
143
|
+
{"value": 100, "color": "#fee5d9"},
|
|
144
|
+
{"value": 500, "color": "#fcae91"},
|
|
145
|
+
{"value": 2000, "color": "#fb6a4a"},
|
|
146
|
+
{"value": 10000, "color": "#de2d26"},
|
|
147
|
+
{"value": null, "color": "#a50f15"}
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`map.add-layer` returns `dateTimeColumns` when the dataset has TIMESTAMP/DATE
|
|
153
|
+
fields — see "Time animation" below for the follow-up.
|
|
154
|
+
|
|
155
|
+
### 2. Time animation (NON-trip layers)
|
|
156
|
+
|
|
157
|
+
When the user asks how something changed/evolved/trended over time and the
|
|
158
|
+
source dataset has a TIMESTAMP/DATE column, build a SINGLE layer and animate
|
|
159
|
+
it with `map.add-time-filter` — do NOT create a separate static layer per
|
|
160
|
+
time step. A stack of snapshot layers does not convey motion and clutters the
|
|
161
|
+
map; the animated layer is the deliverable.
|
|
162
|
+
|
|
163
|
+
- BEFORE creating any layer for a temporal request, DECLARE the expected
|
|
164
|
+
layer count: a change-over-time request ⇒ exactly **1** layer. If you find
|
|
165
|
+
yourself about to issue a 2nd `map.add-layer`, STOP — you have erred.
|
|
166
|
+
- `map.add-layer` returns `dateTimeColumns` when timestamp/date fields are
|
|
167
|
+
detected. Pass the first one to `map.add-time-filter`.
|
|
168
|
+
- If the column is an integer/string epoch, build a real TIMESTAMP column
|
|
169
|
+
first (Kepler.gl cannot handle negative epochs) — NEVER pass an
|
|
170
|
+
integer/string column directly.
|
|
171
|
+
- `map.add-time-filter` input: `{ datasetName, dateTimeColumn, interval? }`.
|
|
172
|
+
`interval` is auto-detected from the data when omitted.
|
|
173
|
+
- **Do not retry `map.add-time-filter` more than once.** If it fails, report
|
|
174
|
+
the error and continue — do not loop.
|
|
175
|
+
- LAYER ORDERING: create the time-filtered layer LAST so its animation is
|
|
176
|
+
not hidden behind later layers.
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"datasetName": "earthquakes",
|
|
181
|
+
"dateTimeColumn": "time"
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Trip layers (`layerType: "trip"`)
|
|
186
|
+
|
|
187
|
+
- Trip layers have **built-in** time animation. Do NOT add a
|
|
188
|
+
`map.add-time-filter` for a trip layer — it is unnecessary and can break the
|
|
189
|
+
animation. After creating a trip layer, STOP.
|
|
190
|
+
- Strict columns: `id` (shared across a trip's waypoints), `latitude` /
|
|
191
|
+
`longitude` (DOUBLE), `timestamp` (epoch milliseconds). If your epoch is in
|
|
192
|
+
seconds, multiply by 1000.
|
|
193
|
+
- Densify sparse O-D data (interpolate lon/lat AND timestamp) or trips
|
|
194
|
+
teleport.
|
|
195
|
+
|
|
196
|
+
### Flow field layer (`layerType: "flowField"`)
|
|
197
|
+
|
|
198
|
+
Animated streamlines over a vector field — wind, ocean currents, or downhill
|
|
199
|
+
terrain flow. Each row of the dataset is one sample point: a position plus a
|
|
200
|
+
direction. It is not the origin→destination `flow` layer — `flow` joins two
|
|
201
|
+
point pairs, `flowField` draws many short, continuously moving lines through a
|
|
202
|
+
field of vectors.
|
|
203
|
+
|
|
204
|
+
Pick the mode from the columns the dataset has; `map.add-layer` auto-detects
|
|
205
|
+
them in this order and names them in `flowField.*Column` when they differ:
|
|
206
|
+
|
|
207
|
+
| `flowField.mode` | Columns | Direction comes from |
|
|
208
|
+
|------------------|---------|----------------------|
|
|
209
|
+
| `UV` (default) | `u`, `v` (+ optional `altitude`) | east/north vector components |
|
|
210
|
+
| `SPEED_DIR` | `speed`, `direction` | speed plus compass direction (from) |
|
|
211
|
+
| `ELEVATION` | `altitude` | the downhill gradient of a height surface |
|
|
212
|
+
|
|
213
|
+
Position comes from lat/lng columns — auto-detected, or `latitudeColumn` /
|
|
214
|
+
`longitudeColumn` — or from a GeoJSON geometry column when the dataset has one.
|
|
215
|
+
|
|
216
|
+
Wind encoded as `u`/`v` (no extra parameters needed):
|
|
217
|
+
|
|
218
|
+
```json
|
|
219
|
+
{
|
|
220
|
+
"datasetName": "wind_grid",
|
|
221
|
+
"layerType": "flowField",
|
|
222
|
+
"layerName": "Wind field",
|
|
223
|
+
"flowField": {"colorBySpeed": true}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Wind encoded as speed + direction under different column names:
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"datasetName": "wind_grid",
|
|
232
|
+
"layerType": "flowField",
|
|
233
|
+
"layerName": "Wind field",
|
|
234
|
+
"flowField": {
|
|
235
|
+
"mode": "SPEED_DIR",
|
|
236
|
+
"speedColumn": "wind_speed",
|
|
237
|
+
"directionColumn": "wind_dir"
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
- `colorBySpeed: true` colors each streamline by its local speed (using the
|
|
243
|
+
layer's color range); without it the streamlines use `color`. `colorBy` /
|
|
244
|
+
`colorMap` do not apply to this layer.
|
|
245
|
+
- Do **not** add `map.add-time-filter` — the flow field animates continuously on
|
|
246
|
+
its own, like a trip layer.
|
|
247
|
+
- Streamline density (`linesPerScreen`), `strokeWidth`, `trailLength`,
|
|
248
|
+
`cycleSeconds`, `lineLifetime` and `smoothing` use tuned defaults; they are
|
|
249
|
+
adjusted in the layer panel, not through `map.add-layer`.
|
|
250
|
+
- A field layer over a very large grid is expensive: one row per sample point is
|
|
251
|
+
fine, but downsample rasters to roughly 10k–100k points before loading.
|
|
252
|
+
|
|
253
|
+
### 3. Split map comparison (`map.split-view`)
|
|
254
|
+
|
|
255
|
+
- `action: "enable"` with `layerIdsForMap0` / `layerIdsForMap1`. Without the
|
|
256
|
+
layer-id arrays all layers show on both panels (no comparison). Use the SAME
|
|
257
|
+
`colorBy` / `colorType` for a fair comparison. `action: "disable"` returns
|
|
258
|
+
to one map.
|
|
259
|
+
|
|
260
|
+
```json
|
|
261
|
+
{
|
|
262
|
+
"action": "enable",
|
|
263
|
+
"layerIdsForMap0": ["layer_2019"],
|
|
264
|
+
"layerIdsForMap1": ["layer_2024"]
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### 4. Toggle the time controller
|
|
269
|
+
|
|
270
|
+
`map.toggle-time-filter` with `action: "show"` enlarges the time controller
|
|
271
|
+
at the bottom of the map; `action: "hide"` collapses it back to the side
|
|
272
|
+
panel. A time filter must already exist (created via `map.add-time-filter`).
|
|
273
|
+
|
|
274
|
+
### 5. Basemap style
|
|
275
|
+
|
|
276
|
+
```json
|
|
277
|
+
{ "styleType": "dark" }
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Valid `styleType` values: `"no_map"`, `"dark-matter"`, `"positron"`,
|
|
281
|
+
`"voyager"`, `"satellite"`, `"dark"`, `"light"`, `"muted"`, `"muted_night"`.
|
|
282
|
+
If the user uses a descriptive name, map it to the closest valid `styleType`.
|
|
283
|
+
If the request is ambiguous, ask the user to clarify.
|
|
284
|
+
|
|
285
|
+
### 6. Layer color
|
|
286
|
+
|
|
287
|
+
```json
|
|
288
|
+
{
|
|
289
|
+
"layerId": "layer_xxx",
|
|
290
|
+
"numberOfColors": 5,
|
|
291
|
+
"customColors": ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd"]
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
`customColors` is an array of hex color strings; `numberOfColors` must equal
|
|
296
|
+
its length. If the user describes colors qualitatively (e.g. "van gogh
|
|
297
|
+
starry night", "watercolor"), generate an appropriate palette from the
|
|
298
|
+
description. If the user refers to a layer by name rather than id, find the
|
|
299
|
+
matching `layerId` from the dataset context.
|
|
300
|
+
|
|
301
|
+
### 7. Load data from URL
|
|
302
|
+
|
|
303
|
+
```json
|
|
304
|
+
{ "url": "https://..." }
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
This fetches the data, parses it, and adds it to the map as a dataset. It
|
|
308
|
+
does NOT create a layer — call `map.add-layer` afterward to visualize the
|
|
309
|
+
data (with the layer type, color, and styling you want).
|
|
310
|
+
|
|
311
|
+
The dataset is named after the URL filename (e.g. `nyc.geojson`). If the
|
|
312
|
+
user wants a specific dataset name, pass it as `datasetName`:
|
|
313
|
+
|
|
314
|
+
```json
|
|
315
|
+
{ "url": "https://.../nyc.geojson", "datasetName": "NYC Neighborhoods" }
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Local files: a local file must be served over http(s) — the browser cannot
|
|
319
|
+
fetch `file://` paths. Copy the file into the demo app's served directory
|
|
320
|
+
(e.g. `examples/demo-app/dist/`) and pass `/filename.csv`, or serve it with
|
|
321
|
+
a CORS-enabled local server (`npx serve --cors -l 8000`) and pass
|
|
322
|
+
`http://localhost:8000/filename.csv`. If you have the file content but
|
|
323
|
+
cannot serve it (e.g. a remote browser context), pass it as a data URL —
|
|
324
|
+
`data:text/csv;base64,<base64 content>` (encode with `base64 -i file.csv`).
|
|
325
|
+
This works for small files (up to ~2MB).
|
|
326
|
+
|
|
327
|
+
For large files (over a few MB), prefer Parquet (`.parquet`) over
|
|
328
|
+
GeoJSON/CSV — it loads much faster and is less likely to time out. Convert
|
|
329
|
+
GeoJSON to GeoParquet with geopandas (`gdf.to_parquet("data.parquet")`);
|
|
330
|
+
kepler reads GeoParquet geometry (WKB) natively, so the map renders the
|
|
331
|
+
same lines.
|
|
332
|
+
|
|
333
|
+
Do NOT create a duplicate dataset just to rename it — never run
|
|
334
|
+
`map.create-table` with a plain `SELECT *` (or any copy) to give the loaded
|
|
335
|
+
data a friendlier name. `map.create-table` creates a SEPARATE new dataset and
|
|
336
|
+
is only for real column transformations; the rename belongs in
|
|
337
|
+
`map.load-data`'s `datasetName` (or in the user's hands). Use the loaded
|
|
338
|
+
dataset as-is and add the layer to it.
|
|
339
|
+
|
|
340
|
+
### 8. Map boundary
|
|
341
|
+
|
|
342
|
+
```json
|
|
343
|
+
{}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Returns the northwest and southeast coordinates of the current map view:
|
|
347
|
+
`{ boundary: { nw: [lon, lat], se: [lon, lat] } }`. When the user says
|
|
348
|
+
"current map view", call this to scope spatial queries to the visible area.
|
|
349
|
+
|
|
350
|
+
### 9. Add / rename / retype a column in an EXISTING dataset (`map.add-column`)
|
|
351
|
+
|
|
352
|
+
`map.add-column` adds a NEW column to an existing dataset **in place** — the
|
|
353
|
+
dataset keeps its name, id, color and layers, and existing columns are
|
|
354
|
+
untouched. The new column's values come from exactly ONE of two sources:
|
|
355
|
+
|
|
356
|
+
- `copyFromColumn` — copy an existing column's values. This is how you rename a
|
|
357
|
+
column in place: to rename `fare` to `fare_amount`, add `fare_amount` copying
|
|
358
|
+
`fare` (the original column stays — the command never removes columns).
|
|
359
|
+
- `expression` — an SQL expression computed per row, for a derived variable
|
|
360
|
+
(z-score, ratio, concatenation) OR a type change:
|
|
361
|
+
- z-score: `{ datasetName, newColumnName: "HR60_Z", expression: "(HR60 - AVG(HR60) OVER()) / STDDEV(HR60) OVER()" }`
|
|
362
|
+
- type change: `{ datasetName, newColumnName: "NOSOUTH_str", expression: "NOSOUTH::VARCHAR" }`
|
|
363
|
+
(or `CAST(NOSOUTH AS VARCHAR)`). The new column holds the cast values; the
|
|
364
|
+
original column stays.
|
|
365
|
+
|
|
366
|
+
```json
|
|
367
|
+
{
|
|
368
|
+
"datasetName": "natregimes.geojson",
|
|
369
|
+
"newColumnName": "NOSOUTH_str",
|
|
370
|
+
"expression": "NOSOUTH::VARCHAR"
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
`map.create-table` is NOT for this — it builds a separate NEW dataset and leaves
|
|
375
|
+
the original untouched. Prefer `map.add-column` whenever the user wants the
|
|
376
|
+
change to land in the existing dataset.
|
|
377
|
+
|
|
378
|
+
### View mode: 2D map, 3D map, or globe (`map.set-view-mode`)
|
|
379
|
+
|
|
380
|
+
```json
|
|
381
|
+
{ "mode": "globe" }
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
- `2d` — flat map, no tilt. The default for city- and region-scale data.
|
|
385
|
+
- `3d` — the flat map tilted (pitch 50). For extruded buildings, 3D layers and a
|
|
386
|
+
more dramatic camera; it is **not** a globe.
|
|
387
|
+
- `globe` — the 3D globe with atmosphere. Use it when the data spans the world
|
|
388
|
+
(global trips, arcs, lines, points, clusters, hexagons, grids, GeoJSON), so
|
|
389
|
+
the user sees the whole planet instead of a distorted flat world.
|
|
390
|
+
|
|
391
|
+
Switching to `globe` clamps zoom into the globe's range; switching back to
|
|
392
|
+
`2d`/`3d` restores the normal range. The camera mode is independent of the
|
|
393
|
+
basemap, so set both when the user asks for a "dark globe" and the like.
|
|
394
|
+
|
|
395
|
+
- **Keep `flow` and `flowField` layers off the globe.** Neither is supported
|
|
396
|
+
there yet — flow arrows collapse when seen edge-on, and the flow field's
|
|
397
|
+
clipping is mercator-only. Create a global flow field on the 2d map; if the
|
|
398
|
+
user wants a globe view of global movement, use `arc`, `line` or `trip`
|
|
399
|
+
instead, or tell them the field layer needs the flat map.
|
|
400
|
+
- Do not switch modes unless the user asks for it or the data's global extent
|
|
401
|
+
makes it clearly better; a map that suddenly becomes a globe is surprising.
|
|
402
|
+
|
|
403
|
+
## Rules
|
|
404
|
+
|
|
405
|
+
- Commands that add datasets to the map — `map.load-data`, `map.save-data`,
|
|
406
|
+
`map.create-table` — do NOT auto-create layers. Always call `map.add-layer`
|
|
407
|
+
afterward to visualize the result.
|
|
408
|
+
- To add a variable to, rename a column in, or change a column type in an
|
|
409
|
+
EXISTING dataset, use `map.add-column` (copy or expression) — do NOT create a
|
|
410
|
+
new dataset. `map.create-table` only when the user wants a SEPARATE new
|
|
411
|
+
dataset.
|
|
412
|
+
- A dataset loaded from a URL keeps the URL filename (or the `datasetName` you
|
|
413
|
+
pass to `map.load-data`). NEVER create a duplicate dataset via
|
|
414
|
+
`map.create-table` just to rename it — use the loaded dataset as-is.
|
|
415
|
+
- For change-over-time requests, create exactly ONE animated layer — never
|
|
416
|
+
a separate static layer per time step.
|
|
417
|
+
- Do not add a `map.add-time-filter` to a trip layer — trips animate
|
|
418
|
+
themselves.
|
|
419
|
+
- If a basemap style name is ambiguous, ask the user to clarify rather than
|
|
420
|
+
guessing.
|
|
421
|
+
- For layer color updates, the `layerId` must match an existing layer on the
|
|
422
|
+
map. If the user refers to a layer by name rather than id, use the layer
|
|
423
|
+
name to find the matching layer id from the dataset context.
|
|
424
|
+
|
|
425
|
+
## Before your final message
|
|
426
|
+
|
|
427
|
+
- If you created a layer, confirm the layer type, name, and (if any) the
|
|
428
|
+
color field/colorType applied.
|
|
429
|
+
- If you added a time filter, confirm the animated column and interval.
|
|
430
|
+
- If you enabled split view, confirm which layers are on each panel.
|
|
431
|
+
- If you changed the basemap, confirm the new `styleType`.
|
|
432
|
+
- If you updated layer colors, confirm the layer id and colors applied.
|
|
433
|
+
- If you loaded data, confirm the dataset was loaded and a layer was created.
|
|
434
|
+
- If you read the map boundary, report the northwest and southeast
|
|
435
|
+
coordinates.
|
|
436
|
+
- If `map.add-layer` returned `dateTimeColumns` and the user asked about
|
|
437
|
+
change over time, you MUST follow up with `map.add-time-filter` on the first
|
|
438
|
+
returned column — do not leave the temporal hint unactioned.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
id: kepler
|
|
2
|
+
version: 0.2.0
|
|
3
|
+
name: Kepler map management
|
|
4
|
+
description: Create and configure kepler.gl map layers (point, flow, flowField, arc, line, grid, hexagon, geojson, cluster, heatmap, h3, trip, s2) from datasets, animate them over time, compare them side by side, change the basemap, switch between the 2D, 3D and globe views, update layer colors, load data from a URL, add columns to existing datasets, and read the current map boundary.
|
package/README.md
DELETED