Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Composite checkout function for orders #134

Merged
merged 6 commits into from
Jul 19, 2023
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
133 changes: 122 additions & 11 deletions planet_explorer/gui/pe_orders.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
iface,
ENABLE_CLIP_SETTING,
ENABLE_HARMONIZATION_SETTING,
ENABLE_COMPOSITE,
ENABLE_STAC_METADATA,
SETTINGS_NAMESPACE,
)
Expand Down Expand Up @@ -134,6 +135,7 @@ def __init__(self, bundleid, bundle, item_type):
)
self.can_harmonize = bundle.get("canHarmonize", False)
self.can_clip = bundle.get("canClip", False)

self.rectified = bundle["rectification"] == "orthorectified"
bands = []
asset_def = PlanetClient.getInstance().asset_types_for_item_type_as_dict(
Expand Down Expand Up @@ -460,6 +462,9 @@ def __init__(self, item_type, bundle_type, images, add_clip, add_harmonize):
self.bundle_type = bundle_type
self.images = images
self.add_clip = add_clip

self.add_composite = True

self.add_harmonize = add_harmonize
self.stac_order = QSettings().value(
f"{SETTINGS_NAMESPACE}/{ENABLE_STAC_METADATA}", False, type=bool
Expand Down Expand Up @@ -508,6 +513,7 @@ def populate_details(self):
layout.setColumnStretch(0, 1)
layout.setColumnStretch(2, 1)
self.chkClip = None
self.chkComposite = None
self.chkHarmonize = None
if self.add_clip:
layout.addWidget(QLabel("<b>Clipping</b>"), 0, 1, Qt.AlignCenter)
Expand All @@ -521,14 +527,63 @@ def populate_details(self):
self.chkClip.setChecked(str(enabled).lower() == str(True).lower())
self.chkClip.stateChanged.connect(self.checkStateChanged)
layout.addWidget(self.chkClip, 2, 1, Qt.AlignCenter)

if self.add_composite:
layout.addWidget(QLabel("<b>Composite Items</b>"), 3, 1, Qt.AlignCenter)
description_label = QLabel(
"The "
"<a style='color: #50a94e; text-decoration: none;' "
"href='https://developers.planet.com/apis/orders/tools/#composite'>"
"Composite tool</a>"
" allows your to composite a "
"set of images into a single output"
)
layout.addWidget(
description_label,
4,
1,
Qt.AlignCenter,
)
description_label.setOpenExternalLinks(True)

self.chkComposite = QCheckBox("Enable composite")
enabled = QSettings().value(
f"{SETTINGS_NAMESPACE}/{ENABLE_COMPOSITE}", False
)
self.chkComposite.setChecked(str(enabled).lower() == str(True).lower())
self.chkComposite.stateChanged.connect(self.compositeStateChanged)
layout.addWidget(self.chkComposite, 5, 1, Qt.AlignCenter)

self.radio_btn_all = QRadioButton("All items")
self.radio_btn_strip = QRadioButton("By strip")
self.radio_btn_all.setChecked(True)

# Show/hide composite radio buttons
if self.chkComposite.isChecked():
self.radio_btn_all.setEnabled(True)
self.radio_btn_strip.setEnabled(True)

self.radio_btn_all.setVisible(True)
self.radio_btn_strip.setVisible(True)

else:
self.radio_btn_all.setEnabled(False)
self.radio_btn_strip.setEnabled(False)

self.radio_btn_all.setVisible(False)
self.radio_btn_strip.setVisible(False)

layout.addWidget(self.radio_btn_all, 6, 1, Qt.AlignCenter)
layout.addWidget(self.radio_btn_strip, 7, 1, Qt.AlignCenter)

if self.add_harmonize:
layout.addWidget(QLabel("<b>Harmonization</b>"), 3, 1, Qt.AlignCenter)
layout.addWidget(QLabel("<b>Harmonization</b>"), 8, 1, Qt.AlignCenter)
layout.addWidget(
QLabel(
"Radiometrically harmonize imagery captured by one satellite "
"instrument type to imagery capture by another"
),
4,
9,
1,
Qt.AlignCenter,
)
Expand All @@ -538,19 +593,19 @@ def populate_details(self):
)
self.chkHarmonize.setChecked(str(enabled).lower() == str(True).lower())
self.chkHarmonize.stateChanged.connect(self.checkStateChanged)
layout.addWidget(self.chkHarmonize, 5, 1, Qt.AlignCenter)
layout.addWidget(self.chkHarmonize, 10, 1, Qt.AlignCenter)

metadata_widget = PlanetOrderReviewMetadataWidget(self.stac_order)
metadata_widget.stac_metadata_box_clicked.connect(self._stac_box_clicked)

layout.addWidget(metadata_widget, 6, 1, Qt.AlignCenter)
layout.addWidget(metadata_widget.description_label, 7, 1, Qt.AlignCenter)
layout.addWidget(metadata_widget.stac_box, 8, 1, Qt.AlignCenter)
layout.addWidget(metadata_widget, 11, 1, Qt.AlignCenter)
layout.addWidget(metadata_widget.description_label, 12, 1, Qt.AlignCenter)
layout.addWidget(metadata_widget.stac_box, 13, 1, Qt.AlignCenter)

layout.addWidget(QLabel("<b>Review Items</b>"), 9, 1, Qt.AlignCenter)
layout.addWidget(QLabel("<b>Review Items</b>"), 14, 1, Qt.AlignCenter)
layout.addWidget(
QLabel("We recommend deselecting items that appear to have no pixels"),
10,
15,
1,
Qt.AlignCenter,
)
Expand All @@ -564,13 +619,28 @@ def populate_details(self):
col = i % 4 + 1
sublayout.addWidget(w, row, col)
self.imgWidgets.append(w)
layout.addLayout(sublayout, 11, 1, Qt.AlignCenter)
layout.addLayout(sublayout, 16, 1, Qt.AlignCenter)

self.widgetDetails.setLayout(layout)

def checkStateChanged(self):
self.selectedImagesChanged.emit()

def compositeStateChanged(self):
# Show/hide composite radio buttons
if self.chkComposite.isChecked():
self.radio_btn_all.setEnabled(True)
self.radio_btn_strip.setEnabled(True)

self.radio_btn_all.setVisible(True)
self.radio_btn_strip.setVisible(True)
else:
self.radio_btn_all.setEnabled(False)
self.radio_btn_strip.setEnabled(False)

self.radio_btn_all.setVisible(False)
self.radio_btn_strip.setVisible(False)

def selected_images(self):
return [w.image for w in self.imgWidgets if w.selected()]

Expand All @@ -586,6 +656,20 @@ def harmonize(self):
else:
return self.chkHarmonize.isChecked()

def composite(self):
if self.chkComposite is None:
return False
else:
return self.chkComposite.isChecked()

def getCompositeType(self):
if self.radio_btn_all.isChecked():
# Will composite all images
return "order"
else:
# Will only composite each strip
return "strip_id"

def _btnDetailsClicked(self):
if self.widgetDetails.isVisible():
self.widgetDetails.hide()
Expand Down Expand Up @@ -871,6 +955,7 @@ def _process_orders(self):
aoi = None
if self.tool_resources.get("aoi") is not None:
aoi = json.loads(self.tool_resources.get("aoi"))

orders = []
for item_type, widget in self._item_type_widgets.items():
for bundle in widget.bundles():
Expand Down Expand Up @@ -901,6 +986,10 @@ def _process_orders(self):
tools = []
if w.clipping():
tools.append({"clip": {"aoi": aoi}})
if w.composite():
# 'order' or 'strip_id' for 'group_by'
composite_type = w.getCompositeType()
tools.append({"composite": {"group_by": composite_type}})
if w.harmonize():
tools.append({"harmonize": {"target_sensor": "Sentinel-2"}})
if bundle["filetype"] == "NITF":
Expand All @@ -911,8 +1000,30 @@ def _process_orders(self):
responses_ok = True
for order in orders:
resp = self._p_client.create_order(order)
responses_ok = responses_ok and resp
send_analytics_for_order(order)
resp_json = resp.json()

# If the order request failed
if resp.status_code >= 400:
order_name = order["name"]

if resp_json and resp_json["general"][0]["message"]:
# Error message received from response
err_message = resp_json["general"][0]["message"]
else:
# If no error message has been received
err_message = "An error occurred for the order."

self.bar.pushMessage(
order_name,
err_message,
Qgis.Warning,
)

responses_ok = False
else:
# Order were a success
responses_ok = responses_ok and resp_json
send_analytics_for_order(order)

if responses_ok:
self.bar.pushMessage(
Expand Down
20 changes: 15 additions & 5 deletions planet_explorer/gui/pe_orders_monitor_dockwidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,15 +428,25 @@ def add_to_map(self):
list_files = manifest_data["files"]
for json_file in list_files:
media_type = json_file["media_type"]

raster_types = ["image/tiff", "application/vnd.lotus-notes"]

if media_type in raster_types:
annotations = json_file["annotations"]
asset_type = annotations["planet/asset_type"]
if asset_type.endswith("_udm") or asset_type.endswith("_udm2"):
# Skips all 'udm' asset rasters
continue
asset_type_key = "planet/asset_type"

if asset_type_key in annotations:
asset_type = annotations[asset_type_key]
if asset_type.endswith("_udm") or asset_type.endswith("_udm2"):
# Skips all 'udm' asset rasters
continue
else:
# A workaround for composite
image_path = json_file["path"]
if not image_path.endswith(
"composite.tif"
) and not image_path.endswith("composite_file_format.ntf"):
# Skips if it's not a composite file
continue

image_path = json_file["path"]
image_dir = "{}/{}".format(final_path, image_path)
Expand Down
2 changes: 2 additions & 0 deletions planet_explorer/pe_analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ def analytics_track(event, properties=None):


item_type_names = {
"PSScene": "planetscope_scene",
"PSScene4Band": "planetscope_scene",
"PSScene3Band": "planetscope_scene",
"PSOrthoTile": "planetscope_ortho",
Expand Down Expand Up @@ -150,6 +151,7 @@ def send_analytics_for_preview(imgs):
def send_analytics_for_order(order):
product = order["products"][0]
name = item_type_names.get(product["item_type"])

if name is not None:
analytics_track(
SCENE_ORDER_PLACED,
Expand Down
1 change: 1 addition & 0 deletions planet_explorer/pe_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
DEFAULT_ORDERS_FOLDERNAME = "planet_orders"
ENABLE_CLIP_SETTING = "enableClip"
ENABLE_STAC_METADATA = "enableStacMetadata"
ENABLE_COMPOSITE = "enableComposite"
ENABLE_HARMONIZATION_SETTING = "enableHarmonization"

BASE_URL = "https://www.planet.com"
Expand Down
3 changes: 2 additions & 1 deletion planet_explorer/planet_api/p_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,8 @@ def create_order(self, request):
headers = {"X-Planet-App": "qgis"}
session = PlanetClient.getInstance().dispatcher.session
res = session.post(url, auth=(api_key, ""), json=request, headers=headers)
return res.json()

return res

def update_search(self, request, searchid):
body = json.dumps(request)
Expand Down
30 changes: 25 additions & 5 deletions planet_explorer/planet_api/p_order_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,32 @@ def images_from_manifest(self, manifest_file):
images = []
for img in manifest["files"]:
if img["media_type"] == "image/tiff":
images.append(
(
os.path.join(base_folder, img["path"]),
img["annotations"]["planet/item_type"],
annotations = img["annotations"]
asset_type_key = "planet/asset_type"

if asset_type_key in annotations:
images.append(
(
os.path.join(base_folder, img["path"]),
img["annotations"]["planet/item_type"],
)
)
)
else:
# A workaround for composite
image_path = img["path"]
if not image_path.endswith(
"composite.tif"
) and not image_path.endswith("composite_file_format.ntf"):
# Skips if it's not a composite file
continue
else:
# Adds the composite file
images.append(
(
os.path.join(base_folder, img["path"]),
"composite", # Item type
)
)
return images

def finished(self, result):
Expand Down