--- name: emem-recall-polygon description: Recalls signed Earth-observation facts at sampled cells inside an area (a named region, a bounding box, or a GeoJSON boundary) in one call. Use when the user asks about an extent rather than a point, such as "average NDVI inside this watershed", "precipitation across the Western Ghats", or "elevation across this admin boundary". Returns per-cell facts, each cell with its own Ed25519 receipt, plus the sampling it actually did so the agent can state coverage honestly. --- # emem-recall-polygon > **Network use.** The commands in this skill send and receive JSON (and, where a step says so, an image or a raster file) to `https://emem.dev` only, the service the plugin's MCP server connects to. Nothing they download is executed. This skill ships no scripts. Its commands read no environment variable, key or credential, and the only local files they read are ones an earlier step saved from `https://emem.dev`. `POST /v1/recall_polygon` samples cells inside an area, recalls the requested bands at each, and returns them per cell with per-cell receipts. It is `locate`, then a sample of the area's cells, then `recall_many`, collapsed into one call. ## When to invoke - "What's the average NDVI inside this watershed?" - "Show me precipitation across the area bounded by ..." - The user pastes a bbox or a GeoJSON polygon. For one point use [`emem-locate-and-recall`](../emem-locate-and-recall/SKILL.md). For the full-resolution pixel grid over an area use [`emem-field-tokens`](../emem-field-tokens/SKILL.md). ## How to invoke Give the area in one of three ways: `place` (free text, geocoded), `polygon_bbox` (an object with named corners; an array is refused, because bbox array orders disagree between conventions), or `polygon_geojson` (a GeoJSON Polygon, MultiPolygon or Feature). A polygon on its own sets the area: its bounds become the box and its boundary masks the sample. `geojson` and `geometry_geojson` are aliases of `polygon_geojson`. Beside a bbox, the polygon only masks the sample. Call `emem_recall_polygon` with: ```json {"polygon_bbox": {"min_lat": 12.95, "max_lat": 13.05, "min_lng": 77.55, "max_lng": 77.65}, "bands": ["copdem30m.elevation_mean"], "max_cells": 16, "budget_ms": 20000} ``` `emem_recall_polygon` is outside the short tool list the plugin's MCP server shows. The server runs it by name through `tools/call`. If your host calls only listed tools, use the REST route. Save the result's JSON text as `poly.json`. A result over the MCP wire budget (about 24 KB) comes back slimmed, and `_emem_truncation` names what it left out. For the whole response, send the same body to `POST https://emem.dev/v1/recall_polygon`. Then: ```sh jq '{cells_sampled, facts_returned, area_km2, converged, coverage_fraction, pending: (.pending | length)}' poly.json ``` - `max_cells` defaults to 64 and caps at 1024; out of range is a 400, not a silent clamp. - `budget_ms` is a soft materialisation budget. When it expires the answer is a partial 200 with `converged: false` and a typed `pending[]` (each entry says why and what to do). Pending fetches are detached, not aborted, so the identical request retried returns strictly more. Pending entries are unsigned; they are not signed absences. - Recorded on 2026-09-28: this exact call returned 200 with `cells_sampled: 16`, `facts_returned: 0`, `converged: false` and 16 `pending` entries (`state: "materializing"`), and nine identical retries over 30 minutes stayed at zero while the responder's fetch path was saturated. Retry a few times with a pause; if `pending` does not shrink, tell the user the upstream fetch is stalled rather than looping, and never report the empty answer as "no data here". - A request with many cold cells and several bands can still pass the responder's 40 s transport budget and return `compute_timeout`. Narrow it: fewer bands, fewer cells, or retry once the first pass has warmed the cache. ## Aggregating The response is per cell; the rollup is yours. Skip absences (a `null` value is a signed statement that the upstream had no data) and guard the empty case: ```sh jq '[.by_cell[].facts[] | select(.band == "copdem30m.elevation_mean" and .value != null) | .value] | if length == 0 then {count: 0} else {count: length, mean: (add / length), min: min, max: max} end' poly.json ``` ## Response shape (abridged) ```jsonc { "by_cell": { "": { "facts": [ ... ], "receipt": { ... }, "fact_order": "tslot_ascending", "bands_already_attested_at_cell": [ ... ] } }, "cells": [ ... ], "cells_sampled": 16, "facts_returned": 16, "area_km2": 120.05, "coverage_fraction": 0.0000133, "is_exhaustive": false, "converged": true, "pending": [], "polygon_bbox": { ..., "source": "..." }, "schema": "emem.recall_polygon.v1" } ``` `polygon_bbox.source` says where the extent came from when you passed a `place`: `centre_cell_bbox` means the geocoder found no polygon and fell back to one cell, so say that rather than presenting a one-cell answer as a regional one. ## Pitfalls - **A sample is not the area.** `coverage_fraction` and `is_exhaustive` state how much of the area the sample covers. Sixteen cells over 120 km² is a sample; report it as one. - **No aggregate receipt.** Each cell's receipt verifies that cell only. To audit the call, pass each `by_cell[].receipt` to [`emem-verify-receipt`](../emem-verify-receipt/SKILL.md). - **History, not just the latest.** Each cell's `facts[]` can carry several readings of one band, oldest first. For "current" take the last per band, or the one whose `tslot` you mean. ## Before you quote the summary An aggregate has no receipt of its own. Say which cells and which statistic, and carry the cells' `memory_token`s (or bundle them with `POST /v1/memory_bundle {"fact_cids":[...]}`) rather than the mean alone. See [`emem-referential-drift`](../emem-referential-drift/SKILL.md) and [`emem-verify-before-publish`](../emem-verify-before-publish/SKILL.md).