From f0c4d5ba6ec630e9c8e53891e8de85d4a1b4a330 Mon Sep 17 00:00:00 2001 From: Charcey Petersen Date: Tue, 18 Nov 2025 10:26:24 -0600 Subject: [PATCH 1/3] add support for subscription catalog source geometry_relation --- planet/cli/subscriptions.py | 14 +++++++++++++- planet/subscription_request.py | 9 +++++++++ tests/integration/test_subscriptions_cli.py | 21 +++++++++++++++++++++ tests/unit/test_subscription_request.py | 14 ++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/planet/cli/subscriptions.py b/planet/cli/subscriptions.py index 7884247f..410f8882 100644 --- a/planet/cli/subscriptions.py +++ b/planet/cli/subscriptions.py @@ -518,6 +518,16 @@ def request(name, @click.option('--time-range-type', type=click.Choice(["acquired", "published"]), help="Subscribe by acquisition time or time of publication.") +@click.option( + '--geometry-relation', + type=click.Choice(["intersects", "contains", "within"]), + help=("The relationship between the subscription geometry and " + "the item geometry. Intersects (default): Returns items whose " + "footprint geometry partially or fully overlaps with the " + "subscription geometry. Contains: Returns items where the " + "footprint geometry fully encloses the AOI. Within: Returns " + "items whose entire footprint geometry is fully contained " + "within the AOI.")) @pretty def request_catalog(item_types, asset_types, @@ -528,6 +538,7 @@ def request_catalog(item_types, filter, publishing_stages, time_range_type, + geometry_relation, pretty): """Generate a subscriptions request catalog source description.""" @@ -540,7 +551,8 @@ def request_catalog(item_types, rrule=rrule, filter=filter, publishing_stages=publishing_stages, - time_range_type=time_range_type) + time_range_type=time_range_type, + geometry_relation=geometry_relation) echo_json(res, pretty) diff --git a/planet/subscription_request.py b/planet/subscription_request.py index ae5dbf19..64119a86 100644 --- a/planet/subscription_request.py +++ b/planet/subscription_request.py @@ -169,6 +169,8 @@ def catalog_source( "standard", "finalized"]]] = None, time_range_type: Optional[Literal["acquired", "published"]] = None, + geometry_relation: Optional[Literal["intersects", "contains", + "within"]] = None, ) -> dict: """Construct a Catalog subscription source. @@ -195,6 +197,10 @@ def catalog_source( 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". + geometry_relation: The relationship between the subscription geometry and the item geometry. Intersects (default): Returns + items whose footprint geometry partially or fully overlaps with the subscription geometry. + Contains: Returns items where the footprint geometry fully encloses the AOI. + Within: Returns items whose entire footprint geometry is fully contained within the AOI. Returns: dict: a representation of a subscription source. @@ -270,6 +276,9 @@ def catalog_source( if time_range_type: parameters['time_range_type'] = time_range_type + if geometry_relation: + parameters['geometry_relation'] = geometry_relation + return {"parameters": parameters} diff --git a/tests/integration/test_subscriptions_cli.py b/tests/integration/test_subscriptions_cli.py index f40ce048..bfe8f59d 100644 --- a/tests/integration/test_subscriptions_cli.py +++ b/tests/integration/test_subscriptions_cli.py @@ -487,6 +487,27 @@ def test_catalog_source_time_range_type(mock_bundles, assert req['parameters']['time_range_type'] == time_range_type +@pytest.mark.parametrize("geometry_relation", + ["intersects", "contains", "within"]) +def test_catalog_source_geometry_relation(mock_bundles, + invoke, + geom_geojson, + geometry_relation): + """Catalog source time range type is configured.""" + result = invoke([ + 'request-catalog', + '--item-types=PSScene', + '--asset-types=ortho_analytic_4b', + f"--geometry={json.dumps(geom_geojson)}", + '--start-time=2021-03-01T00:00:00', + f'--geometry-relation={geometry_relation}', + ]) + + assert result.exit_code == 0 # success. + req = json.loads(result.output) + assert req['parameters']['geometry_relation'] == geometry_relation + + @pytest.mark.parametrize( "hosting_option, collection_id_option, configuration_option, expected_success", [ diff --git a/tests/unit/test_subscription_request.py b/tests/unit/test_subscription_request.py index d7661e11..49393c0b 100644 --- a/tests/unit/test_subscription_request.py +++ b/tests/unit/test_subscription_request.py @@ -617,6 +617,20 @@ def test_catalog_source_time_range_type_acquired(geom_geojson, mock_bundles): assert source["parameters"]["time_range_type"] == "acquired" +@respx.mock +def test_catalog_source_geometry_relation_contains(geom_geojson, mock_bundles): + """Configure 'contains' geometry relation for a catalog source.""" + source = subscription_request.catalog_source( + item_types=["PSScene"], + asset_types=["ortho_analytic_4b"], + start_time=datetime(2021, 3, 1), + geometry_relation="contains", + geometry=geom_geojson, + ) + + assert source["parameters"]["geometry_relation"] == "contains" + + def test_cloud_filter_tool_success(): res = subscription_request.cloud_filter_tool( clear_percent=subscription_request.FilterValue(gte=90), From 88e76c27bb85a6c174a57336396fc0635f2d83a3 Mon Sep 17 00:00:00 2001 From: charcey <156946780+charcey@users.noreply.github.com> Date: Tue, 18 Nov 2025 12:55:20 -0600 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- planet/subscription_request.py | 8 ++++---- tests/integration/test_subscriptions_cli.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/planet/subscription_request.py b/planet/subscription_request.py index 64119a86..ab53136e 100644 --- a/planet/subscription_request.py +++ b/planet/subscription_request.py @@ -197,10 +197,10 @@ def catalog_source( 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". - geometry_relation: The relationship between the subscription geometry and the item geometry. Intersects (default): Returns - items whose footprint geometry partially or fully overlaps with the subscription geometry. - Contains: Returns items where the footprint geometry fully encloses the AOI. - Within: Returns items whose entire footprint geometry is fully contained within the AOI. + geometry_relation: The relationship between the subscription geometry and the item geometry. + 'intersects' (default): Returns items whose footprint geometry partially or fully overlaps with the subscription geometry. + 'contains': Returns items where the footprint geometry fully encloses the AOI. + 'within': Returns items whose entire footprint geometry is fully contained within the AOI. Returns: dict: a representation of a subscription source. diff --git a/tests/integration/test_subscriptions_cli.py b/tests/integration/test_subscriptions_cli.py index bfe8f59d..acafc652 100644 --- a/tests/integration/test_subscriptions_cli.py +++ b/tests/integration/test_subscriptions_cli.py @@ -493,7 +493,7 @@ def test_catalog_source_geometry_relation(mock_bundles, invoke, geom_geojson, geometry_relation): - """Catalog source time range type is configured.""" + """Catalog source geometry relation is configured.""" result = invoke([ 'request-catalog', '--item-types=PSScene', From 22ae6b978a80abed63ca9f5d925fbb4b48fe3cc2 Mon Sep 17 00:00:00 2001 From: charcey <156946780+charcey@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:47:57 -0600 Subject: [PATCH 3/3] reformat help string --- planet/cli/subscriptions.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/planet/cli/subscriptions.py b/planet/cli/subscriptions.py index 410f8882..b267f448 100644 --- a/planet/cli/subscriptions.py +++ b/planet/cli/subscriptions.py @@ -521,13 +521,15 @@ def request(name, @click.option( '--geometry-relation', type=click.Choice(["intersects", "contains", "within"]), - help=("The relationship between the subscription geometry and " - "the item geometry. Intersects (default): Returns items whose " - "footprint geometry partially or fully overlaps with the " - "subscription geometry. Contains: Returns items where the " - "footprint geometry fully encloses the AOI. Within: Returns " - "items whose entire footprint geometry is fully contained " - "within the AOI.")) + help= # noqa: E251 + ('\b\n' + 'The relationship between the subscription geometry and the item geometry.\n' + 'intersects (default): Returns items whose footprint geometry partially or \n' + 'fully overlaps with the subscription geometry.\n' + 'contains: Returns items where the footprint geometry fully encloses the \n' + 'subscription geometry.\n' + 'within: Returns items whose entire footprint geometry is fully contained \n' + 'within the subscription geometry.')) @pretty def request_catalog(item_types, asset_types,