Source code for aws.osml.gdal.sidd_sensor_model_builder

#  Copyright 2023-2024 Amazon.com, Inc. or its affiliates.

import logging
from typing import Optional, Union

from xsdata.formats.dataclass.parsers import XmlParser

import aws.osml.formats.sidd.models.sidd_v1_0_0 as sidd100
import aws.osml.formats.sidd.models.sidd_v2_0_0 as sidd200
import aws.osml.formats.sidd.models.sidd_v3_0_0 as sidd300

from ..photogrammetry import (
    ChippedImageSensorModel,
    ImageCoordinate,
    PlaneProjectionSet,
    SARImageCoordConverter,
    SensorModel,
    SICDSensorModel,
    WorldCoordinate,
)
from .sensor_model_builder import SensorModelBuilder
from .sicd_sensor_model_builder import poly2d_to_native, xyzpoly_to_native, xyztype_to_ndarray

logger = logging.getLogger(__name__)


[docs] class SIDDSensorModelBuilder(SensorModelBuilder): """ This builder is used to create sensor models for images that have SIDD metadata. The metadata is provided as XML that conforms to the SIDD specifications. We intend to support multiple SIDD versions but the current software was implemented using the v2.0.0 and v3.0.0 specifications. Note that the SIDD sensor models rely heavily on the SICD projections so the class of the returned model will be a SICDSensorModel. Future versions may rename this to SISensorModel or SARSensorModel. """ def __init__(self, sidd_xml: str): """ Construct the builder given the SIDD XML. :param sidd_xml: the XML string """ super().__init__() self.sidd_xml = sidd_xml
[docs] def build(self) -> Optional[SensorModel]: """ Attempt to build a precise SAR sensor model. This sensor model handles chipped images natively. :return: the sensor model; if available """ try: if self.sidd_xml is None or len(self.sidd_xml) == 0: return None parser = XmlParser() sicd = parser.from_string(self.sidd_xml) return SIDDSensorModelBuilder.from_dataclass(sicd) except Exception as e: logging.error("Exception caught attempting to build SIDD sensor model.", e) return None
[docs] @staticmethod def from_dataclass(sidd: Union[sidd100.SIDD, sidd200.SIDD, sidd300.SIDD]) -> Optional[SensorModel]: """ This method constructs a SIDD sensor model from the python dataclasses generated when parsing the XML. If the metadata shows that this is a chip then a ChippedImageSensorModel will be constructed to wrap the SICDSensorModel used for the full image. :param sidd: the dataclass object constructed from the XML :return: the sensor model; if available """ plane_projection = sidd.measurement.plane_projection scp_ecf = WorldCoordinate(xyztype_to_ndarray(plane_projection.reference_point.ecef)) scp_pixel = ImageCoordinate([plane_projection.reference_point.point.col, plane_projection.reference_point.point.row]) time_coa_poly = poly2d_to_native(plane_projection.time_coapoly) arp_poly = xyzpoly_to_native(sidd.measurement.arppoly) u_row = xyztype_to_ndarray(plane_projection.product_plane.row_unit_vector) u_col = xyztype_to_ndarray(plane_projection.product_plane.col_unit_vector) coord_converter = SARImageCoordConverter( scp_pixel=scp_pixel, scp_ecf=scp_ecf, u_row=u_row, u_col=u_col, row_ss=plane_projection.sample_spacing.row, col_ss=plane_projection.sample_spacing.col, first_pixel=ImageCoordinate([0, 0]), ) projection_set = PlaneProjectionSet( scp_ecf=scp_ecf, image_plane_urow=u_row, image_plane_ucol=u_col, coa_time_poly=time_coa_poly, arp_poly=arp_poly, ) u_gpn = SICDSensorModel.compute_u_gpn(scp_ecf=scp_ecf, u_row=u_row, u_col=u_col) sidd_sensor_model = SICDSensorModel( coord_converter=coord_converter, coa_projection_set=projection_set, u_spn=u_gpn, u_gpn=u_gpn, ) if sidd.downstream_reprocessing is None or sidd.downstream_reprocessing.geometric_chip is None: return sidd_sensor_model else: # Since this SIDD image is a chip of a full image wrap the regular sensor model in a sensor model that # will handle the conversions between the chipped image coordinates and the full image coordinates. # This 4 corner transformation handles images that are cropped, rotated, and scaled, from the full # SIDD image grid. geo_chip = sidd.downstream_reprocessing.geometric_chip chip_num_rows = geo_chip.chip_size.row chip_num_cols = geo_chip.chip_size.col chipped_image_coords = [ ImageCoordinate(coord) for coord in [[0, 0], [chip_num_cols, 0], [chip_num_cols, chip_num_rows], [0, chip_num_rows]] ] full_image_coords = [ ImageCoordinate([x.col, x.row]) for x in [ geo_chip.original_upper_left_coordinate, geo_chip.original_upper_right_coordinate, geo_chip.original_lower_right_coordinate, geo_chip.original_lower_left_coordinate, ] ] return ChippedImageSensorModel( full_image_coords, chipped_image_coords, sidd_sensor_model, )