Skip to content

Commit 3942a0b

Browse files
authored
Merge pull request #976 from planetlabs/issue966
Add a planetary_variable_source subscription helper
2 parents 79d9a3c + e0156ee commit 3942a0b

File tree

3 files changed

+134
-25
lines changed

3 files changed

+134
-25
lines changed

CHANGES.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
2.1.0 (TBD)
22

33
Added:
4+
- A subscription_request.planetary_variable_source function has been added
5+
(#976).
46
- The subscription_request.build_request function has a new option to clip to
5-
the subscription's source geometry. This is a preview of the default
6-
behavior of the next version of the Subscriptions API.
7+
the subscription's source geometry. This is a preview of the default behavior
8+
of the next version of the Subscriptions API (#971).
79

810
2.0.3 (2023-06-28)
911

planet/subscription_request.py

Lines changed: 110 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# the License.
1414
"""Functionality for preparing subscription requests."""
1515
from datetime import datetime
16-
from typing import Any, Dict, Optional, List, Mapping
16+
from typing import Any, Dict, Optional, List, Literal, Mapping
1717

1818
from . import geojson, specs
1919
from .exceptions import ClientError
@@ -49,7 +49,7 @@ def build_request(name: str,
4949
delivery: Mapping,
5050
notifications: Optional[Mapping] = None,
5151
tools: Optional[List[Mapping]] = None,
52-
clip_to_source=False) -> dict:
52+
clip_to_source: Optional[bool] = False) -> dict:
5353
"""Construct a Subscriptions API request.
5454
5555
The return value can be passed to
@@ -73,12 +73,12 @@ def build_request(name: str,
7373
behavior.
7474
7575
Returns:
76-
A Python dict representation of a Subscriptions API request for
77-
a new subscription.
76+
dict: a representation of a Subscriptions API request for
77+
a new subscription.
7878
7979
Raises:
80-
ClientError when a valid Subscriptions API request can't be
81-
constructed.
80+
ClientError: when a valid Subscriptions API request can't be
81+
constructed.
8282
8383
Examples:
8484
```python
@@ -152,27 +152,34 @@ def catalog_source(
152152
end_time: Optional[datetime] = None,
153153
rrule: Optional[str] = None,
154154
) -> dict:
155-
"""Catalog subscription source.
155+
"""Construct a Catalog subscription source.
156+
157+
The return value can be passed to
158+
[planet.subscription_request.build_request][].
156159
157160
Parameters:
158-
item_types: The class of spacecraft and processing level of the
159-
subscription's matching items, e.g. PSScene.
160-
asset_types: The data products which will be delivered for all subscription
161-
matching items. An item will only match and deliver if all specified
162-
asset types are published for that item.
163-
geometry: The area of interest of the subscription that will be used to
164-
determine matches.
165-
start_time: The start time of the subscription. This time can be in the
166-
past or future.
167-
filter: The filter criteria based on item-level metadata.
168-
end_time: The end time of the subscription. This time can be in the past or
169-
future, and must be after the start_time.
170-
rrule: The recurrence rule, given in iCalendar RFC 5545 format. Only
171-
monthly recurrences are supported at this time.
161+
item_types: The class of spacecraft and processing level of the
162+
subscription's matching items, e.g. PSScene.
163+
asset_types: The data products which will be delivered for all
164+
subscription matching items. An item will only match and
165+
deliver if all specified asset types are published for that
166+
item.
167+
geometry: The area of interest of the subscription that will be
168+
used to determine matches.
169+
start_time: The start time of the subscription. This time can be
170+
in the past or future.
171+
filter: The filter criteria based on item-level metadata.
172+
end_time: The end time of the subscription. This time can be in
173+
the past or future, and must be after the start_time.
174+
rrule: The recurrence rule, given in iCalendar RFC 5545 format.
175+
Only monthly recurrences are supported at this time.
176+
177+
Returns:
178+
dict: a representation of a subscription source.
172179
173180
Raises:
174-
planet.exceptions.ClientError: If start_time or end_time are not valid
175-
datetimes
181+
ClientError: if a source can not be
182+
configured.
176183
"""
177184
if len(item_types) > 1:
178185
raise ClientError(
@@ -212,6 +219,86 @@ def catalog_source(
212219
return {"type": "catalog", "parameters": parameters}
213220

214221

222+
def planetary_variable_source(
223+
var_type: Literal["biomass_proxy",
224+
"land_surface_temperature",
225+
"soil_water_content",
226+
"vegetation_optical_depth"],
227+
var_id: str,
228+
geometry: Mapping,
229+
start_time: datetime,
230+
end_time: Optional[datetime] = None,
231+
) -> dict:
232+
"""Construct a Planetary Variable subscription source.
233+
234+
Planetary Variables come in 4 types and are further subdivided
235+
within these types. See [Subscribing to Planetary
236+
Variables](https://developers.planet.com/docs/subscriptions/pvs-subs/#planetary-variables-types-and-ids)
237+
for details.
238+
239+
The return value can be passed to
240+
[planet.subscription_request.build_request][].
241+
242+
Note: this function does not validate variable types and ids.
243+
244+
Parameters:
245+
var_type: one of "biomass_proxy", "land_surface_temperature",
246+
"soil_water_content", or "vegetation_optical_depth".
247+
var_id: a value such as "SWC-AMSR2-C_V1.0_100" for soil water
248+
content derived from AMSR2 C band.
249+
geometry: The area of interest of the subscription that will be
250+
used to determine matches.
251+
start_time: The start time of the subscription. This time can be
252+
in the past or future.
253+
end_time: The end time of the subscription. This time can be in
254+
the past or future, and must be after the start_time.
255+
256+
Returns:
257+
dict: a representation of a subscription source.
258+
259+
Raises:
260+
ClientError: if a source can not be
261+
configured.
262+
263+
Examples:
264+
```python
265+
>>> source = planetary_variable_source(
266+
... "soil_water_content",
267+
... "SWC-AMSR2-C_V1.0_100",
268+
... geometry={
269+
... "type": "Polygon",
270+
... "coordinates": [[[37.791595458984375, 14.84923123791421],
271+
... [37.90214538574219, 14.84923123791421],
272+
... [37.90214538574219, 14.945448293647944],
273+
... [37.791595458984375, 14.945448293647944],
274+
... [37.791595458984375, 14.84923123791421]]]
275+
... },
276+
... start_time=datetime(2021, 3, 1)
277+
... )
278+
>>> request = build_request(source=source, ...)
279+
```
280+
"""
281+
# TODO: validation of variable types and ids.
282+
283+
parameters = {
284+
"id": var_id,
285+
"geometry": geojson.as_geom(dict(geometry)),
286+
}
287+
288+
try:
289+
parameters['start_time'] = _datetime_to_rfc3339(start_time)
290+
except AttributeError:
291+
raise ClientError('Could not convert start_time to an iso string')
292+
293+
if end_time:
294+
try:
295+
parameters['end_time'] = _datetime_to_rfc3339(end_time)
296+
except AttributeError:
297+
raise ClientError('Could not convert end_time to an iso string')
298+
299+
return {"type": var_type, "parameters": parameters}
300+
301+
215302
def _datetime_to_rfc3339(value: datetime) -> str:
216303
"""Converts the datetime to an RFC3339 string"""
217304
iso = value.isoformat()

tests/unit/test_subscription_request.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,3 +346,23 @@ def test_toar_tool_success():
346346

347347
expected = {"type": "toar", "parameters": {"scale_factor": 12345}}
348348
assert res == expected
349+
350+
351+
def test_pv_source_success(geom_geojson):
352+
"""Configure a planetary variable subscription source."""
353+
# NOTE: this function does not yet validate type and id.
354+
# The nonsense values are intended to fail when the function does
355+
# add validation.
356+
source = subscription_request.planetary_variable_source(
357+
"var1",
358+
"VAR1-abcd",
359+
geometry=geom_geojson,
360+
start_time=datetime(2021, 3, 1),
361+
end_time=datetime(2021, 3, 2),
362+
)
363+
364+
assert source["type"] == "var1"
365+
params = source["parameters"]
366+
assert params["id"] == "VAR1-abcd"
367+
assert params["geometry"] == geom_geojson
368+
assert params["start_time"].startswith("2021-03-01")

0 commit comments

Comments
 (0)