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:
- 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". - 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/lookupresolves 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 returns202with atask_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 optionalidcorrelates each output feature to its input.output_crs: CRS for the returned geometries (defaultEPSG: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"