Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
2.1.0 (TBD)

2.1b1 (2023-07-11)

Added:
- The request command of the subscriptions CLI has a new --clip-to-source
option (#988).
- A new request-pv command has been added to the subscriptions CLi (#988).
- Support for catalog source publishing stages (#977) and time range types
(#978) have been added to subscription_request.catalog_source.
- Add the option to get Planetary Variable subscription results as a CSV file
Expand Down
2 changes: 1 addition & 1 deletion planet/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '2.1.0dev'
__version__ = '2.1dev'
70 changes: 67 additions & 3 deletions planet/cli/subscriptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,14 +238,32 @@ async def list_subscription_results_cmd(ctx,
'--tools',
type=types.JSON(),
help='Toolchain JSON. Can be a string, filename, or - for stdin.')
@click.option(
'--clip-to-source',
is_flag=True,
default=False,
help="Clip to the source geometry without specifying a clip tool.")
@pretty
def request(name, source, delivery, notifications, tools, pretty):
"""Generate a subscriptions request."""
def request(name,
source,
delivery,
notifications,
tools,
clip_to_source,
pretty):
"""Generate a subscriptions request.

Note: the next version of the Subscription API will remove the clip
tool option and always clip to the source geometry. Thus the
--clip-to-source option is a preview of the next API version's
default behavior.
"""
res = subscription_request.build_request(name,
source,
delivery,
notifications=notifications,
tools=tools)
tools=tools,
clip_to_source=clip_to_source)
echo_json(res, pretty)


Expand Down Expand Up @@ -298,3 +316,49 @@ def request_catalog(item_types,
rrule=rrule,
filter=filter)
echo_json(res, pretty)


@subscriptions.command() # type: ignore
@translate_exceptions
@click.option(
'--var-type',
required=True,
help='Planetary variable type.',
type=click.Choice([
"biomass_proxy",
"land_surface_temperature",
"soil_water_content",
"vegetation_optical_depth"
]),
)
@click.option('--var-id', required=True, help='Planetary variable id.')
@click.option(
'--geometry',
required=True,
type=types.JSON(),
help="""Geometry of the area of interest of the subscription that will be
used to determine matches. Can be a string, filename, or - for stdin.""")
@click.option('--start-time',
required=True,
type=types.DateTime(),
help='Date and time to begin subscription.')
@click.option('--end-time',
type=types.DateTime(),
help='Date and time to end subscription.')
@pretty
def request_pv(var_type, var_id, geometry, start_time, end_time, pretty):
"""Generate a Planetary Variable subscription source.

Planetary Variables come in 4 types and are further subdivided
within these types. See [Subscribing to Planetary
Variables](https://developers.planet.com/docs/subscriptions/pvs-subs/#planetary-variables-types-and-ids)
for details.
"""
res = subscription_request.planetary_variable_source(
var_type,
var_id,
geometry,
start_time,
end_time=end_time,
)
echo_json(res, pretty)
138 changes: 75 additions & 63 deletions planet/subscription_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,30 +81,32 @@ def build_request(name: str,
constructed.

Examples:

```python
>>> from datetime import datetime
>>> from planet.subscription_request import (
... build_request, catalog_source, amazon_s3)
...
... geom = {
... "coordinates": [[[139.5648193359375,35.42374884923695],
... [140.1031494140625,35.42374884923695],
... [140.1031494140625,35.77102915686019],
... [139.5648193359375,35.77102915686019],
... [139.5648193359375,35.42374884923695]]],
... "type": "Polygon"
... }
>>> source = catalog_source(
... ["PSScene"], ["ortho_analytic_4b"], geom, datetime(2021,3,1))

>>> delivery = amazon_s3(
... ACCESS_KEY_ID, SECRET_ACCESS_KEY, "test", "us-east-1")
...
>>> subscription_request = build_request(
... 'test_subscription', source=source, delivery=delivery)
...

```
from datetime import datetime
from planet.subscription_request import build_request, catalog_source, amazon_s3

geom = {
"coordinates": [
[
[139.5648193359375, 35.42374884923695],
[140.1031494140625, 35.42374884923695],
[140.1031494140625, 35.77102915686019],
[139.5648193359375, 35.77102915686019],
[139.5648193359375, 35.42374884923695],
]
],
"type": "Polygon",
}

source = catalog_source(["PSScene"], ["ortho_analytic_4b"], geom, datetime(2021, 3, 1))

delivery = amazon_s3(ACCESS_KEY_ID, SECRET_ACCESS_KEY, "test", "us-east-1")

subscription_request = build_request(
"test_subscription", source=source, delivery=delivery
)
```
"""
# Because source and delivery are Mappings we must make copies for
# the function's return value. dict() shallow copies a Mapping
Expand All @@ -116,8 +118,8 @@ def build_request(name: str,
if notifications:
details['notifications'] = dict(notifications)

if tools:
tool_list = [dict(tool) for tool in tools]
if tools or clip_to_source:
tool_list = [dict(tool) for tool in (tools or [])]

# If clip_to_source is True a clip configuration will be added
# to the list of requested tools unless an existing clip tool
Expand Down Expand Up @@ -179,6 +181,7 @@ def catalog_source(
Only monthly recurrences are supported at this time.
publishing_stages: A sequence of one or more of the values
"preview", "standard", or "finalized".
time_range_type: "acquired" (new in 2.1.0) or "published".

Returns:
dict: a representation of a subscription source.
Expand All @@ -188,28 +191,30 @@ def catalog_source(
configured.

Examples:
```pycon
>>> source = catalog_source(
... ["PSScene"],
... ["ortho_analytic_4b"],
... geometry={
... "type": "Polygon",
... "coordinates": [[[37.791595458984375, 14.84923123791421],
... [37.90214538574219, 14.84923123791421],
... [37.90214538574219, 14.945448293647944],
... [37.791595458984375, 14.945448293647944],
... [37.791595458984375, 14.84923123791421]]]
... },
... start_time=datetime(2021, 3, 1),
... publishing_stages=["standard"],
... time_range_type="acquired",
... )
>>> request = build_request(
... "Standard PSScene Ortho Analytic",
... source=source,
... delivery={})
```

```python
source = catalog_source(
["PSScene"],
["ortho_analytic_4b"],
geometry={
"type": "Polygon",
"coordinates": [
[
[37.791595458984375, 14.84923123791421],
[37.90214538574219, 14.84923123791421],
[37.90214538574219, 14.945448293647944],
[37.791595458984375, 14.945448293647944],
[37.791595458984375, 14.84923123791421],
]
],
},
start_time=datetime(2021, 3, 1),
publishing_stages=["standard"],
time_range_type="acquired",
)

request = build_request("Standard PSScene Ortho Analytic", source=source, delivery={})
```
"""
if len(item_types) > 1:
raise ClientError(
Expand Down Expand Up @@ -297,24 +302,31 @@ def planetary_variable_source(
configured.

Examples:

```python
>>> source = planetary_variable_source(
... "soil_water_content",
... "SWC-AMSR2-C_V1.0_100",
... geometry={
... "type": "Polygon",
... "coordinates": [[[37.791595458984375, 14.84923123791421],
... [37.90214538574219, 14.84923123791421],
... [37.90214538574219, 14.945448293647944],
... [37.791595458984375, 14.945448293647944],
... [37.791595458984375, 14.84923123791421]]]
... },
... start_time=datetime(2021, 3, 1)
... )
>>> request = build_request(
... "Soil Water Content",
... source=source,
... delivery={})
pv_source = planetary_variables_source(
"soil_water_content",
"SWC-AMSR2-C_V1.0_100",
geometry={
"type": "Polygon",
"coordinates": [
[
[37.791595458984375, 14.84923123791421],
[37.90214538574219, 14.84923123791421],
[37.90214538574219, 14.945448293647944],
[37.791595458984375, 14.945448293647944],
[37.791595458984375, 14.84923123791421],
]
],
},
start_time=datetime(2021, 3, 1),
)

request = build_request(
"soil_water_subscription",
source=pv_source,
delivery={},
)
```
"""
# TODO: validation of variable types and ids.
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/test_subscriptions_api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Tests of the Planet Subscriptions API client."""

import csv
from datetime import datetime
from itertools import zip_longest
import json
Expand Down Expand Up @@ -288,7 +289,6 @@ async def test_get_results_csv():
async with Session() as session:
client = SubscriptionsClient(session, base_url=TEST_URL)
results = [res async for res in client.get_results_csv("42")]
import csv
rows = list(csv.reader(results))
assert rows == [['id', 'status'], ['1234-abcd', 'SUCCESS']]

Expand Down
44 changes: 43 additions & 1 deletion tests/integration/test_subscriptions_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ def test_subscriptions_results_success(invoke, options, expected_count):


def test_request_base_success(invoke, geom_geojson):
"""Request command succeeds"""
"""Request command succeeds."""
source = json.dumps({
"type": "catalog",
"parameters": {
Expand Down Expand Up @@ -285,6 +285,32 @@ def test_request_base_success(invoke, geom_geojson):
assert result.exit_code == 0 # success.


def test_request_base_clip_to_source(invoke, geom_geojson):
"""Clip to source using command line option."""
source = json.dumps({
"type": "catalog",
"parameters": {
"geometry": geom_geojson,
"start_time": "2021-03-01T00:00:00Z",
"item_types": ["PSScene"],
"asset_types": ["ortho_analytic_4b"]
}
})
result = invoke([
'request',
'--name=test',
f'--source={source}',
'--delivery={"type": "fake"}',
'--clip-to-source'
])

assert result.exit_code == 0 # success.
req = json.loads(result.output)
tool = req["tools"][0]
assert tool["type"] == "clip"
assert tool["parameters"]["aoi"] == geom_geojson


def test_request_catalog_success(invoke, geom_geojson):
"""Request-catalog command succeeds"""
source = {
Expand Down Expand Up @@ -314,3 +340,19 @@ def test_subscriptions_results_csv(invoke):
result = invoke(['results', 'test', '--csv'])
assert result.exit_code == 0 # success.
assert result.output.splitlines() == ['id,status', '1234-abcd,SUCCESS']


def test_request_pv_success(invoke, geom_geojson):
"""Request-pv command succeeds"""
result = invoke([
'request-pv',
'--var-type=biomass_proxy',
'--var-id=BIOMASS-PROXY_V3.0_10',
f"--geometry={json.dumps(geom_geojson)}",
'--start-time=2021-03-01T00:00:00'
])

assert result.exit_code == 0 # success.
source = json.loads(result.output)
assert source["type"] == "biomass_proxy"
assert source["parameters"]["id"] == "BIOMASS-PROXY_V3.0_10"