planet 2.6.dev0__tar.gz → 2.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {planet-2.6.dev0 → planet-2.7}/PKG-INFO +1 -1
- planet-2.7/planet/__version__.py +1 -0
- {planet-2.6.dev0 → planet-2.7}/planet/cli/data.py +22 -6
- {planet-2.6.dev0 → planet-2.7}/planet/cli/subscriptions.py +6 -2
- {planet-2.6.dev0 → planet-2.7}/planet/cli/types.py +14 -0
- planet-2.7/planet/cli/validators.py +27 -0
- {planet-2.6.dev0 → planet-2.7}/planet/clients/data.py +17 -1
- {planet-2.6.dev0 → planet-2.7}/planet/data_filter.py +4 -3
- {planet-2.6.dev0 → planet-2.7}/planet/exceptions.py +4 -0
- {planet-2.6.dev0 → planet-2.7}/planet/geojson.py +73 -19
- {planet-2.6.dev0 → planet-2.7}/planet/order_request.py +2 -2
- {planet-2.6.dev0 → planet-2.7}/planet/subscription_request.py +12 -10
- {planet-2.6.dev0 → planet-2.7}/planet.egg-info/PKG-INFO +1 -1
- {planet-2.6.dev0 → planet-2.7}/planet.egg-info/SOURCES.txt +1 -0
- planet-2.6.dev0/planet/__version__.py +0 -1
- {planet-2.6.dev0 → planet-2.7}/LICENSE +0 -0
- {planet-2.6.dev0 → planet-2.7}/README.md +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/__init__.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/auth.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/cli/__init__.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/cli/auth.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/cli/cli.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/cli/cmds.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/cli/collect.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/cli/io.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/cli/options.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/cli/orders.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/cli/session.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/clients/__init__.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/clients/orders.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/clients/subscriptions.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/constants.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/data/Feature.json +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/data/README.md +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/data/orders_product_bundle_2023-02-24.json +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/http.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/io.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/models.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/reporting.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet/specs.py +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet.egg-info/dependency_links.txt +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet.egg-info/entry_points.txt +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet.egg-info/not-zip-safe +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet.egg-info/requires.txt +0 -0
- {planet-2.6.dev0 → planet-2.7}/planet.egg-info/top_level.txt +0 -0
- {planet-2.6.dev0 → planet-2.7}/setup.cfg +0 -0
- {planet-2.6.dev0 → planet-2.7}/setup.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.7'
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
from typing import List, Optional
|
|
16
16
|
from contextlib import asynccontextmanager
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
|
|
19
18
|
import click
|
|
20
19
|
|
|
21
20
|
from planet.reporting import AssetStatusBar
|
|
@@ -37,6 +36,7 @@ from .cmds import coro, translate_exceptions
|
|
|
37
36
|
from .io import echo_json
|
|
38
37
|
from .options import limit, pretty
|
|
39
38
|
from .session import CliSession
|
|
39
|
+
from .validators import check_geom
|
|
40
40
|
|
|
41
41
|
valid_item_string = "Valid entries for ITEM_TYPES: " + "|".join(
|
|
42
42
|
get_data_item_types())
|
|
@@ -281,6 +281,7 @@ def filter(ctx,
|
|
|
281
281
|
@click.argument("item_types",
|
|
282
282
|
type=types.CommaSeparatedString(),
|
|
283
283
|
callback=check_item_types)
|
|
284
|
+
@click.option("--geom", type=types.Geometry(), callback=check_geom)
|
|
284
285
|
@click.option('--filter',
|
|
285
286
|
type=types.JSON(),
|
|
286
287
|
help="""Apply specified filter to search. Can be a json string,
|
|
@@ -293,7 +294,7 @@ def filter(ctx,
|
|
|
293
294
|
show_default=True,
|
|
294
295
|
help='Field and direction to order results by.')
|
|
295
296
|
@pretty
|
|
296
|
-
async def search(ctx, item_types, filter, limit, name, sort, pretty):
|
|
297
|
+
async def search(ctx, item_types, geom, filter, limit, name, sort, pretty):
|
|
297
298
|
"""Execute a structured item search.
|
|
298
299
|
|
|
299
300
|
This function outputs a series of GeoJSON descriptions, one for each of the
|
|
@@ -311,6 +312,7 @@ async def search(ctx, item_types, filter, limit, name, sort, pretty):
|
|
|
311
312
|
async with data_client(ctx) as cl:
|
|
312
313
|
|
|
313
314
|
async for item in cl.search(item_types,
|
|
315
|
+
geometry=geom,
|
|
314
316
|
search_filter=filter,
|
|
315
317
|
name=name,
|
|
316
318
|
sort=sort,
|
|
@@ -325,6 +327,7 @@ async def search(ctx, item_types, filter, limit, name, sort, pretty):
|
|
|
325
327
|
@click.argument("item_types",
|
|
326
328
|
type=types.CommaSeparatedString(),
|
|
327
329
|
callback=check_item_types)
|
|
330
|
+
@click.option("--geom", type=types.Geometry(), callback=check_geom)
|
|
328
331
|
@click.option(
|
|
329
332
|
'--filter',
|
|
330
333
|
type=types.JSON(),
|
|
@@ -339,7 +342,13 @@ async def search(ctx, item_types, filter, limit, name, sort, pretty):
|
|
|
339
342
|
is_flag=True,
|
|
340
343
|
help='Send a daily email when new results are added.')
|
|
341
344
|
@pretty
|
|
342
|
-
async def search_create(ctx,
|
|
345
|
+
async def search_create(ctx,
|
|
346
|
+
item_types,
|
|
347
|
+
geom,
|
|
348
|
+
filter,
|
|
349
|
+
name,
|
|
350
|
+
daily_email,
|
|
351
|
+
pretty):
|
|
343
352
|
"""Create a new saved structured item search.
|
|
344
353
|
|
|
345
354
|
This function outputs a full JSON description of the created search,
|
|
@@ -349,6 +358,7 @@ async def search_create(ctx, item_types, filter, name, daily_email, pretty):
|
|
|
349
358
|
"""
|
|
350
359
|
async with data_client(ctx) as cl:
|
|
351
360
|
items = await cl.create_search(item_types=item_types,
|
|
361
|
+
geometry=geom,
|
|
352
362
|
search_filter=filter,
|
|
353
363
|
name=name,
|
|
354
364
|
enable_email=daily_email)
|
|
@@ -485,6 +495,10 @@ async def search_delete(ctx, search_id):
|
|
|
485
495
|
type=str,
|
|
486
496
|
required=True,
|
|
487
497
|
help='Name of the saved search.')
|
|
498
|
+
@click.option("--geom",
|
|
499
|
+
type=types.Geometry(),
|
|
500
|
+
callback=check_geom,
|
|
501
|
+
default=None)
|
|
488
502
|
@click.option('--daily-email',
|
|
489
503
|
is_flag=True,
|
|
490
504
|
help='Send a daily email when new results are added.')
|
|
@@ -493,6 +507,7 @@ async def search_update(ctx,
|
|
|
493
507
|
search_id,
|
|
494
508
|
item_types,
|
|
495
509
|
filter,
|
|
510
|
+
geom,
|
|
496
511
|
name,
|
|
497
512
|
daily_email,
|
|
498
513
|
pretty):
|
|
@@ -504,9 +519,10 @@ async def search_update(ctx,
|
|
|
504
519
|
async with data_client(ctx) as cl:
|
|
505
520
|
items = await cl.update_search(search_id,
|
|
506
521
|
item_types,
|
|
507
|
-
filter,
|
|
508
|
-
name,
|
|
509
|
-
|
|
522
|
+
search_filter=filter,
|
|
523
|
+
name=name,
|
|
524
|
+
geometry=geom,
|
|
525
|
+
enable_email=daily_email)
|
|
510
526
|
echo_json(items, pretty)
|
|
511
527
|
|
|
512
528
|
|
|
@@ -13,6 +13,7 @@ from planet.clients.subscriptions import SubscriptionsClient
|
|
|
13
13
|
from .. import subscription_request
|
|
14
14
|
from ..subscription_request import sentinel_hub
|
|
15
15
|
from ..specs import get_item_types, validate_item_type, SpecificationException
|
|
16
|
+
from .validators import check_geom
|
|
16
17
|
|
|
17
18
|
ALL_ITEM_TYPES = get_item_types()
|
|
18
19
|
valid_item_string = "Valid entries for ITEM_TYPES: " + "|".join(ALL_ITEM_TYPES)
|
|
@@ -346,7 +347,8 @@ def request(name,
|
|
|
346
347
|
@click.option(
|
|
347
348
|
'--geometry',
|
|
348
349
|
required=True,
|
|
349
|
-
type=types.
|
|
350
|
+
type=types.Geometry(),
|
|
351
|
+
callback=check_geom,
|
|
350
352
|
help="""Geometry of the area of interest of the subscription that will be
|
|
351
353
|
used to determine matches. Can be a string, filename, or - for stdin.""")
|
|
352
354
|
@click.option('--start-time',
|
|
@@ -406,6 +408,7 @@ def request_catalog(item_types,
|
|
|
406
408
|
required=True,
|
|
407
409
|
help='Planetary variable type.',
|
|
408
410
|
type=click.Choice([
|
|
411
|
+
"analysis_ready_ps",
|
|
409
412
|
"biomass_proxy",
|
|
410
413
|
"land_surface_temperature",
|
|
411
414
|
"soil_water_content",
|
|
@@ -418,7 +421,8 @@ def request_catalog(item_types,
|
|
|
418
421
|
@click.option(
|
|
419
422
|
'--geometry',
|
|
420
423
|
required=True,
|
|
421
|
-
type=types.
|
|
424
|
+
type=types.Geometry(),
|
|
425
|
+
callback=check_geom,
|
|
422
426
|
help="""Geometry of the area of interest of the subscription that will be
|
|
423
427
|
used to determine matches. Can be a string, filename, or - for stdin.""")
|
|
424
428
|
@click.option('--start-time',
|
|
@@ -93,6 +93,20 @@ class JSON(click.ParamType):
|
|
|
93
93
|
return convdict
|
|
94
94
|
|
|
95
95
|
|
|
96
|
+
class Geometry(click.ParamType):
|
|
97
|
+
name = 'geom'
|
|
98
|
+
|
|
99
|
+
def __init__(self):
|
|
100
|
+
self.types = [JSON(), CommaSeparatedString()]
|
|
101
|
+
|
|
102
|
+
def convert(self, value, param, ctx):
|
|
103
|
+
for type in self.types:
|
|
104
|
+
try:
|
|
105
|
+
return type.convert(value, param, ctx)
|
|
106
|
+
except click.BadParameter:
|
|
107
|
+
continue
|
|
108
|
+
|
|
109
|
+
|
|
96
110
|
class Field(click.ParamType):
|
|
97
111
|
"""Clarify that this entry is for a field"""
|
|
98
112
|
name = 'field'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Copyright 2022 Planet Labs, PBC.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
4
|
+
# use this file except in compliance with the License. You may obtain a copy of
|
|
5
|
+
# the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
11
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
12
|
+
# License for the specific language governing permissions and limitations under
|
|
13
|
+
# the License.
|
|
14
|
+
"""CLI Parameter validation"""
|
|
15
|
+
from typing import Optional
|
|
16
|
+
from planet import geojson
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def check_geom(ctx, param, geometry) -> Optional[dict]:
|
|
20
|
+
"""Validates geometry as GeoJSON or feature ref(s)."""
|
|
21
|
+
if isinstance(geometry, dict):
|
|
22
|
+
return geojson.as_geom_or_ref(geometry)
|
|
23
|
+
geoms = {}
|
|
24
|
+
if geometry:
|
|
25
|
+
for geom in geometry:
|
|
26
|
+
geoms.update(geojson.as_geom_or_ref(geom))
|
|
27
|
+
return geoms if geoms else None
|
|
@@ -26,6 +26,7 @@ from ..constants import PLANET_BASE_URL
|
|
|
26
26
|
from ..http import Session
|
|
27
27
|
from ..models import Paged, StreamingBody
|
|
28
28
|
from ..specs import validate_data_item_type
|
|
29
|
+
from ..geojson import as_geom_or_ref
|
|
29
30
|
|
|
30
31
|
BASE_URL = f'{PLANET_BASE_URL}/data/v1/'
|
|
31
32
|
SEARCHES_PATH = '/searches'
|
|
@@ -112,6 +113,7 @@ class DataClient:
|
|
|
112
113
|
|
|
113
114
|
async def search(self,
|
|
114
115
|
item_types: List[str],
|
|
116
|
+
geometry: Optional[dict] = None,
|
|
115
117
|
search_filter: Optional[dict] = None,
|
|
116
118
|
name: Optional[str] = None,
|
|
117
119
|
sort: Optional[str] = None,
|
|
@@ -134,6 +136,8 @@ class DataClient:
|
|
|
134
136
|
sort: Field and direction to order results by. Valid options are
|
|
135
137
|
given in SEARCH_SORT.
|
|
136
138
|
name: The name of the saved search.
|
|
139
|
+
geometry: GeoJSON, a feature reference or a list of feature
|
|
140
|
+
references
|
|
137
141
|
limit: Maximum number of results to return. When set to 0, no
|
|
138
142
|
maximum is applied.
|
|
139
143
|
|
|
@@ -149,6 +153,9 @@ class DataClient:
|
|
|
149
153
|
|
|
150
154
|
item_types = [validate_data_item_type(item) for item in item_types]
|
|
151
155
|
request_json = {'filter': search_filter, 'item_types': item_types}
|
|
156
|
+
|
|
157
|
+
if geometry:
|
|
158
|
+
request_json['geometry'] = as_geom_or_ref(geometry)
|
|
152
159
|
if name:
|
|
153
160
|
request_json['name'] = name
|
|
154
161
|
|
|
@@ -159,7 +166,6 @@ class DataClient:
|
|
|
159
166
|
raise exceptions.ClientError(
|
|
160
167
|
f'{sort} must be one of {SEARCH_SORT}')
|
|
161
168
|
params['_sort'] = sort
|
|
162
|
-
|
|
163
169
|
response = await self._session.request(method='POST',
|
|
164
170
|
url=url,
|
|
165
171
|
json=request_json,
|
|
@@ -171,6 +177,7 @@ class DataClient:
|
|
|
171
177
|
item_types: List[str],
|
|
172
178
|
search_filter: dict,
|
|
173
179
|
name: str,
|
|
180
|
+
geometry: Optional[dict] = None,
|
|
174
181
|
enable_email: bool = False) -> dict:
|
|
175
182
|
"""Create a new saved structured item search.
|
|
176
183
|
|
|
@@ -192,6 +199,7 @@ class DataClient:
|
|
|
192
199
|
|
|
193
200
|
Parameters:
|
|
194
201
|
item_types: The item types to include in the search.
|
|
202
|
+
geometry: A feature reference or a GeoJSON
|
|
195
203
|
search_filter: Structured search criteria.
|
|
196
204
|
name: The name of the saved search.
|
|
197
205
|
enable_email: Send a daily email when new results are added.
|
|
@@ -205,12 +213,15 @@ class DataClient:
|
|
|
205
213
|
url = self._searches_url()
|
|
206
214
|
|
|
207
215
|
item_types = [validate_data_item_type(item) for item in item_types]
|
|
216
|
+
|
|
208
217
|
request = {
|
|
209
218
|
'name': name,
|
|
210
219
|
'filter': search_filter,
|
|
211
220
|
'item_types': item_types,
|
|
212
221
|
'__daily_email_enabled': enable_email
|
|
213
222
|
}
|
|
223
|
+
if geometry:
|
|
224
|
+
request['geometry'] = as_geom_or_ref(geometry)
|
|
214
225
|
|
|
215
226
|
response = await self._session.request(method='POST',
|
|
216
227
|
url=url,
|
|
@@ -222,12 +233,14 @@ class DataClient:
|
|
|
222
233
|
item_types: List[str],
|
|
223
234
|
search_filter: dict,
|
|
224
235
|
name: str,
|
|
236
|
+
geometry: Optional[dict] = None,
|
|
225
237
|
enable_email: bool = False) -> dict:
|
|
226
238
|
"""Update an existing saved search.
|
|
227
239
|
|
|
228
240
|
Parameters:
|
|
229
241
|
search_id: Saved search identifier.
|
|
230
242
|
item_types: The item types to include in the search.
|
|
243
|
+
geometry: A feature reference or a GeoJSON
|
|
231
244
|
search_filter: Structured search criteria.
|
|
232
245
|
name: The name of the saved search.
|
|
233
246
|
enable_email: Send a daily email when new results are added.
|
|
@@ -238,12 +251,15 @@ class DataClient:
|
|
|
238
251
|
url = f'{self._searches_url()}/{search_id}'
|
|
239
252
|
|
|
240
253
|
item_types = [validate_data_item_type(item) for item in item_types]
|
|
254
|
+
|
|
241
255
|
request = {
|
|
242
256
|
'name': name,
|
|
243
257
|
'filter': search_filter,
|
|
244
258
|
'item_types': item_types,
|
|
245
259
|
'__daily_email_enabled': enable_email
|
|
246
260
|
}
|
|
261
|
+
if geometry:
|
|
262
|
+
request['geometry'] = geometry
|
|
247
263
|
|
|
248
264
|
response = await self._session.request(method='PUT',
|
|
249
265
|
url=url,
|
|
@@ -238,9 +238,10 @@ def geometry_filter(geom: dict) -> dict:
|
|
|
238
238
|
geom: GeoJSON describing the filter geometry, feature, or feature
|
|
239
239
|
collection.
|
|
240
240
|
"""
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
241
|
+
geom_filter = _field_filter('GeometryFilter',
|
|
242
|
+
field_name='geometry',
|
|
243
|
+
config=geojson.validate_geom_as_geojson(geom))
|
|
244
|
+
return geom_filter
|
|
244
245
|
|
|
245
246
|
|
|
246
247
|
def number_in_filter(field_name: str, values: List[float]) -> dict:
|
|
@@ -12,23 +12,22 @@
|
|
|
12
12
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
13
13
|
# License for the specific language governing permissions and limitations under
|
|
14
14
|
# the License.
|
|
15
|
-
"""Functionality for interacting with GeoJSON."""
|
|
15
|
+
"""Functionality for interacting with GeoJSON and planet references."""
|
|
16
16
|
import json
|
|
17
17
|
import logging
|
|
18
18
|
import typing
|
|
19
19
|
|
|
20
20
|
import geojson as gj
|
|
21
21
|
from jsonschema import Draft7Validator
|
|
22
|
-
|
|
23
22
|
from .constants import DATA_DIR
|
|
24
|
-
from .exceptions import GeoJSONError
|
|
23
|
+
from .exceptions import GeoJSONError, FeatureError
|
|
25
24
|
|
|
26
|
-
GEOJSON_TYPES = [
|
|
25
|
+
GEOJSON_TYPES = ["Feature"]
|
|
27
26
|
|
|
28
27
|
LOGGER = logging.getLogger(__name__)
|
|
29
28
|
|
|
30
29
|
|
|
31
|
-
def
|
|
30
|
+
def as_geom_or_ref(data) -> dict:
|
|
32
31
|
"""Extract the geometry from GeoJSON and validate.
|
|
33
32
|
|
|
34
33
|
Parameters:
|
|
@@ -42,13 +41,65 @@ def as_geom(data: dict) -> dict:
|
|
|
42
41
|
or FeatureCollection or if more than one Feature is in a
|
|
43
42
|
FeatureCollection.
|
|
44
43
|
"""
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
if isinstance(data, str):
|
|
45
|
+
return as_ref(data)
|
|
46
|
+
geom_type = data['type']
|
|
47
|
+
if geom_type == 'ref':
|
|
48
|
+
return as_ref(data)
|
|
49
|
+
else:
|
|
50
|
+
geom = geom_from_geojson(data)
|
|
51
|
+
validate_geom_as_geojson(geom)
|
|
52
|
+
return geom
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def validate_ref(uri) -> bool:
|
|
56
|
+
if uri is None:
|
|
57
|
+
raise FeatureError("Expected str, not None")
|
|
58
|
+
parts = uri.split("/", 4)
|
|
59
|
+
if parts[0] != "pl:features":
|
|
60
|
+
raise FeatureError("Expected scheme pl:features")
|
|
61
|
+
path = parts[1:]
|
|
62
|
+
if len(path) < 2:
|
|
63
|
+
raise FeatureError("Expceted dataset/collection path")
|
|
64
|
+
return True
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def convert_ref_to_dict(data: str) -> dict:
|
|
68
|
+
""" Ensure geom reference is in the expected format
|
|
69
|
+
Then convert it into a geometry block
|
|
70
|
+
|
|
71
|
+
Parameters:
|
|
72
|
+
data: str, a feature reference
|
|
73
|
+
Returns:
|
|
74
|
+
GeoJSON geometry reference
|
|
75
|
+
"""
|
|
76
|
+
if validate_ref(data):
|
|
77
|
+
geom = {
|
|
78
|
+
"type": "ref",
|
|
79
|
+
"content": data,
|
|
80
|
+
}
|
|
81
|
+
return geom
|
|
82
|
+
return dict()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def as_ref(data) -> dict:
|
|
86
|
+
if isinstance(data, str):
|
|
87
|
+
data = convert_ref_to_dict(data)
|
|
88
|
+
if isinstance(data, dict):
|
|
89
|
+
geom_type = data['type']
|
|
90
|
+
if geom_type.lower() != 'ref':
|
|
91
|
+
raise FeatureError(
|
|
92
|
+
f'Invalid geometry reference: {geom_type} is not a reference (the type should be "ref").'
|
|
93
|
+
)
|
|
94
|
+
if "content" not in data:
|
|
95
|
+
raise FeatureError(
|
|
96
|
+
'Invalid geometry reference: Missing content block that contains the reference.'
|
|
97
|
+
)
|
|
98
|
+
return data
|
|
48
99
|
|
|
49
100
|
|
|
50
101
|
def as_polygon(data: dict) -> dict:
|
|
51
|
-
geom =
|
|
102
|
+
geom = as_geom_or_ref(data)
|
|
52
103
|
geom_type = geom['type']
|
|
53
104
|
if geom_type.lower() != 'polygon':
|
|
54
105
|
raise GeoJSONError(
|
|
@@ -75,7 +126,7 @@ def geom_from_geojson(data: dict) -> dict:
|
|
|
75
126
|
else:
|
|
76
127
|
try:
|
|
77
128
|
# feature
|
|
78
|
-
ret =
|
|
129
|
+
ret = as_geom_or_ref(data['geometry'])
|
|
79
130
|
except KeyError:
|
|
80
131
|
try:
|
|
81
132
|
# FeatureCollection
|
|
@@ -88,11 +139,11 @@ def geom_from_geojson(data: dict) -> dict:
|
|
|
88
139
|
'FeatureCollection has multiple features. Only one feature'
|
|
89
140
|
' can be used to get geometry.')
|
|
90
141
|
|
|
91
|
-
ret =
|
|
142
|
+
ret = as_geom_or_ref(features[0])
|
|
92
143
|
return ret
|
|
93
144
|
|
|
94
145
|
|
|
95
|
-
def
|
|
146
|
+
def validate_geom_as_geojson(data: dict):
|
|
96
147
|
"""Validate GeoJSON geometry.
|
|
97
148
|
|
|
98
149
|
Parameters:
|
|
@@ -101,23 +152,26 @@ def validate_geom(data: dict):
|
|
|
101
152
|
Raises:
|
|
102
153
|
planet.exceptions.GeoJSONError: If data is not a valid GeoJSON
|
|
103
154
|
geometry.
|
|
155
|
+
Returns:
|
|
156
|
+
GeoJSON
|
|
104
157
|
"""
|
|
158
|
+
data = geom_from_geojson(data)
|
|
105
159
|
if 'type' not in data:
|
|
106
|
-
raise GeoJSONError(
|
|
160
|
+
raise GeoJSONError('Missing "type" key.')
|
|
107
161
|
if 'coordinates' not in data:
|
|
108
|
-
raise GeoJSONError(
|
|
162
|
+
raise GeoJSONError('Missing "coordinates" key.')
|
|
109
163
|
|
|
110
164
|
try:
|
|
111
|
-
cls = getattr(gj, data[
|
|
112
|
-
obj = cls(data[
|
|
165
|
+
cls = getattr(gj, data['type'])
|
|
166
|
+
obj = cls(data['coordinates'])
|
|
113
167
|
if not obj.is_valid:
|
|
114
168
|
raise GeoJSONError(obj.errors())
|
|
115
169
|
except AttributeError as err:
|
|
116
|
-
raise GeoJSONError(
|
|
170
|
+
raise GeoJSONError('Not a GeoJSON geometry type') from err
|
|
117
171
|
except ValueError as err:
|
|
118
|
-
raise GeoJSONError(
|
|
172
|
+
raise GeoJSONError('Not a GeoJSON coordinate value') from err
|
|
119
173
|
|
|
120
|
-
return
|
|
174
|
+
return data
|
|
121
175
|
|
|
122
176
|
|
|
123
177
|
def as_featurecollection(features: typing.List[dict]) -> dict:
|
|
@@ -356,9 +356,9 @@ def clip_tool(aoi: dict) -> dict:
|
|
|
356
356
|
planet.exceptions.ClientError: If GeoJSON is not a valid polygon or
|
|
357
357
|
multipolygon.
|
|
358
358
|
"""
|
|
359
|
-
valid_types = ['Polygon', 'MultiPolygon']
|
|
359
|
+
valid_types = ['Polygon', 'MultiPolygon', 'ref']
|
|
360
360
|
|
|
361
|
-
geom = geojson.
|
|
361
|
+
geom = geojson.as_geom_or_ref(aoi)
|
|
362
362
|
if geom['type'].lower() not in [v.lower() for v in valid_types]:
|
|
363
363
|
raise ClientError(
|
|
364
364
|
f'Invalid geometry type: {geom["type"]} is not in {valid_types}.')
|
|
@@ -167,7 +167,7 @@ def build_request(name: str,
|
|
|
167
167
|
def catalog_source(
|
|
168
168
|
item_types: List[str],
|
|
169
169
|
asset_types: List[str],
|
|
170
|
-
geometry:
|
|
170
|
+
geometry: dict,
|
|
171
171
|
start_time: datetime,
|
|
172
172
|
filter: Optional[Mapping] = None,
|
|
173
173
|
end_time: Optional[datetime] = None,
|
|
@@ -250,7 +250,7 @@ def catalog_source(
|
|
|
250
250
|
parameters = {
|
|
251
251
|
"item_types": item_types,
|
|
252
252
|
"asset_types": asset_types,
|
|
253
|
-
"geometry": geojson.
|
|
253
|
+
"geometry": geojson.as_geom_or_ref(geometry),
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
try:
|
|
@@ -280,14 +280,15 @@ def catalog_source(
|
|
|
280
280
|
|
|
281
281
|
|
|
282
282
|
def planetary_variable_source(
|
|
283
|
-
var_type: Literal["
|
|
283
|
+
var_type: Literal["analysis_ready_ps",
|
|
284
|
+
"biomass_proxy",
|
|
284
285
|
"land_surface_temperature",
|
|
285
286
|
"soil_water_content",
|
|
286
287
|
"vegetation_optical_depth",
|
|
287
288
|
"forest_carbon_diligence_30m",
|
|
288
289
|
"field_boundaries_sentinel_2_p1m"],
|
|
289
290
|
var_id: str,
|
|
290
|
-
geometry:
|
|
291
|
+
geometry: dict,
|
|
291
292
|
start_time: datetime,
|
|
292
293
|
end_time: Optional[datetime] = None,
|
|
293
294
|
) -> dict:
|
|
@@ -304,9 +305,10 @@ def planetary_variable_source(
|
|
|
304
305
|
Note: this function does not validate variable types and ids.
|
|
305
306
|
|
|
306
307
|
Parameters:
|
|
307
|
-
var_type: one of "
|
|
308
|
-
"
|
|
309
|
-
"
|
|
308
|
+
var_type: one of "analysis_ready_ps", "biomass_proxy",
|
|
309
|
+
"land_surface_temperature", "soil_water_content",
|
|
310
|
+
"vegetation_optical_depth", "forest_carbon_diligence_30m,
|
|
311
|
+
or field_boundaries_sentinel_2_p1m".
|
|
310
312
|
var_id: a value such as "SWC-AMSR2-C_V1.0_100" for soil water
|
|
311
313
|
content derived from AMSR2 C band.
|
|
312
314
|
geometry: The area of interest of the subscription that will be
|
|
@@ -355,7 +357,7 @@ def planetary_variable_source(
|
|
|
355
357
|
|
|
356
358
|
parameters = {
|
|
357
359
|
"id": var_id,
|
|
358
|
-
"geometry": geojson.
|
|
360
|
+
"geometry": geojson.as_geom_or_ref(geometry),
|
|
359
361
|
}
|
|
360
362
|
|
|
361
363
|
try:
|
|
@@ -596,9 +598,9 @@ def clip_tool(aoi: Mapping) -> dict:
|
|
|
596
598
|
planet.exceptions.ClientError: If aoi is not a valid polygon or
|
|
597
599
|
multipolygon.
|
|
598
600
|
"""
|
|
599
|
-
valid_types = ['Polygon', 'MultiPolygon']
|
|
601
|
+
valid_types = ['Polygon', 'MultiPolygon', 'ref']
|
|
600
602
|
|
|
601
|
-
geom = geojson.
|
|
603
|
+
geom = geojson.as_geom_or_ref(dict(aoi))
|
|
602
604
|
if geom['type'].lower() not in [v.lower() for v in valid_types]:
|
|
603
605
|
raise ClientError(
|
|
604
606
|
f'Invalid geometry type: {geom["type"]} is not in {valid_types}.')
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '2.6dev'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|