Skip to content

Forest Polygon Lookup

The /forest_polygon/lookup endpoint resolves the homogeneous forest patch that contains a given point — a contiguous segment of spectrally-similar forest derived from AlphaEarth Foundations (AEF) embeddings. Each input GeoJSON Point returns one polygon feature, so a caller can turn a coordinate into a mapped forest patch.

It backs the forest fallback of the cadastral parcel search (/cadastral/parcels/search with forest_fallback: true). Cadastral parcel data is only available for the US and supported Canadian provinces (today British Columbia); for a point anywhere outside that footprint there is no parcel to return, so the parcel service calls this endpoint and records the returned forest patch instead. The endpoint is also callable directly for the same point-to-forest-patch lookup.

How it resolves a point

The lookup runs in two tiers, returning the first that produces a segment:

  1. Tier 1 — precomputed store (fast). Reads the segment label covering the point from the precomputed plotIds store and vectorizes it. No live compute. source = "plotids-store".
  2. Tier 2 — live AEF watershed. On a Tier-1 miss, loads a windowed AlphaEarth Foundations (AEF) embedding around the point and runs watershed segmentation to produce the containing segment. source = "aef-watershed".

source_year reports the embedding/store year used (the newest available; the caller does not pick a year).

Boundary smoothing

Returned polygons are smoothed so the boundary is a natural shape rather than a 90-degree pixel staircase from raster vectorization. This uses the same smoothing as the detect-plots pipeline (segmentize → Taubin pre-smooth → Chaikin corner-cutting with structural-corner pinning, bounded to a ~1% area change), so a forest-fallback polygon matches the geometry detect-plots produces. Smoothing is applied in EPSG:4326 before reprojecting to the requested CRS.

Synchronous vs async

Tier-2 watershed segmentation is CPU- and memory-heavy. Two modes are available:

  • Synchronous (default). POST /forest_polygon/lookup resolves inline and returns the FeatureCollection in the response.
  • Async (async_mode=true). The lookup is dispatched to a worker so the heavy segmentation runs off the API service. The endpoint returns 202 with a task_id; the caller polls for completion and then fetches the result. This is the mode the cadastral parcel fallback uses, so concurrent lookups cannot exhaust the API container's memory.

Authentication

Internal endpoint: authenticate with the internal API key (x-internal-api-key plus x-user-id) or a Bearer JWT.

Endpoints

POST /forest_polygon/lookup

Resolve a batch of points.

Query parameters

Name Type Default Description
async_mode bool false If true, dispatch to a worker and return 202 + task_id instead of resolving inline.

Body

{
  "features": [
    { "type": "Feature", "id": "p1", "geometry": { "type": "Point", "coordinates": [-62.2159, -3.4653] } }
  ],
  "output_crs": "EPSG:4326"
}
  • features: GeoJSON Point features (max 100 per request). An optional id correlates each output feature to its input.
  • output_crs: CRS for the returned geometries (default EPSG:4326).

Synchronous response (200) — a FeatureCollection, one feature per input point:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "id": "p1",
      "geometry": { "type": "Polygon", "coordinates": [ ... ] },
      "properties": {
        "input_point": [-62.2159, -3.4653],
        "source": "aef-watershed",
        "source_year": 2025
      }
    }
  ]
}

A point with no segment (outside coverage, or on background/nodata) returns a feature with geometry: null and a properties.warnings entry, so a batch never fails as a whole.

Async response (202) — when async_mode=true:

{
  "task_id": "…",
  "status": "processing",
  "poll_url": "/forest_polygon/{task_id}/status",
  "results_url": "/forest_polygon/{task_id}/results"
}

GET /forest_polygon/{task_id}/status

Returns the task status: {"task_id", "status", "results_url"}. status is processing, completed, or failed.

GET /forest_polygon/{task_id}/results

Returns the resolved FeatureCollection (same shape as the synchronous response) once the task is completed. Returns 409 while still processing and 404 for an unknown task.

Examples

Synchronous:

curl -X POST "$BASE_URL/forest_polygon/lookup" \
  -H "x-internal-api-key: $KEY" -H "x-user-id: $ORG" \
  -H "content-type: application/json" \
  -d '{"features":[{"type":"Feature","id":"p1","geometry":{"type":"Point","coordinates":[-62.2159,-3.4653]}}]}'

Async (dispatch, then poll + fetch):

# 1. dispatch
curl -X POST "$BASE_URL/forest_polygon/lookup?async_mode=true" \
  -H "x-internal-api-key: $KEY" -H "x-user-id: $ORG" \
  -H "content-type: application/json" \
  -d '{"features":[{"type":"Feature","id":"p1","geometry":{"type":"Point","coordinates":[-62.2159,-3.4653]}}]}'
# -> {"task_id":"…","poll_url":"…","results_url":"…"}

# 2. poll status until "completed"
curl "$BASE_URL/forest_polygon/$TASK_ID/status" -H "x-internal-api-key: $KEY" -H "x-user-id: $ORG"

# 3. fetch results
curl "$BASE_URL/forest_polygon/$TASK_ID/results" -H "x-internal-api-key: $KEY" -H "x-user-id: $ORG"