Tasking¶
CSDA supports tasking commercial satellites. "Tasking" is the process of submitting a request to a commercial satellite vendor to collect data over a specific area within a specific time window (and other possible, optional constraints). Tasking in CSDA is a two step process:
- Tasking proposal: A research user submits a proposal to task one or more satellites. The CSDA review board assesses the proposal and either approves or denies it.
- Tasking request: If their tasking proposal is approved, the user can then make one or more tasking requests against that proposal's (estimated) allocation. The data are collected by the commercial satellite vendor, delivered to CSDA, and made available for download by the user, as well as other CSDA users.
The tasking workflow is usually done via the CSDA frontend, but this notebook demonstrates how you might do it programmatically. Note that Tasking is under active development, so we'll be demonstrating its usage against our staging environment.
Setup¶
For all of these operations, we'll need a CsdaClient.
This client will log in to the Earthdata using either basic (username and password) or netrc authentication.
In this example, we'll assume you've set up your .netrc file per the documentation.
import datetime
from tempfile import NamedTemporaryFile
import json_schema_for_humans.generate
import tabulate
from geojson_pydantic import Point
from geojson_pydantic.types import Position2D
from httpx import NetRCAuth
from IPython.display import Markdown
from json_schema_for_humans.generation_configuration import GenerationConfiguration
from stapi_pydantic import OrderPayload
from csda_client import STAGING_URL, CsdaClient
from csda_client.models import (
CreateTaskingProductRequest,
CreateTaskingProposal,
Grant,
OrderParameters,
)
client = CsdaClient.open(NetRCAuth(), url=STAGING_URL)
client.verify()
'Hello ceholden_devseed, you have a valid token!'
Tasking proposal¶
To create a new proposal, we first need to assemble a few things:
- The
Grantthat we'll be associating with this proposal - One or more vendor's products
First, let's find what grants we have available. If you're running this notebook yourself, make sure to change this value to be your own Earthdata username.
rows = []
grants: dict[str, Grant] = {}
for grant in client.profile("ceholden_devseed").grants:
grants[grant.grant_number] = grant
rows.append([grant.grant_number, grant.start_date, grant.end_date])
tabulate.tabulate(
rows, headers=["Grant number", "Start date", "End date"], tablefmt="html"
)
| Grant number | Start date | End date |
|---|---|---|
| NASA IMPACT | 2027-10-28 |
Now let's list the vendors and find all of them that have tasking enabled.
rows = []
vendors = {}
for vendor in client.vendors():
if vendor.has_tasking:
vendors[vendor.slug] = vendor
rows.append([vendor.id, vendor.slug, vendor.full_name])
tabulate.tabulate(rows, headers=["Id", "Slug", "Full name"], tablefmt="html")
| Id | Slug | Full name |
|---|---|---|
| 12 | ghgsat | GHGSat |
| 17 | capellaspace | Capella Space |
| 18 | iceye | ICEYE US |
| 34 | umbra | Umbra Space |
| 45 | maxar-legion | Maxar Technologies – Legion |
| 47 | airbus | Airbus U.S. |
| 49 | satellogic | Satellogic Inc. |
Let's create a proposal to task Umbra Space. We'll need to see what products are available.
rows = []
products = {}
for product in client.products(vendor_id=vendors["umbra"].id):
products[product.slug] = product
rows.append([product.id, product.slug, product.name, product.long_desc])
tabulate.tabulate(
rows, headers=["Id", "Slug", "Name", "Long description"], tablefmt="html"
)
| Id | Slug | Name | Long description |
|---|---|---|---|
| 72 | CPHD | Compensated Phase History Data | CPHD product preserves raw SAR echo data before image formation, useful for advanced processing tasks |
| 76 | CSI-SIDD | Color Single Image SIDD | It provides clarity and context to spot objects like buildings in a forest or cars moving on a field. |
| 77 | CSI | Color Single Image TIFF | A specialized Color Single Image TIFF product using a three-color (RGB) sub-aperture processing approach to enhance detail. |
| 74 | SIDD | Sensor Independent Detected Data | SIDD offers ground-plane imagery optimized for viewing, omitting phase data but preserving detailed visual information. |
| 75 | GEC | Geocoded Ellipsoid Corrected | This is a radiometrically corrected SAR image with map projection, allowing for easy viewing in common image applications. |
| 73 | SICD | Sensor Independent Complex Data | SICD is a slant-plane image retaining SAR phase and magnitude data |
Let's ask for a "Color Single Image TIFF", referencing it by its slug value.
Here's how to make a tasking proposal.
create_tasking_proposal = CreateTaskingProposal(
name="An excellent tasking proposal",
grant=list(grants.values())[0].id, # select one grant from all of your grants
products=[
CreateTaskingProductRequest(product=products["CSI"].id, n_proposed_granules=42)
],
research_description="Very important things",
tasking_justification="It just won't be collected unless I ask for it",
)
tasking_proposal = client.create_tasking_proposal(
create_tasking_proposal, submit=False
) # We'll just submit this as a draft, since usually you'll be creating a proposal via the frontend
tasking_proposal
TaskingProposal(id=2949, proposal_products=[], is_draft=True, grant=Grant(id=514, grant_number='NASA IMPACT', start_date=None, end_date=datetime.date(2027, 10, 28)), user='ceholden_devseed', name='An excellent tasking proposal', research_description='Very important things', tasking_justification="It just won't be collected unless I ask for it", final_decision_type='', decision_details=None)
Tasking requests¶
Once a tasking proposal is approved by the review board, research users can submit tasking requests against that proposal. Each vendor has its own parameters that can be provided when creating a request. Let's see what parameters are available for Umbra Space.
config = GenerationConfiguration(template_name="md", show_toc=False)
order_parameters = client.get_tasking_order_parameters(
"umbra-sar"
) # There's currently no way to derive this slug, you just have to know it: https://github.com/NASA-IMPACT/csda-project/issues/1682
with NamedTemporaryFile("w", delete_on_close=False) as f:
f.write(order_parameters.model_dump_json())
f.close()
display(
Markdown(
json_schema_for_humans.generate.generate_from_schema(f.name, config=config)
)
)
== Generating tmprqzowgws == == Generated tmprqzowgws in 0:00:00.016576 ==
Umbra Order Parameters¶
Title: Umbra Order Parameters
| Type | object |
| Required | No |
| Additional properties | Not allowed |
| Property | Pattern | Type | Deprecated | Definition | Title/Description |
|---|---|---|---|---|---|
| + proposal_id | No | integer | No | - | Proposal Id |
| + csda_product_ids | No | array of integer | No | - | Csda Product Ids |
| + task_name | No | string | No | - | Task Name |
| - imaging_mode | No | enum (of string) | No | In #/$defs/ImagingMode | Imaging Mode |
| - grazing_angle_min | No | integer | No | - | Grazing Angle Min |
| - grazing_angle_max | No | integer | No | - | Grazing Angle Max |
| - multilook_factor | No | enum (of integer) | No | In #/$defs/MultilookFactor | Multilook Factor |
1. Property Umbra Order Parameters > proposal_id¶
Title: Proposal Id
| Type | integer |
| Required | Yes |
2. Property Umbra Order Parameters > csda_product_ids¶
Title: Csda Product Ids
| Type | array of integer |
| Required | Yes |
| Array restrictions | |
|---|---|
| Min items | 1 |
| Max items | N/A |
| Items unicity | False |
| Additional items | False |
| Tuple validation | See below |
| Each item of this array must be | Description |
|---|---|
| csda_product_ids items | - |
2.1. Umbra Order Parameters > csda_product_ids > csda_product_ids items¶
| Type | integer |
| Required | No |
3. Property Umbra Order Parameters > task_name¶
Title: Task Name
| Type | string |
| Required | Yes |
4. Property Umbra Order Parameters > imaging_mode¶
Title: Imaging Mode
| Type | enum (of string) |
| Required | No |
| Default | "SPOTLIGHT" |
| Defined in | #/$defs/ImagingMode |
Must be one of:
- "SPOTLIGHT"
- "SCAN"
5. Property Umbra Order Parameters > grazing_angle_min¶
Title: Grazing Angle Min
| Type | integer |
| Required | No |
| Default | 45 |
6. Property Umbra Order Parameters > grazing_angle_max¶
Title: Grazing Angle Max
| Type | integer |
| Required | No |
| Default | 65 |
7. Property Umbra Order Parameters > multilook_factor¶
Title: Multilook Factor
| Type | enum (of integer) |
| Required | No |
| Default | 1 |
| Defined in | #/$defs/MultilookFactor |
Must be one of:
- 1
- 2
- 4
- 8
Generated using json-schema-for-humans on 2026-01-09 at 11:49:19 -0500
The required fields are:
proposal_id, which we haveproduct_ids, which we have from via our proposaltask_name, which we can specify
Easy enough! Let's create a tasking request, which is also a STAPI order. We'll create the request for a time period starting tomorrow and ending in a week, over Longmont, Colorado, USA.
order_parameters = OrderParameters.model_validate(
{
"proposal_id": tasking_proposal.id,
"csda_product_ids": [products["CSI"].id],
"task_name": "My tasking proposal",
}
)
order_payload = OrderPayload(
datetime=(
datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(days=1),
datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(days=7),
),
geometry=Point(type="Point", coordinates=Position2D(-105.1019, 40.1672)),
order_parameters=order_parameters,
)
order = client.create_tasking_request("umbra-sar", order_payload)
order
Order(bbox=None, id='8ae9de56-414a-4526-a2cf-3f62d9e51ef6', type='Feature', stapi_type='Order', stapi_version='0.1.0', geometry=Point(bbox=None, type='Point', coordinates=Position2D(longitude=-105.1019, latitude=40.1672)), properties=OrderProperties(product_id='umbra-sar', created=datetime.datetime(2026, 1, 9, 16, 49, 30, 277970, tzinfo=TzInfo(UTC)), status=OrderStatus(timestamp=datetime.datetime(2026, 1, 9, 16, 49, 30, 277970, tzinfo=TzInfo(UTC)), status_code=<OrderStatusCode.received: 'received'>, reason_code=None, reason_text='Order initially created.', links=[]), search_parameters=OrderSearchParameters(datetime=(datetime.datetime(2026, 1, 10, 16, 49, 30, 157450, tzinfo=datetime.timezone.utc), datetime.datetime(2026, 1, 16, 16, 49, 30, 157465, tzinfo=datetime.timezone.utc)), geometry=Point(bbox=None, type='Point', coordinates=Position2D(longitude=-105.1019, latitude=40.1672)), filter=None), opportunity_properties={}, order_parameters={'proposal_id': 2949, 'csda_product_ids': [77], 'task_name': 'My tasking proposal', 'imaging_mode': 'SPOTLIGHT', 'grazing_angle_min': 45, 'grazing_angle_max': 65, 'multilook_factor': 1}, proposal_id=2949, username='ceholden_devseed', csda_product_ids=[77], estimated_granules=0, downloaded_granules=0, linked_granules=0), links=[Link(href=AnyUrl('https://csdap-staging.ds.io/api/v1/stapi/orders/8ae9de56-414a-4526-a2cf-3f62d9e51ef6'), rel='self', type=None, title=None, method=None, headers=None, body=None), Link(href=AnyUrl('https://csdap-staging.ds.io/api/v1/stapi/orders/8ae9de56-414a-4526-a2cf-3f62d9e51ef6/statuses'), rel='monitor', type=None, title=None, method=None, headers=None, body=None)])
After a tasking request is submitted it will be vetted, first by an automatic system and then by CSDA Operations. If it's valid, it will be submitted to the vendor. When the tasking request is fulfilled, CSDA will notify the research user. When data related to the tasking request is delivered, CSDA will catalog it and make it accessible for download.
Accessing your tasked data¶
TODO