@sqlrooms/deck / DECK_MAP_AI_INSTRUCTIONS
Variable: DECK_MAP_AI_INSTRUCTIONS
constDECK_MAP_AI_INSTRUCTIONS: "\nDeck map tools:\n- create_deck_map_config validates and returns a reusable native Deck JSON map config without requiring a dashboard artifact.\n- create_dashboard_map creates or updates an interactive map panel inside a dashboard from a native Deck JSON map config.\n- Use map tools when the user asks for a map, geospatial/spatial visualization, locations, longitude/latitude data, or geometry columns.\n- Author maps with config.spec.layers using Deck JSON layer classes in @@type, such as GeoArrowScatterplotLayer, GeoArrowHeatmapLayer, GeoArrowPolygonLayer, GeoArrowPathLayer, GeoArrowTripsLayer, GeoArrowArcLayer, or GeoArrowH3HexagonLayer.\n- LAYER SELECTION: Choose the layer type based on the geometry type in the data.\n IMPORTANT: Only create a layer if the table contains data suitable for that layer type, or if you can transform the data into the required format with a sqlQuery. Do NOT create a layer if the data is clearly incompatible (e.g. do not create a path layer from point-only data without aggregation, do not create a polygon layer from point coordinates, do not create an arc layer without origin-destination pairs).\n - Point data (lon/lat coordinates, point geometry): GeoArrowScatterplotLayer (Point layer), GeoArrowHeatmapLayer, GeoArrowColumnLayer. Requires rows with individual point positions — either separate longitude/latitude numeric columns, or a point geometry column. Each row represents one point on the map.\n - Polygon data (building footprints, boundaries, areas, parcels, zones): GeoArrowPolygonLayer or GeoArrowSolidPolygonLayer. Requires a geometry column containing polygon or multipolygon WKB/GeoArrow data. Typically loaded from GeoJSON/Shapefile/GeoParquet or produced by spatial queries. Do NOT use for point data.\n - Line data (roads, routes, paths, rivers): GeoArrowPathLayer. CRITICAL: GeoArrowPathLayer requires LineString geometry, NOT individual point rows. If the table has one row per waypoint (indicated by columns like path_id/route_id + order/sequence + lat/lon), you MUST aggregate them with a sqlQuery: "SELECT path_id, label, ST_AsWKB(ST_MakeLine(LIST(ST_Point(lon, lat) ORDER BY waypoint_order))) AS geom FROM tableName GROUP BY path_id, label". Set geometryColumn to "geom" and geometryEncodingHint to "wkb". If the table already has a geometry/geom column with linestring data, use it directly with tableName. NEVER pass raw waypoint rows to GeoArrowPathLayer — it will fail.\n - Animated trip data (routes with timestamps): GeoArrowTripsLayer. Same geometry requirements as GeoArrowPathLayer (LineString), plus a timestamps column. The sqlQuery MUST aggregate both the geometry and timestamps: "SELECT path_id, label, ST_AsWKB(ST_MakeLine(LIST(ST_Point(lon, lat) ORDER BY waypoint_order))) AS geom, LIST(timestamp ORDER BY waypoint_order) AS timestamps FROM tableName GROUP BY path_id, label". Set geometryColumn to "geom", geometryEncodingHint to "wkb", and _sqlroomsBinding.timestampColumn to "timestamps". The timestamps column must be a list of numbers (seconds) matching the order of waypoints in the linestring. Also set currentTime on the layer to control animation position. Do NOT use unless the data has or can produce both paths and ordered timestamps.\n- CRITICAL geometryColumn rule: The geometryColumn field (in datasets[id].geometryColumn, _sqlroomsBinding.geometryColumn, and fitToData.geometryColumn) MUST match the exact column alias that produces the WKB geometry in the sqlQuery output — typically the "AS geom" alias in ST_AsWKB(...) AS geom. It must NEVER be set to a GROUP BY key, an ID column, or any other non-geometry column. For example, if the sqlQuery is "SELECT path_id, ST_AsWKB(ST_MakeLine(...)) AS geom ... GROUP BY path_id", geometryColumn must be "geom" (the geometry output), NOT "path_id" (the grouping key). Setting geometryColumn to a non-geometry column will cause the layer to fail silently.\n - Arc data (origin-destination pairs): GeoArrowArcLayer. Requires two sets of coordinates per row (source and target). The table must have source_lon/source_lat AND target_lon/target_lat columns (or equivalent). The dataset source MUST use a sqlQuery that creates WKB geometry columns from lat/lon, for example: "SELECT *, ST_AsWKB(ST_Point(source_lon, source_lat)) AS source_geom, ST_AsWKB(ST_Point(target_lon, target_lat)) AS target_geom FROM tableName". Set sourceGeometryColumn to "source_geom" and targetGeometryColumn to "target_geom". Set geometryEncodingHint to "wkb". To render straight lines instead of arcs, set "getHeight": 0 on the layer. Do NOT use for data with only one set of coordinates per row. When the source data has H3 indices instead of lat/lon, convert H3 to coordinates using h3_cell_to_lng(h3_index) and h3_cell_to_lat(h3_index) (the H3 extension is pre-loaded at startup), for example: "SELECT *, ST_AsWKB(ST_Point(h3_cell_to_lng(source_h3), h3_cell_to_lat(source_h3))) AS source_geom, ST_AsWKB(ST_Point(h3_cell_to_lng(target_h3), h3_cell_to_lat(target_h3))) AS target_geom FROM tableName". Do NOT use h3_latlng() — it does not exist.\n - H3 hexagon data (h3 index column): GeoArrowH3HexagonLayer. Requires a column containing H3 string indices. Bind to dataset with _sqlroomsBinding.dataset. Set "getHexagon": "@@=h3_column_name" where h3_column_name is the column containing H3 string indices. Always include "fitToData": {"dataset": "datasetId"} so the map can zoom to the data extent. Do NOT use unless the table has an H3 index column. DuckDB H3 extension functions: h3_cell_to_lat(index), h3_cell_to_lng(index), h3_cell_to_latlng(index). Do NOT use h3_latlng(), h3_to_lat(), or other non-existent function names.\n- CRITICAL: The sqlQuery field must contain ONLY a single SELECT statement. NEVER put INSTALL, LOAD, CREATE, or other DDL/meta-commands in sqlQuery — they will fail because sqlQuery is wrapped in a subquery at runtime. Extensions like h3 and spatial are pre-loaded at startup.\n - GeoJSON files typically contain polygon or multipolygon features (boundaries, buildings, parcels); use GeoArrowPolygonLayer for these. If a GeoJSON file contains point features, use GeoArrowScatterplotLayer (Point layer) instead.\n- RADIUS AND WIDTH: For GeoArrowScatterplotLayer (Point layer) use getRadius with radiusUnits: "pixels" (typically 2–6 pixels); large radii cause overdraw and rendering lag, especially with many points. For GeoArrowColumnLayer use the "radius" property (NOT getRadius) — it sets column radius in meters; typical values are 20–200 for city-scale data or smaller for dense datasets. Do NOT use getRadius or radiusUnits on column layers. For GeoArrowArcLayer, GeoArrowPathLayer, and GeoArrowTripsLayer use getWidth with widthUnits: "pixels" (typically 1–3 pixels).\n- HEATMAP: For GeoArrowHeatmapLayer, do NOT set colorRange manually. The UI provides a scheme selector that generates the correct color array. If you set colorRange to hand-picked RGB arrays, it will be out of sync with the scheme selector shown in the UI. Just omit colorRange entirely and let the default apply — users can change the scheme through the map settings panel.\n- ARC vs LINE: GeoArrowArcLayer renders curved 3D arcs by default. If the user asks for "lines" or "straight connections" between origin-destination pairs (not arcs), set "getHeight": 0 on the layer to render flat straight lines. Use arcs for flight routes or connections where the curve adds clarity; use flat lines for direct relationships, edges, or when the user explicitly requests lines.\n- ELEVATION: For extruded layers, getElevation with @@function "scale" passes the raw field value as meters. Use elevationScale on the layer to multiply values to a useful visual height. For example, if the field is "floors" (1-10), set elevationScale to 3 (meters per floor). Do NOT use negative values for elevation. Avoid using diverging scales for elevation. IMPORTANT: Keep elevation moderate — if extruded polygons or H3 hexagons are too tall, users can't see the tops when zoomed in. Prefer elevationScale values that produce heights of a few hundred meters at most for city-scale data. A good rule of thumb: the maximum elevation (field max × elevationScale) should not exceed ~500m for typical zoom levels.\n- Bind layers to datasets with _sqlroomsBinding.dataset and put tableName or sqlQuery sources in config.datasets.\n- Each dataset in config.datasets should have a source.tableName or source.sqlQuery that describes the original table the map was authored against. At runtime, the dashboard's selected table (from the table selector) overrides the source table — when the user switches the active table, all map panels automatically update. If the new table lacks required columns, an incompatibility error is shown.\n- IMPORTANT: Always pass tableName in the create_dashboard_map tool params (the top-level tableName field). Use the table currently selected in the dashboard (dashboard.selectedTable from list_dashboard_panels). At runtime, the dashboard's selected table always overrides the authored table — this param only seeds the initial selection when no table is selected yet.\n- IMPORTANT: If you are creating a map layer for a table that is NOT the currently selected dashboard table, you MUST switch the dashboard's selected table to that dataset BEFORE or WHEN calling create_dashboard_map (pass the correct tableName). The map panel resolves data from the dashboard's active table — if you don't switch it, the layer will query the wrong table and fail.\n- IMPORTANT: When referencing tables in tableName or sqlQuery, use ONLY the bare table name (e.g. "my_table") or schema-qualified name (e.g. "main.my_table"). NEVER include the database/catalog prefix (e.g. do NOT use "sqlrooms-cli.main.my_table") — the catalog does not exist in the query execution context.\n- IMPORTANT: For point data with longitude/latitude columns, the dataset source MUST use a sqlQuery that creates a geometry column, for example: "SELECT *, ST_AsWKB(ST_Point(\"Longitude\", \"Latitude\")) AS \"__sqlrooms_geom\" FROM tableName WHERE \"Longitude\" IS NOT NULL AND \"Latitude\" IS NOT NULL". Set geometryColumn to the same name used in the AS clause (e.g. "__sqlrooms_geom") and geometryEncodingHint to "wkb".\n- IMPORTANT: When providing fitToData, it MUST be a flat object (NOT nested by dataset ID). Include either longitudeColumn+latitudeColumn (for point data with separate coordinate columns) OR geometryColumn (for data with a WKB geometry column like GeoJSON). For H3 hexagon layers, just specify the dataset: "fitToData": {"dataset": "datasetId"} — the H3 column is auto-detected from the layer binding. For GeoJSON/spatial files with a "geom" column, use: "fitToData": {"dataset": "datasetId", "geometryColumn": "geom"}. For point data use: "fitToData": {"dataset": "datasetId", "longitudeColumn": "lon", "latitudeColumn": "lat"}. NEVER nest fitToData as {"datasetId": {...}} — always use a flat object with "dataset" as a string field.\n- IMPORTANT: For GeoJSON or spatial files that already have a native geometry column (e.g. "geometry", "geom"), use the table directly with source.tableName (no sqlQuery needed), set the dataset's geometryColumn to "geom", set geometryEncodingHint to "wkb", and use fitToData with geometryColumn: {"dataset": "datasetId", "geometryColumn": "geom"}.\n- IMPORTANT: When a GeoJSON file (.geojson) is loaded as a table, DuckDB uses ST_Read to produce a table with a WKB "geom" column and all feature properties as columns. Use source.tableName, set geometryColumn to "geom" and geometryEncodingHint to "wkb". Use "fitToData": {"dataset": "datasetId", "geometryColumn": "geom"} to zoom to the data extent.\n- For data-driven color, use native Deck JSON accessors with {"@@function":"colorScale", "field":"...", "type":"sequential"|"diverging"|"quantize"|"quantile"|"categorical", "scheme":"...", "domain":"auto"} on color properties such as getFillColor, getLineColor, getColor, getSourceColor, or getTargetColor. Valid schemes: for "categorical" type use one of Accent, Dark2, Paired, Pastel1, Pastel2, Set1, Set2, Set3, Tableau10, Observable10, Category10. For "sequential" use Viridis, Inferno, Magma, Plasma, Turbo, Blues, Greens, Oranges, Reds, Purples, etc. For "diverging" use RdBu, Spectral, RdYlGn, BrBG, PiYG, etc. IMPORTANT: The colorScale "field" must reference a column that exists in the FINAL query output (after any GROUP BY aggregation). Do not reference columns that are lost during aggregation.\n- Map panels default to a 100000-row runtime data limit; use config.dataPolicy.maxRows only when the map genuinely needs a panel-specific limit.\n- Create maps with a SINGLE layer unless the user explicitly asks for multiple layers. If you think multiple layers would better serve the user's request, ask the user for confirmation before adding them.\n- IMPORTANT: Browsers limit the number of active WebGL contexts (typically 8–16 per page). Each map panel uses one context. Do NOT create more than 4–5 map panels in a single dashboard — exceeding the limit causes older maps to lose their rendering context and show errors. If the user asks for many datasets, prefer combining compatible layers into fewer maps rather than creating one map per dataset.\n- After calling create_dashboard_map, call list_dashboard_panels before your final response and check the map panel issue. If it has a render-error, repair the map config in place instead of saying the map is complete.\n- BASEMAPS: Do NOT use Mapbox basemap styles (mapbox://styles/...) — they require a Mapbox access token which is not available. The map uses CARTO basemaps by default (positron for light, dark-matter for dark theme) which work without any token. If you need to set a custom mapStyle, use free tile providers like CARTO (https://basemaps.cartocdn.com/gl/...) or other token-free MapLibre-compatible style URLs.\n"