A collection manager’s loop — rank targets, plan imagery, check the next pass
You can only task or review so much imagery; the question is which events deserve it, what to collect, and when the next opportunity is.
Steps
- Rank where observation is most worthwhile (collection priority).
- For the top event, get the deterministic SAR + optical plan — the all-weather look can never be silently skipped.
- Check when the place can next be imaged, and by which satellite.
In the web app
See imaging priority on the Watchfloor →
REST
Authenticate with Authorization: Bearer ond_… (create a key at /account/api). Try any of these live in the Playground.
Rank imaging priority
curl "https://offnadir-delta.com/api/v1/collection/priority?top_n=10" \
-H "Authorization: Bearer ond_YOUR_API_KEY"Plan imagery for one event
curl "https://offnadir-delta.com/api/v1/collection/plan?event_id=67f0dcc5-19e4-4a38-8c7c-fc3fa1ad4513&analysis_goal=damage_assessment" \
-H "Authorization: Bearer ond_YOUR_API_KEY"Next passes over the target
curl "https://offnadir-delta.com/api/v1/passes?lat=48.85&lon=2.35" \
-H "Authorization: Bearer ond_YOUR_API_KEY"Python
This is the runnable example shipped with the SDK (pip install offnadir-delta, then set OFFNADIR_DELTA_API_KEY).
"""A collection manager's loop: rank targets, plan the imagery, check the next pass.
This is the workflow the SDK could not express before 0.4.0 — you could read events but
not act on them. Nothing here invents a capability: every step reports what the data can
and cannot support, which is the part a tasking decision actually rests on.
Run with: OFFNADIR_DELTA_API_KEY=ond_... python examples/collection_tasking.py
"""
from offnadir_delta import Client
# bbox = [min_lon, min_lat, max_lon, max_lat].
AOI = [-180.0, -85.0, 180.0, 85.0] # worldwide; narrow this to your own area of interest.
def main() -> None:
with Client() as client:
# 1. WHAT IS WORTH IMAGING. `total_available` vs `returned` matters: a short list
# can mean "few candidates" or "few that could be placed precisely enough to
# image", and those are different situations for a collection manager.
ranked = client.collection.priority(bbox=AOI, top_n=5)
body = ranked.priority
if body is None:
print("No priority body returned.")
return
print(f"{body.returned} target(s) returned of {body.total_available} available")
# An empty list is a real answer, and the exclusion breakdown is the useful part
# of it: it says WHICH gate emptied the funnel. A collection manager needs that,
# not a silent zero.
if not ranked.targets:
print("\nNothing to rank. Excluded by:")
for gate, count in sorted((body.excluded or {}).items(), key=lambda kv: -kv[1]):
if count:
print(f" {gate:32s} {count}")
return
print()
for target in ranked.targets:
print(
f" {target.event_id} {(target.title or '')[:52]:52s} "
f"{target.spec_class or '-':6s} {target.sensor or '-'}"
)
# 2. PLAN THE TOP TARGET. The plan searches each collection exactly once against
# the event footprint, so the all-weather SAR look is never skipped in favour of
# whichever optical scene happened to be cloud-free.
target = ranked.targets[0]
print(f"\nPlanning imagery for {target.event_id}…")
plan = client.collection.plan(
event_id=target.event_id or "", analysis_goal="damage_assessment"
)
for step in (plan.plan.steps if plan.plan else []):
pair = f" sar_pair={step.sar_pair_status}" if step.sar_pair_status else ""
print(
f" {step.collection:18s} returned={step.returned_count} "
f"usable={step.usable_count}{pair}"
)
# 3. WHEN CAN IT NEXT BE SEEN. `collection_mode` is the field to read: a
# systematic satellite will acquire on its own plan (the data will exist), an
# agile one only images if somebody orders it. A pass is an OPPORTUNITY.
if target.lat is None or target.lng is None:
print("\n(no point on the target — pass prediction needs a point or bbox)")
else:
passes = client.collection.passes(lat=target.lat, lon=target.lng, max_passes=5)
if passes.retrieval_ok is False:
print("\n! orbital elements could not be refreshed — windows are indicative")
print()
for p in passes.passes:
mode = p.collection_mode or "-"
print(f" {p.start} {p.satellite:22s} {mode:10s} peak={p.peak_elevation_deg}°")
# 4. WATCH IT — the app's "Watch this area", checked weekly. Creating the order is
# free, and its checks run no model and are not charged. The area has an upper
# size limit, so watch the target's surroundings, not the whole search box.
if target.lat is not None and target.lng is not None:
box = [target.lng - 0.25, target.lat - 0.25, target.lng + 0.25, target.lat + 0.25]
order = client.standing_orders.create(bbox=box, name="collection-tasking-example")
print(f"\n{order.summary}")
if order.watch_id:
# The order joined the Watchlist. Pause or remove it there, as in the app — note
# that deleting a watch also removes everything else bound to the same area.
print(f"(stop it with client.watches.pause({order.watch_id!r}) or .delete(...))")
if __name__ == "__main__":
main()
MCP
Connect once, from any MCP client:
claude mcp add --transport http off-nadir-delta \
https://offnadir-delta.com/api/v1/mcp \
--header "Authorization: Bearer ond_..."The tools this workflow uses: rank_imaging_priority, plan_event_imagery, predict_satellite_passes. See the full roster at /docs/mcp.
Go deeper
From headline to satellite evidence
One connected intelligence workflow across four surfaces — free to start, no GIS software or remote-sensing background required.