Skip to content

Commit 43ee719

Browse files
committed
create order_details tools by name, e.g. harmonize_tool()
1 parent a900ba1 commit 43ee719

File tree

2 files changed

+179
-33
lines changed

2 files changed

+179
-33
lines changed

planet/api/order_details.py

Lines changed: 124 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,6 @@
2121
LOGGER = logging.getLogger(__name__)
2222

2323

24-
class OrderDetailsException(Exception):
25-
"""Exceptions thrown by OrderDetails"""
26-
pass
27-
28-
2924
def build_request(
3025
name: str,
3126
products: List[dict],
@@ -38,13 +33,24 @@ def build_request(
3833
'''Prepare an order request.
3934
4035
```python
41-
>>> from planet.api.order_details import build_request, product
42-
>>>
43-
>>> image_ids = ['3949357_1454705_2020-12-01_241c']
36+
>>> from planet.api.order_details import (
37+
... build_request, product, toar_tool, reproject_tool, tile_tool)
38+
...
39+
>>> products = [
40+
... product(['20170614_113217_3163208_RapidEye-5'],
41+
... 'analytic', 'REOrthoTile')])
42+
... ]
43+
...
44+
>>> tools = [
45+
... toar_tool(scale_factor=10000),
46+
... reproject_tool(projection='WSG84', kernel='cubic'),
47+
... tile_tool(1232, origin_x=-180, origin_y=-90,
48+
... pixel_size=0.000027056277056,
49+
... name_template='C1232_30_30_{tilex:04d}_{tiley:04d}')
50+
... ]
51+
...
4452
>>> order_request = build_request(
45-
... 'test_order',
46-
... [product(image_ids, 'analytic', 'psorthotile')]
47-
... )
53+
... 'test_order', products, tools)
4854
...
4955
5056
```
@@ -118,9 +124,9 @@ def product(
118124

119125

120126
def notifications(
121-
email: bool = False,
127+
email: bool = None,
122128
webhook_url: str = None,
123-
webhook_per_order: bool = False
129+
webhook_per_order: bool = None
124130
) -> dict:
125131
'''Notifications description for an order detail.
126132
@@ -130,18 +136,7 @@ def notifications(
130136
webhook_per_order: Request a single webhook call per order instead
131137
of one call per each delivered item.
132138
'''
133-
details = {}
134-
135-
if email:
136-
details['email'] = email
137-
138-
if webhook_url is not None:
139-
details['webhook_url'] = webhook_url
140-
141-
if webhook_per_order:
142-
details['webhook_per_order'] = True
143-
144-
return details
139+
return dict((k, v) for k, v in locals().items() if v)
145140

146141

147142
def delivery(
@@ -297,7 +292,7 @@ def google_earth_engine(
297292
return {'google_earth_engine': cloud_details}
298293

299294

300-
def tool(name: str, parameters: dict) -> dict:
295+
def _tool(name: str, parameters: dict) -> dict:
301296
'''Create the API spec representation of a tool.
302297
303298
See [Tools and Toolchains](
@@ -337,4 +332,106 @@ def clip_tool(aoi: dict) -> dict:
337332
geojson.GeoJSONException: If GeoJSON is not a valid polygon.
338333
'''
339334
parameters = {'aoi': geojson.as_polygon(aoi)}
340-
return tool('clip', parameters)
335+
return _tool('clip', parameters)
336+
337+
338+
def composite_tool() -> dict:
339+
'''Create the API spec representation of a composite tool.
340+
'''
341+
return _tool('composite', {})
342+
343+
344+
def coregister_tool(anchor_item: str) -> dict:
345+
'''Create the API spec representation of a coregister tool.
346+
347+
Parameters:
348+
anchor_item: The item_id of the item to which all other items should be
349+
coregistered.
350+
'''
351+
return _tool('coregister', {'anchor_item': anchor_item})
352+
353+
354+
def file_format_tool(file_format: str) -> dict:
355+
'''Create the API spec representation of a file format tool.
356+
357+
Parameters:
358+
file_format: The format of the tool output. Either 'COG' or 'PL_NITF'.
359+
'''
360+
return _tool('file_format', {'format': file_format})
361+
362+
363+
def reproject_tool(
364+
projection: str,
365+
resolution: float = None,
366+
kernel: str = None
367+
) -> dict:
368+
'''Create the API spec representation of a reproject tool.
369+
370+
Parameters:
371+
projection: A coordinate system in the form EPSG:n. (ex. EPSG:4326 for
372+
WGS84, EPSG:32611 for UTM 11 North (WGS84), or EPSG:3857 for Web
373+
Mercator).
374+
resolution: The pixel width and height in the output file. The API
375+
default is the resolution of the input item. This value will be in
376+
meters unless the coordinate system is geographic (like EPSG:4326),
377+
then it will be a pixel size in decimal degrees.
378+
kernel: The resampling kernel used. The API default is "near". This
379+
parameter also supports "bilinear", "cubic", "cubicspline",
380+
"lanczos", "average" and "mode".
381+
'''
382+
parameters = dict((k, v) for k, v in locals().items() if v)
383+
return _tool('reproject', parameters)
384+
385+
386+
def tile_tool(
387+
tile_size: int,
388+
origin_x: float = None,
389+
origin_y: float = None,
390+
pixel_size: float = None,
391+
name_template: str = None,
392+
conformal_x_scaling: bool = None
393+
) -> dict:
394+
'''Create the API spec representation of a reproject tool.
395+
396+
Parameters:
397+
tile_size: Height and width of output tiles in pixels and lines
398+
(always square).
399+
origin_x: Tiling system x origin in projected coordinates. The API
400+
default is zero.
401+
origin_y: Tiling system y origin in projected coordinates. The API
402+
default is zero.
403+
pixel_size: Tiling system pixel size in projected coordinates. The API
404+
default is the pixel_size of input raster.
405+
name_template: A naming template for creating output tile filenames.
406+
The API default is "{tilex}_{tiley}.tif" resulting in filenames
407+
like 128_200.tif. The {tilex} and {tiley} parameters can be of the
408+
form {tilex:06d} to produce a fixed width field with leading zeros.
409+
'''
410+
parameters = dict((k, v) for k, v in locals().items() if v)
411+
return _tool('tile', parameters)
412+
413+
414+
def toar_tool(
415+
scale_factor: int = None,
416+
) -> dict:
417+
'''Create the API spec representation of a TOAR tool.
418+
419+
Parameters:
420+
scale_factor: Scale factor applied to convert 0.0 to 1.0 reflectance
421+
floating point values to a value that fits in 16bit integer pixels.
422+
The API default is 10000. Values over 65535 could result in high
423+
reflectances not fitting in 16bit integers.
424+
'''
425+
parameters = {}
426+
if scale_factor:
427+
parameters['scale_factor'] = scale_factor
428+
return _tool('toar', parameters)
429+
430+
431+
def harmonize_tool() -> dict:
432+
'''Create the API spec representation of a harmonize tool.
433+
434+
Currently, only "PS2" (Dove Classic) is supported as a target sensor, and
435+
it will transform only items captured by “PS2.SD” (Dove-R).
436+
'''
437+
return _tool('harmonize', {'target_sensor': 'PS2'})

tests/unit/test_order_details.py

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ def test_notifications():
130130
}
131131
assert notifications_config == expected
132132

133+
empty_notifications_config = order_details.notifications(
134+
)
135+
empty_expected = {}
136+
assert empty_notifications_config == empty_expected
137+
133138

134139
def test_delivery():
135140
as3_config = {
@@ -222,23 +227,67 @@ def test_google_earth_engine():
222227
assert gee_config == expected
223228

224229

225-
def test_tool():
226-
test_tool = order_details.tool('band_math', 'jsonstring')
230+
def test__tool():
231+
test_tool = order_details._tool('band_math', 'jsonstring')
227232
assert test_tool == {'band_math': 'jsonstring'}
228233

229234
with pytest.raises(specs.SpecificationException):
230-
_ = order_details.tool('notsupported', 'jsonstring')
235+
_ = order_details._tool('notsupported', 'jsonstring')
231236

232237

233-
def test_clip_tool_success(geom_geojson):
238+
def test_clip_tool(geom_geojson, point_geom_geojson):
234239
ct = order_details.clip_tool(geom_geojson)
235240
expected = {
236241
'clip': {
237242
'aoi': geom_geojson
238243
}}
239244
assert ct == expected
240245

241-
242-
def test_clip_tool_wrong_type(point_geom_geojson):
243246
with pytest.raises(geojson.WrongTypeException):
244247
_ = order_details.clip_tool(point_geom_geojson)
248+
249+
250+
def test_reproject_tool():
251+
rt = order_details.reproject_tool(
252+
projection='proj',
253+
resolution=5
254+
)
255+
expected = {
256+
'reproject': {
257+
'projection': 'proj',
258+
'resolution': 5
259+
}
260+
}
261+
assert rt == expected
262+
263+
264+
def test_tile_tool():
265+
tt = order_details.tile_tool(
266+
30,
267+
pixel_size=3
268+
)
269+
expected = {
270+
'tile': {
271+
'tile_size': 30,
272+
'pixel_size': 3
273+
}
274+
}
275+
assert tt == expected
276+
277+
278+
def test_toar_tool():
279+
tt = order_details.toar_tool(
280+
scale_factor=5
281+
)
282+
expected = {
283+
'toar': {
284+
'scale_factor': 5
285+
}
286+
}
287+
assert tt == expected
288+
289+
tt_empty = order_details.toar_tool()
290+
expected_empty = {
291+
'toar': {}
292+
}
293+
assert tt_empty == expected_empty

0 commit comments

Comments
 (0)