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

Fix for stale references in DIST-S1 ISO XML #604

Merged
merged 3 commits into from
Mar 6, 2025
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
14 changes: 14 additions & 0 deletions .ci/scripts/cslc_s1/test_int_cslc_s1.sh
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ metrics_collection_end "$PGE_NAME" "$container_name" "$docker_exit_status" "$TES
if [ $docker_exit_status -ne 0 ]; then
echo "docker exit indicates failure: ${docker_exit_status}"
overall_status=1
else
# Retrieve the return code written to disk by the comparison script
test_status=$(cat "$output_dir/compare_cslc_s1_products.rc")

if [ $test_status -ne 0 ]; then
overall_status=$test_status
fi
fi

# Run the static layer workflow
Expand Down Expand Up @@ -140,6 +147,13 @@ metrics_collection_end "${PGE_NAME}_static" "$container_name" "$docker_exit_stat
if [ $docker_exit_status -ne 0 ]; then
echo "docker exit indicates failure: ${docker_exit_status}"
overall_status=1
else
# Retrieve the return code written to disk by the comparison script
test_status=$(cat "$output_dir/compare_cslc_s1_products.rc")

if [ $test_status -ne 0 ]; then
overall_status=$test_status
fi
fi

# Copy the PGE/SAS log file(s) to the test results directory so it can be archived
Expand Down
7 changes: 7 additions & 0 deletions .ci/scripts/dswx_ni/test_int_dswx_ni.sh
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ cp "${output_dir}"/test_int_dswx_ni_results.html "${TEST_RESULTS_DIR}"/test_int_
if [ $docker_exit_status -ne 0 ]; then
echo "docker exit indicates failure: ${docker_exit_status}"
overall_status=1
else
# Retrieve the return code written to disk by the comparison script
test_status=$(cat "$output_dir/compare_dswx_ni_products.rc")

if [ $test_status -ne 0 ]; then
overall_status=$test_status
fi
fi

echo " "
Expand Down
7 changes: 7 additions & 0 deletions .ci/scripts/dswx_s1/test_int_dswx_s1.sh
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ cp "${output_dir}"/test_int_dswx_s1_results.html "${TEST_RESULTS_DIR}"/test_int_
if [ $docker_exit_status -ne 0 ]; then
echo "docker exit indicates failure: ${docker_exit_status}"
overall_status=1
else
# Retrieve the return code written to disk by the comparison script
test_status=$(cat "$output_dir/compare_dswx_s1_products.rc")

if [ $test_status -ne 0 ]; then
overall_status=$test_status
fi
fi

echo " "
Expand Down
44 changes: 30 additions & 14 deletions src/opera/pge/dist_s1/dist_s1_pge.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import os
import re
from datetime import datetime
from os.path import join, isdir, isfile, abspath, basename

from opera.pge.base.base_pge import PreProcessorMixin, PgeExecutor, PostProcessorMixin
Expand All @@ -19,7 +20,7 @@
from opera.util.input_validation import check_input_list
from opera.util.render_jinja2 import augment_measured_parameters, render_jinja2
from opera.util.tiff_utils import get_geotiff_metadata
from opera.util.time import get_time_for_filename
from opera.util.time import get_time_for_filename, get_iso_time


class DistS1PreProcessorMixin(PreProcessorMixin):
Expand Down Expand Up @@ -85,33 +86,31 @@ class DistS1PostProcessorMixin(PostProcessorMixin):

_valid_layer_names = _main_output_layer_names + _confirmation_db_output_layer_names

def _validate_outputs(self):
product_id_pattern = (r'(?P<id>(?P<project>OPERA)_(?P<level>L3)_(?P<product_type>DIST(-ALERT)?)-(?P<source>S1)_'
r'(?P<tile_id>T[^\W_]{5})_(?P<acquisition_ts>(?P<acq_year>\d{4})(?P<acq_month>\d{2})'
r'(?P<acq_day>\d{2})T(?P<acq_hour>\d{2})(?P<acq_minute>\d{2})(?P<acq_second>\d{2})Z)_'
r'(?P<creation_ts>(?P<cre_year>\d{4})(?P<cre_month>\d{2})(?P<cre_day>\d{2})T'
r'(?P<cre_hour>\d{2})(?P<cre_minute>\d{2})(?P<cre_second>\d{2})Z)_(?P<sensor>S1[AC]?)_'
r'(?P<spacing>30)_(?P<product_version>v\d+[.]\d+))')
_product_id_pattern = (r'(?P<id>(?P<project>OPERA)_(?P<level>L3)_(?P<product_type>DIST(-ALERT)?)-(?P<source>S1)_'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized that we can simplify this regex a bit, since I don't think we'll have a need for granular access to the parsed timestamps, meaning we could remove the ?<acq_hour/minute/second> and ?P<cre_hour/minute/second> and just keep the full capture for ?p<acquisition_ts> and ?p<creation_ts>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trimmed it down quite nicely, thanks

r'(?P<tile_id>T[^\W_]{5})_(?P<acquisition_ts>\d{4}\d{2}\d{2}T\d{2}\d{2}\d{2}Z)_'
r'(?P<creation_ts>\d{4}\d{2}\d{2}T\d{2}\d{2}\d{2}Z)_(?P<sensor>S1[AC]?)_(?P<spacing>30)_'
r'(?P<product_version>v\d+[.]\d+))')

granule_filename_pattern = (product_id_pattern + rf'((_(?P<layer_name>{"|".join(self._valid_layer_names)}))|'
r'_BROWSE)?[.](?P<ext>tif|tiff|png)$')
_granule_filename_pattern = (_product_id_pattern + rf'((_(?P<layer_name>{"|".join(_valid_layer_names)}))|'
r'_BROWSE)?[.](?P<ext>tif|tiff|png)$')

product_id = re.compile(product_id_pattern + r'$')
granule_filename = re.compile(granule_filename_pattern)
_product_id_re = re.compile(_product_id_pattern + r'$')
_granule_filename_re = re.compile(_granule_filename_pattern)

def _validate_outputs(self):
output_product_path = abspath(self.runconfig.output_product_path)
output_products = []

for file in os.listdir(output_product_path):
dir_path = join(output_product_path, file)
if isdir(dir_path) and product_id.match(file):
if isdir(dir_path) and self._product_id_re.match(file):
bands = []
generated_band_names = []

for granule in os.listdir(dir_path):
granule_path = join(dir_path, granule)
if isfile(granule_path):
match_result = granule_filename.match(granule)
match_result = self._granule_filename_re.match(granule)

if match_result is None: # or match_result.groupdict()['ext'] != 'tif':
error_msg = f'Invalid product filename {granule}'
Expand Down Expand Up @@ -345,6 +344,23 @@ def _collect_dist_s1_product_metadata(self, geotiff_product):
'spacing': 30 # meters/pixel
}

# TODO: Replace these with metadata values sourced from the granule when available
# (Or remove entirely if possible to refer to them straight through the MP dict)

match_result = self._granule_filename_re.match(basename(geotiff_product))

if match_result is None:
# This really should not happen due to passing the _validate_outputs function but check anyway
msg = f'Failed to parse DIST-S1 filename {basename(geotiff_product)}'
self.logger.critical(self.name, ErrorCode.INVALID_OUTPUT, msg)

match_result = match_result.groupdict()
acq_time = match_result['acquisition_ts']
acq_time = datetime.strptime(acq_time, '%Y%m%dT%H%M%SZ')

output_product_metadata['acquisition_start_time'] = get_iso_time(acq_time)
output_product_metadata['acquisition_end_time'] = get_iso_time(acq_time)

return output_product_metadata

def _create_custom_metadata(self, tile_filename):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,8 @@
<gmd:EX_TemporalExtent id="boundingTemporalExtent">
<gmd:extent>
<gml:TimePeriod gml:id="DIST_Time_Range">
<gml:beginPosition>{{ product_output.identification.secondary_zero_doppler_start_time }}{# ISO_OPERA_refDateTime #}</gml:beginPosition>
<gml:endPosition>{{ product_output.identification.secondary_zero_doppler_end_time }}{# ISO_OPERA_secDateTime #}</gml:endPosition>
<gml:beginPosition>{{ product_output.acquisition_start_time }}{# ISO_OPERA_refDateTime #}</gml:beginPosition>
<gml:endPosition>{{ product_output.acquisition_end_time }}{# ISO_OPERA_secDateTime #}</gml:endPosition>
</gml:TimePeriod>
</gmd:extent>
</gmd:EX_TemporalExtent>
Expand Down