Example #1
0
class PolygonType(Serializable):
    _fields = ('Polygon', )
    _required = ('Polygon', )
    _collections_tags = {'Polygon': {'array': True, 'child_tag': 'Vertex'}}
    # descriptors
    Polygon = SerializableArrayDescriptor(
        'Polygon', LatLonArrayElementType, _collections_tags, _required, strict=DEFAULT_STRICT, minimum_length=3,
        docstring='A geographic polygon (array) with WGS-84 coordinates.'
    )  # type: Union[SerializableArray, List[LatLonArrayElementType]]

    def __init__(self, Polygon=None, **kwargs):
        """

        Parameters
        ----------
        Polygon: SerializableArray|List[LatLonArrayElementType]|numpy.ndarray|list|tuple
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.Polygon = Polygon
        super(PolygonType, self).__init__(**kwargs)
Example #2
0
class LineType(Serializable):
    _fields = ('EndPoints', )
    _required = ('EndPoints', )
    _collections_tags = {'EndPoints': {'array': True, 'child_tag': 'Endpoint'}}
    # descriptors
    EndPoints = SerializableArrayDescriptor(
        'EndPoints', LatLonArrayElementType, _collections_tags, _required, strict=DEFAULT_STRICT, minimum_length=2,
        docstring='A geographic line (array) with WGS-84 coordinates.'
    )  # type: Union[SerializableArray, List[LatLonArrayElementType]]

    def __init__(self, EndPoints=None, **kwargs):
        """

        Parameters
        ----------
        EndPoints : SerializableArray|List[LatLonArrayElementType]|numpy.ndarray|list|tuple
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.EndPoints = EndPoints
        super(LineType, self).__init__(**kwargs)
Example #3
0
class BandEqualizationType(Serializable):
    """

    """
    _fields = ('Algorithm', 'BandLUTs')
    _required = ('Algorithm', 'BandLUTs')
    _collections_tags = {'BandLUTs': {'array': True, 'child_tag': 'BandLUT'}}
    # Descriptor
    Algorithm = StringEnumDescriptor(
        'Algorithm', ('1DLUT', ), _required, strict=DEFAULT_STRICT, default_value='1DLUT',
        docstring='The algorithm type.')  # type: str
    BandLUTs = SerializableArrayDescriptor(
        'BandLUTs', BandLUTType, _collections_tags, _required, strict=DEFAULT_STRICT, array_extension=BandLUTArray,
        docstring='')  # type: Union[BandLUTArray, List[BandLUTType]]

    def __init__(self, Algorithm='1DLUT', BandLUTs=None, **kwargs):
        """

        Parameters
        ----------
        Algorithm : str
            `1DLUT` is currently the only allowed value.
        BandLUTs : BandLUTArray|List[BandLUTType]
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.Algorithm = Algorithm
        self.BandLUTs = BandLUTs
        super(BandEqualizationType, self).__init__(**kwargs)
Example #4
0
class SegmentType(Serializable):
    """
    Rectangle segment.
    """

    _fields = ('Identifier', 'StartLine', 'StartSample', 'EndLine', 'EndSample', 'SegmentPolygon')
    _required = ('Identifier', 'StartLine', 'StartSample', 'EndLine', 'EndSample')
    _collections_tags = {'SegmentPolygon': {'array': True, 'child_tag': 'SV'}}
    # descriptors
    Identifier = StringDescriptor(
        'Identifier', _required, strict=DEFAULT_STRICT,
        docstring='String that uniquely identifies the Image Segment.')  # type: str
    StartLine = IntegerDescriptor(
        'StartLine', _required, strict=DEFAULT_STRICT,
        docstring='Start line of the segment.')  # type: int
    StartSample = IntegerDescriptor(
        'StartSample', _required, strict=DEFAULT_STRICT,
        docstring='Start sample of the segment.')  # type: int
    EndLine = IntegerDescriptor(
        'EndLine', _required, strict=DEFAULT_STRICT,
        docstring='End line of the segment.')  # type: int
    EndSample = IntegerDescriptor(
        'EndSample', _required, strict=DEFAULT_STRICT,
        docstring='End sample of the segment.')  # type: int
    SegmentPolygon = SerializableArrayDescriptor(
        'SegmentPolygon', LSVertexType, _collections_tags, _required,
        strict=DEFAULT_STRICT, minimum_length=3,
        docstring='Polygon that describes a portion of the segment '
                  'rectangle.')  # type: Union[SerializableArray, List[LSVertexType]]

    def __init__(self, Identifier=None, StartLine=None, StartSample=None, EndLine=None,
                 EndSample=None, SegmentPolygon=None, **kwargs):
        """

        Parameters
        ----------
        Identifier : str
        StartLine : int
        StartSample : int
        EndLine : int
        EndSample : int
        SegmentPolygon : SerializableArray|List[LSVertexType]|numpy.ndarray|list|tuple
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.Identifier = Identifier
        self.StartLine = StartLine
        self.StartSample = StartSample
        self.EndLine = EndLine
        self.EndSample = EndSample
        self.SegmentPolygon = SegmentPolygon
        super(SegmentType, self).__init__(**kwargs)
Example #5
0
class ImageGridType(Serializable):
    """
    Parameters that describe a geo-referenced image grid for image data products
    that may be formed from the CPHD signal array(s).
    """

    _fields = ('Identifier', 'IARPLocation', 'IAXExtent', 'IAYExtent', 'SegmentList')
    _required = ('IARPLocation', 'IAXExtent', 'IAYExtent')
    _collections_tags = {'SegmentList': {'array': True, 'child_tag': 'Segment'}}
    # descriptors
    Identifier = StringDescriptor(
        'Identifier', _required, strict=DEFAULT_STRICT,
        docstring='String that uniquely identifies the Image Grid.')  # type: Union[None, str]
    IARPLocation = SerializableDescriptor(
        'IARPLocation', LSType, _required, strict=DEFAULT_STRICT,
        docstring='IARP grid location. Grid locations indexed by (line, sample) or (L,S). '
                  'Image grid line and sample are pixel-centered indices.')  # type: LSType
    IAXExtent = SerializableDescriptor(
        'IAXExtent', IAXExtentType, _required, strict=DEFAULT_STRICT,
        docstring='Increasing line index is in the +IAX direction.')  # type: IAXExtentType
    IAYExtent = SerializableDescriptor(
        'IAYExtent', IAYExtentType, _required, strict=DEFAULT_STRICT,
        docstring='Increasing sample index is in the +IAY direction.')  # type: IAYExtentType
    SegmentList = SerializableArrayDescriptor(
        'SegmentList', SegmentType, _collections_tags, _required, strict=DEFAULT_STRICT,
        array_extension=SegmentListType,
        docstring='List of image grid segments defined relative to the image '
                  'grid.')  # type: Union[SegmentListType, List[SegmentType]]

    def __init__(self, Identifier=None, IARPLocation=None, IAXExtent=None, IAYExtent=None,
                 SegmentList=None, **kwargs):
        """

        Parameters
        ----------
        Identifier : None|str
        IARPLocation : LSType
        IAXExtent : IAXExtentType
        IAYExtent : IAYExtentType
        SegmentList : SegmentListType|List[SegmentType]|numpy.array|list|tuple
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.Identifier = Identifier
        self.IARPLocation = IARPLocation
        self.IAXExtent = IAXExtent
        self.IAYExtent = IAYExtent
        self.SegmentList = SegmentList
        super(ImageGridType, self).__init__(**kwargs)
Example #6
0
class AreaType(Serializable):
    """
    An area object.
    """

    _fields = ('X1Y1', 'X2Y2', 'Polygon')
    _required = _fields
    _collections_tags = {'Polygon': {'array': True, 'child_tag': 'Vertex'}}
    # descriptors
    X1Y1 = SerializableDescriptor(
        'X1Y1',
        XYType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='*"Minimum"* corner of the rectangle in Image '
        'coordinates.')  # type: XYType
    X2Y2 = SerializableDescriptor(
        'X2Y2',
        XYType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='*"Maximum"* corner of the rectangle in Image '
        'coordinates.')  # type: XYType
    Polygon = SerializableArrayDescriptor(
        'Polygon',
        XYVertexType,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        minimum_length=3,
        docstring='Polygon further reducing the bounding box, in Image '
        'coordinates.')  # type: Union[SerializableArray, List[XYVertexType]]

    def __init__(self, X1Y1=None, X2Y2=None, Polygon=None, **kwargs):
        """

        Parameters
        ----------
        X1Y1 : XYType|numpy.ndarray|list|tuple
        X2Y2 : XYType|numpy.ndarray|list|tuple
        Polygon : SerializableArray|List[XYVertexType]|numpy.ndarray|list|tuple
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.X1Y1 = X1Y1
        self.X2Y2 = X2Y2
        self.Polygon = Polygon
        super(AreaType, self).__init__(**kwargs)
Example #7
0
class NoiseLevelType(Serializable):
    """
    Thermal noise level for the reference signal vector.
    """

    _fields = ('PNRef', 'BNRef', 'FxNoiseProfile')
    _required = ('PNRef', 'BNRef')
    _collections_tags = {
        'FxNoiseProfile': {'array': True, 'child_tag': 'Point'}}
    _numeric_format = {'PNRef': FLOAT_FORMAT, 'BNRef': FLOAT_FORMAT}
    # descriptors
    PNRef = FloatDescriptor(
        'PNRef', _required, strict=DEFAULT_STRICT, bounds=(0, None),
        docstring='Noise power level for thermal noise.')  # type: float
    BNRef = FloatDescriptor(
        'BNRef', _required, strict=DEFAULT_STRICT, bounds=(0, 1),
        docstring='Noise Equivalent BW for noise signal. Bandwidth BN is '
                  'expressed relative to the sample bandwidth.')  # type: float
    FxNoiseProfile = SerializableArrayDescriptor(
        'FxNoiseProfile', FxPNPointType, _collections_tags, _required, strict=DEFAULT_STRICT,
        minimum_length=2, array_extension=FxNoiseProfileType,
        docstring='FX Domain Noise Level Profile. Power level for thermal noise (PN) vs. FX '
                  'frequency values.')  # type: Union[None, FxNoiseProfileType, List[FxPNPointType]]

    def __init__(self, PNRef=None, BNRef=None, FxNoiseProfile=None, **kwargs):
        """

        Parameters
        ----------
        PNRef : float
        BNRef : float
        FxNoiseProfile : FxNoiseProfileType|List[FxPNPointType]
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.PNRef = PNRef
        self.BNRef = BNRef
        self.FxNoiseProfile = FxNoiseProfile
        super(NoiseLevelType, self).__init__(**kwargs)
Example #8
0
class TargetInformationType(Serializable):
    """
    Information about the target.
    """
    _fields = ('Identifiers', 'Footprint', 'TargetInformationExtensions')
    _required = ()
    _collections_tags = {
        'Identifiers': {'array': False, 'child_tag': 'Identifier'},
        'Footprint': {'array': True, 'child_tag': 'Vertex'},
        'TargetInformationExtensions': {'array': False, 'child_tag': 'TargetInformationExtension'}}
    # Descriptors
    Identifiers = ParametersDescriptor(
        'Identifiers', _collections_tags, _required, strict=DEFAULT_STRICT,
        docstring='Target may have one or more identifiers.  Examples: names, BE numbers, etc. Use '
                  'the "name" attribute to describe what this is.')  # type: ParametersCollection
    Footprint = SerializableArrayDescriptor(
        'Footprint', LatLonArrayElementType, _collections_tags, _required, strict=DEFAULT_STRICT,
        docstring='Target footprint as defined by polygonal '
                  'shape.')  # type: Union[SerializableArray, List[LatLonArrayElementType]]
    TargetInformationExtensions = ParametersDescriptor(
        'TargetInformationExtensions', _collections_tags, _required, strict=DEFAULT_STRICT,
        docstring='Generic extension. Could be used to indicate type of target, '
                  'terrain, etc.')  # type: ParametersCollection

    def __init__(self, Identifiers=None, Footprint=None, TargetInformationExtensions=None, **kwargs):
        """

        Parameters
        ----------
        Identifiers : None|ParametersCollection|dict
        Footprint : None|List[LatLonArrayElementType]|numpy.ndarray|list|tuple
        TargetInformationExtensions : None|ParametersCollection|dict
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.Identifiers = Identifiers
        self.Footprint = Footprint
        self.TargetInformationExtensions = TargetInformationExtensions
        super(TargetInformationType, self).__init__(**kwargs)
Example #9
0
class GeographicCoverageType(Serializable):
    """
    The geographic coverage area for the product.
    """

    _fields = ('GeoregionIdentifiers', 'Footprint', 'GeographicInfo')
    _required = ('Footprint', )
    _collections_tags = {
        'GeoregionIdentifiers': {'array': False, 'child_tag': 'GeoregionIdentifier'},
        'Footprint': {'array': True, 'child_tag': 'Vertex'},
        'SubRegions': {'array': False, 'child_tag': 'SubRegion'}}
    # Descriptors
    GeoregionIdentifiers = ParametersDescriptor(
        'GeoregionIdentifiers', _collections_tags, _required, strict=DEFAULT_STRICT,
        docstring='Target may have one or more identifiers.  Examples: names, BE numbers, etc. Use '
                  'the "name" attribute to describe what this is.')  # type: ParametersCollection
    Footprint = SerializableArrayDescriptor(
        'Footprint', LatLonArrayElementType, _collections_tags, _required, strict=DEFAULT_STRICT,
        docstring='Estimated ground footprint of the '
                  'product.')  # type: Union[None, SerializableArray, List[LatLonArrayElementType]]
    GeographicInfo = SerializableDescriptor(
        'GeographicInfo', GeographicInformationType, _required, strict=DEFAULT_STRICT,
        docstring='')  # type: Union[None, GeographicInformationType]

    def __init__(self, GeoregionIdentifiers=None, Footprint=None, SubRegions=None, GeographicInfo=None, **kwargs):
        """

        Parameters
        ----------
        GeoregionIdentifiers : None|ParametersCollection|dict
        Footprint : None|List[LatLonArrayElementType]|numpy.ndarray|list|tuple
        SubRegions : None|List[GeographicCoverageType]
        GeographicInfo : None|GeographicInformationType
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.GeoregionIdentifiers = GeoregionIdentifiers
        self.Footprint = Footprint
        self.GeographicInfo = GeographicInfo

        self._SubRegions = []
        if SubRegions is None:
            pass
        elif isinstance(SubRegions, GeographicCoverageType):
            self.addSubRegion(SubRegions)
        elif isinstance(SubRegions, (list, tuple)):
            for el in SubRegions:
                self.addSubRegion(el)
        else:
            raise ('SubRegions got unexpected type {}'.format(type(SubRegions)))
        super(GeographicCoverageType, self).__init__(**kwargs)

    @property
    def SubRegions(self):
        """
        List[GeographicCoverageType]: list of sub-regions.
        """

        return self._SubRegions

    def addSubRegion(self, value):
        """
        Add the given SubRegion to the SubRegions list.

        Parameters
        ----------
        value : GeographicCoverageType

        Returns
        -------
        None
        """

        if isinstance(value, ElementTree.Element):
            value = GeographicCoverageType.from_node(value, self._xml_ns, ns_key=self._xml_ns_key)
        elif isinstance(value, dict):
            value = GeographicCoverageType.from_dict(value)

        if isinstance(value, GeographicCoverageType):
            self._SubRegions.append(value)
        else:
            raise TypeError('Trying to set SubRegion element with unexpected type {}'.format(type(value)))

    @classmethod
    def from_node(cls, node, xml_ns, ns_key=None, kwargs=None):
        if kwargs is None:
            kwargs = OrderedDict()
        kwargs['SubRegions'] = find_children(node, 'SubRegion', xml_ns, ns_key)
        return super(GeographicCoverageType, cls).from_node(node, xml_ns, ns_key=ns_key, kwargs=kwargs)

    def to_node(self, doc, tag, ns_key=None, parent=None, check_validity=False, strict=DEFAULT_STRICT, exclude=()):
        node = super(GeographicCoverageType, self).to_node(
            doc, tag, ns_key=ns_key, parent=parent, check_validity=check_validity, strict=strict, exclude=exclude)
        # slap on the SubRegion children
        sub_key = self._child_xml_ns_key.get('SubRegions', ns_key)
        for entry in self._SubRegions:
            entry.to_node(doc, 'SubRegion', ns_key=sub_key, parent=node, strict=strict)
        return node

    def to_dict(self, check_validity=False, strict=DEFAULT_STRICT, exclude=()):
        out = super(GeographicCoverageType, self).to_dict(check_validity=check_validity, strict=strict, exclude=exclude)
        # slap on the SubRegion children
        if len(self.SubRegions) > 0:
            out['SubRegions'] = [
                entry.to_dict(check_validity=check_validity, strict=strict) for entry in self._SubRegions]
        return out
Example #10
0
class PositionType(Serializable):
    """The details for platform and ground reference positions as a function of time since collection start."""
    _fields = ('ARPPoly', 'GRPPoly', 'TxAPCPoly', 'RcvAPC')
    _required = ('ARPPoly', )
    _collections_tags = {'RcvAPC': {'array': True, 'child_tag': 'RcvAPCPoly'}}

    # descriptors
    ARPPoly = SerializableDescriptor(
        'ARPPoly',
        XYZPolyType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Aperture Reference Point (ARP) position polynomial in ECF as a function of elapsed '
        'seconds since start of collection.')  # type: XYZPolyType
    GRPPoly = SerializableDescriptor(
        'GRPPoly',
        XYZPolyType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Ground Reference Point (GRP) position polynomial in ECF as a function of elapsed '
        'seconds since start of collection.')  # type: XYZPolyType
    TxAPCPoly = SerializableDescriptor(
        'TxAPCPoly',
        XYZPolyType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Transmit Aperture Phase Center (APC) position polynomial in ECF as a function of '
        'elapsed seconds since start of collection.')  # type: XYZPolyType
    RcvAPC = SerializableArrayDescriptor(
        'RcvAPC',
        XYZPolyAttributeType,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Receive Aperture Phase Center polynomials array. '
        'Each polynomial has output in ECF, and represents a function of elapsed seconds since start of '
        'collection.'
    )  # type: Union[SerializableArray, List[XYZPolyAttributeType]]

    def __init__(self,
                 ARPPoly=None,
                 GRPPoly=None,
                 TxAPCPoly=None,
                 RcvAPC=None,
                 **kwargs):
        """

        Parameters
        ----------
        ARPPoly : XYZPolyType
        GRPPoly : XYZPolyType
        TxAPCPoly : XYZPolyType
        RcvAPC : SerializableArray|List[XYZPolyAttributeType]|list|tuple
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.ARPPoly = ARPPoly
        self.GRPPoly = GRPPoly
        self.TxAPCPoly = TxAPCPoly
        self.RcvAPC = RcvAPC
        super(PositionType, self).__init__(**kwargs)

    def _derive_arp_poly(self, SCPCOA):
        """
        Expected to be called from SICD parent. Set the aperture position polynomial from position, time,
        acceleration at scptime, if necessary.

        .. Note::

            This assumes constant velocity and acceleration.

        Parameters
        ----------
        SCPCOA : sarpy.io.complex.sicd_elements.SCPCOA.SCPCOAType

        Returns
        -------
        None
        """

        if self.ARPPoly is not None:
            return  # nothing to be done

        if SCPCOA is None or SCPCOA.ARPPos is None or SCPCOA.ARPVel is None or SCPCOA.SCPTime is None:
            return  # not enough information to derive

        if SCPCOA.ARPAcc is None:
            SCPCOA.ARPAcc = XYZType.from_array((0, 0, 0))
        # define the polynomial
        coefs = numpy.zeros((3, 3), dtype=numpy.float64)
        scptime = SCPCOA.SCPTime
        pos = SCPCOA.ARPPos.get_array()
        vel = SCPCOA.ARPVel.get_array()
        acc = SCPCOA.ARPAcc.get_array()
        coefs[:, 0] = pos - vel * scptime + 0.5 * acc * scptime * scptime
        coefs[:, 1] = vel - acc * scptime
        coefs[:, 2] = acc
        self.ARPPoly = XYZPolyType(X=coefs[0, :], Y=coefs[1, :], Z=coefs[2, :])

    def _basic_validity_check(self):
        condition = super(PositionType, self)._basic_validity_check()
        if self.ARPPoly is not None and \
                (self.ARPPoly.X.order1 < 1 or self.ARPPoly.Y.order1 < 1 or self.ARPPoly.Z.order1 < 1):
            self.log_validity_error(
                'ARPPoly should be order at least 1 in each component. '
                'Got X.order1 = {}, Y.order1 = {}, and Z.order1 = {}'.format(
                    self.ARPPoly.X.order1, self.ARPPoly.Y.order1,
                    self.ARPPoly.Z.order1))
            condition = False
        return condition
Example #11
0
class ImageDataType(Serializable):
    """The image pixel data."""
    _collections_tags = {
        'AmpTable': {
            'array': True,
            'child_tag': 'Amplitude'
        },
        'ValidData': {
            'array': True,
            'child_tag': 'Vertex'
        },
    }
    _fields = ('PixelType', 'AmpTable', 'NumRows', 'NumCols', 'FirstRow',
               'FirstCol', 'FullImage', 'SCPPixel', 'ValidData')
    _required = ('PixelType', 'NumRows', 'NumCols', 'FirstRow', 'FirstCol',
                 'FullImage', 'SCPPixel')
    _numeric_format = {'AmpTable': FLOAT_FORMAT}
    _PIXEL_TYPE_VALUES = ("RE32F_IM32F", "RE16I_IM16I", "AMP8I_PHS8I")
    # descriptors
    PixelType = StringEnumDescriptor(
        'PixelType',
        _PIXEL_TYPE_VALUES,
        _required,
        strict=True,
        docstring=
        "The PixelType attribute which specifies the interpretation of the file data."
    )  # type: str
    AmpTable = FloatArrayDescriptor(
        'AmpTable',
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        minimum_length=256,
        maximum_length=256,
        docstring="The amplitude look-up table. This is required if "
        "`PixelType == 'AMP8I_PHS8I'`")  # type: numpy.ndarray
    NumRows = IntegerDescriptor(
        'NumRows',
        _required,
        strict=True,
        docstring='The number of Rows in the product. May include zero rows.'
    )  # type: int
    NumCols = IntegerDescriptor(
        'NumCols',
        _required,
        strict=True,
        docstring='The number of Columns in the product. May include zero rows.'
    )  # type: int
    FirstRow = IntegerDescriptor(
        'FirstRow',
        _required,
        strict=DEFAULT_STRICT,
        docstring='Global row index of the first row in the product. '
        'Equal to 0 in full image product.')  # type: int
    FirstCol = IntegerDescriptor(
        'FirstCol',
        _required,
        strict=DEFAULT_STRICT,
        docstring='Global column index of the first column in the product. '
        'Equal to 0 in full image product.')  # type: int
    FullImage = SerializableDescriptor(
        'FullImage',
        FullImageType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Original full image product.')  # type: FullImageType
    SCPPixel = SerializableDescriptor(
        'SCPPixel',
        RowColType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Scene Center Point pixel global row and column index. Should be located near the '
        'center of the full image.')  # type: RowColType
    ValidData = SerializableArrayDescriptor(
        'ValidData',
        RowColArrayElement,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        minimum_length=3,
        docstring=
        'Indicates the full image includes both valid data and some zero filled pixels. '
        'Simple polygon encloses the valid data (may include some zero filled pixels for simplification). '
        'Vertices in clockwise order.'
    )  # type: Union[SerializableArray, List[RowColArrayElement]]

    def __init__(self,
                 PixelType=None,
                 AmpTable=None,
                 NumRows=None,
                 NumCols=None,
                 FirstRow=None,
                 FirstCol=None,
                 FullImage=None,
                 SCPPixel=None,
                 ValidData=None,
                 **kwargs):
        """

        Parameters
        ----------
        PixelType : str
        AmpTable : numpy.ndarray|list|tuple
        NumRows : int
        NumCols : int
        FirstRow : int
        FirstCol : int
        FullImage : FullImageType|numpy.ndarray|list|tuple
        SCPPixel : RowColType|numpy.ndarray|list|tuple
        ValidData : SerializableArray|List[RowColArrayElement]|numpy.ndarray|list|tuple
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.PixelType = PixelType
        self.AmpTable = AmpTable
        self.NumRows, self.NumCols = NumRows, NumCols
        self.FirstRow, self.FirstCol = FirstRow, FirstCol
        self.FullImage = FullImage
        self.SCPPixel = SCPPixel
        self.ValidData = ValidData
        super(ImageDataType, self).__init__(**kwargs)

    def _check_valid_data(self):
        if self.ValidData is None:
            return True
        if len(self.ValidData) < 2:
            return True

        value = True
        valid_data = self.ValidData.get_array(dtype='float64')
        lin_ring = LinearRing(coordinates=valid_data)
        area = lin_ring.get_area()
        if area == 0:
            self.log_validity_error('ValidData encloses no area.')
            value = False
        elif area > 0:
            self.log_validity_error(
                "ValidData must be traversed in clockwise direction.")
            value = False
        for i, entry in enumerate(valid_data):
            if not (
                (self.FirstRow <= entry[0] <= self.FirstRow + self.NumRows) and
                (self.FirstCol <= entry[1] <= self.FirstCol + self.NumCols)):
                self.log_validity_warning(
                    'ValidData entry {} is not contained in the image bounds'.
                    format(i))
                value = False
        return value

    def _basic_validity_check(self):
        condition = super(ImageDataType, self)._basic_validity_check()
        if (self.PixelType == 'AMP8I_PHS8I') and (self.AmpTable is None):
            self.log_validity_error(
                "We have `PixelType='AMP8I_PHS8I'` and `AmpTable` is not defined for ImageDataType."
            )
            condition = False
        if (self.PixelType != 'AMP8I_PHS8I') and (self.AmpTable is not None):
            self.log_validity_error(
                "We have `PixelType != 'AMP8I_PHS8I'` and `AmpTable` is defined for ImageDataType."
            )
            condition = False
        if (self.ValidData is not None) and (len(self.ValidData) < 3):
            self.log_validity_error(
                "We have `ValidData` defined with fewer than 3 entries.")
            condition = False
        condition &= self._check_valid_data()
        return condition

    def get_valid_vertex_data(self, dtype=numpy.int64):
        """
        Gets an array of `[row, col]` indices defining the valid data. If this is not viable, then `None`
        will be returned.

        Parameters
        ----------
        dtype : object
            the data type for the array

        Returns
        -------
        numpy.ndarray|None
        """

        if self.ValidData is None:
            return None
        out = numpy.zeros((self.ValidData.size, 2), dtype=dtype)
        for i, entry in enumerate(self.ValidData):
            out[i, :] = entry.get_array(dtype=dtype)
        return out

    def get_full_vertex_data(self, dtype=numpy.int64):
        """
        Gets an array of `[row, col]` indices defining the full vertex data. If this is not viable, then `None`
        will be returned.

        Parameters
        ----------
        dtype : object
            the data type for the array

        Returns
        -------
        numpy.ndarray|None
        """

        if self.NumRows is None or self.NumCols is None:
            return None
        return numpy.array(
            [[0, 0], [0, self.NumCols - 1],
             [self.NumRows - 1, self.NumCols - 1], [self.NumRows - 1, 0]],
            dtype=dtype)

    def get_pixel_size(self) -> int:
        """
        Gets the size per pixel, in bytes.

        Returns
        -------
        int
        """

        if self.PixelType == "RE32F_IM32F":
            return 8
        elif self.PixelType == "RE16I_IM16I":
            return 4
        elif self.PixelType == "AMP8I_PHS8I":
            return 2
        else:
            raise ValueError('Got unhandled pixel type `{}`'.format(
                self.PixelType))
Example #12
0
class MeasurementType(Serializable):
    """
    Geometric SAR information required for measurement/geolocation.
    """

    _fields = ('PolynomialProjection', 'GeographicProjection',
               'PlaneProjection', 'CylindricalProjection', 'PixelFootprint',
               'ARPFlag', 'ARPPoly', 'ValidData')
    _required = ('PixelFootprint', 'ARPPoly', 'ValidData')
    _collections_tags = {'ValidData': {'array': True, 'child_tag': 'Vertex'}}
    _numeric_format = {'ValidData': '0.16G'}
    _choice = ({
        'required':
        False,
        'collection': ('PolynomialProjection', 'GeographicProjection',
                       'PlaneProjection', 'CylindricalProjection')
    }, )
    # Descriptor
    PolynomialProjection = SerializableDescriptor(
        'PolynomialProjection',
        PolynomialProjectionType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Polynomial pixel to ground. Should only used for sensor systems where the radar '
        'geometry parameters are not recorded.'
    )  # type: Union[None, PolynomialProjectionType]
    GeographicProjection = SerializableDescriptor(
        'GeographicProjection',
        GeographicProjectionType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Geographic mapping of the pixel grid referred to as GGD in the '
        'Design and Exploitation document.'
    )  # type: Union[None, GeographicProjectionType]
    PlaneProjection = SerializableDescriptor(
        'PlaneProjection',
        PlaneProjectionType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Planar representation of the pixel grid referred to as PGD in the '
        'Design and Exploitation document.'
    )  # type: Union[None, PlaneProjectionType]
    CylindricalProjection = SerializableDescriptor(
        'CylindricalProjection',
        CylindricalProjectionType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Cylindrical mapping of the pixel grid referred to as CGD in the '
        'Design and Exploitation document.'
    )  # type: Union[None, CylindricalProjectionType]
    PixelFootprint = SerializableDescriptor(
        'PixelFootprint',
        RowColIntType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Size of the image in pixels.')  # type: RowColIntType
    ARPFlag = StringEnumDescriptor(
        'ARPFlag', ('REALTIME', 'PREDICTED', 'POST PROCESSED'),
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Flag indicating whether ARP polynomial is based on the best available (`collect time` or '
        '`predicted`) ephemeris.')  # type: Union[None, str]
    ARPPoly = SerializableDescriptor('ARPPoly',
                                     XYZPolyType,
                                     _required,
                                     strict=DEFAULT_STRICT,
                                     docstring='')  # type: XYZPolyType
    ValidData = SerializableArrayDescriptor(
        'ValidData',
        RowColArrayElement,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        minimum_length=3,
        docstring=
        'Indicates the full image includes both valid data and some zero filled pixels. '
        'Simple polygon encloses the valid data (may include some zero filled pixels for simplification). '
        'Vertices in clockwise order.'
    )  # type: Union[SerializableArray, List[RowColArrayElement]]

    def __init__(self,
                 PolynomialProjection=None,
                 GeographicProjection=None,
                 PlaneProjection=None,
                 CylindricalProjection=None,
                 PixelFootprint=None,
                 ARPFlag=None,
                 ARPPoly=None,
                 ValidData=None,
                 **kwargs):
        """

        Parameters
        ----------
        PolynomialProjection : PolynomialProjectionType
        GeographicProjection : GeographicProjectionType
        PlaneProjection : PlaneProjectionType
        CylindricalProjection : CylindricalProjectionType
        PixelFootprint : RowColIntType|numpy.ndarray|list|tuple
        ARPFlag : str
        ARPPoly : XYZPolyType|numpy.ndarray|list|tuple
        ValidData : SerializableArray|List[RowColArrayElement]|numpy.ndarray|list|tuple
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.PolynomialProjection = PolynomialProjection
        self.GeographicProjection = GeographicProjection
        self.PlaneProjection = PlaneProjection
        self.CylindricalProjection = CylindricalProjection
        self.PixelFootprint = PixelFootprint
        self.ARPFlag = ARPFlag
        self.ARPPoly = ARPPoly
        self.ValidData = ValidData
        super(MeasurementType, self).__init__(**kwargs)

    @property
    def ProjectionType(self):
        """str: *READ ONLY* Identifies the specific image projection type supplied."""
        for attribute in self._choice[0]['collection']:
            if getattr(self, attribute) is not None:
                return attribute
        return None
Example #13
0
class RadarCollectionType(Serializable):
    """The Radar Collection Type"""
    _fields = (
        'TxFrequency', 'RefFreqIndex', 'Waveform', 'TxPolarization', 'TxSequence', 'RcvChannels', 'Area', 'Parameters')
    _required = ('TxFrequency', 'TxPolarization', 'RcvChannels')
    _collections_tags = {
        'Waveform': {'array': True, 'child_tag': 'WFParameters'},
        'TxSequence': {'array': True, 'child_tag': 'TxStep'},
        'RcvChannels': {'array': True, 'child_tag': 'ChanParameters'},
        'Parameters': {'array': False, 'child_tag': 'Parameter'}}
    # descriptors
    TxFrequency = SerializableDescriptor(
        'TxFrequency', TxFrequencyType, _required, strict=DEFAULT_STRICT,
        docstring='The transmit frequency range.')  # type: TxFrequencyType
    RefFreqIndex = IntegerDescriptor(
        'RefFreqIndex', _required, strict=DEFAULT_STRICT,
        docstring='The reference frequency index, if applicable. If present and non-zero, '
                  'all (most) RF frequency values are expressed as offsets from a reference '
                  'frequency.')  # type: int
    Waveform = SerializableArrayDescriptor(
        'Waveform', WaveformParametersType, _collections_tags, _required,
        strict=DEFAULT_STRICT, minimum_length=1,
        docstring='Transmit and receive demodulation waveform parameters.'
    )  # type: Union[SerializableArray, List[WaveformParametersType]]
    TxPolarization = StringEnumDescriptor(
        'TxPolarization', POLARIZATION1_VALUES, _required, strict=DEFAULT_STRICT,
        docstring='The transmit polarization.')  # type: str
    TxSequence = SerializableArrayDescriptor(
        'TxSequence', TxStepType, _collections_tags, _required, strict=DEFAULT_STRICT, minimum_length=1,
        docstring='The transmit sequence parameters array. If present, indicates the transmit signal steps through '
                  'a repeating sequence of waveforms and/or polarizations. '
                  'One step per Inter-Pulse Period.')  # type: Union[SerializableArray, List[TxStepType]]
    RcvChannels = SerializableArrayDescriptor(
        'RcvChannels', ChanParametersType, _collections_tags,
        _required, strict=DEFAULT_STRICT, minimum_length=1,
        docstring='Receive data channel parameters.')  # type: Union[SerializableArray, List[ChanParametersType]]
    Area = SerializableDescriptor(
        'Area', AreaType, _required, strict=DEFAULT_STRICT,
        docstring='The imaged area covered by the collection.')  # type: AreaType
    Parameters = ParametersDescriptor(
        'Parameters', _collections_tags, _required, strict=DEFAULT_STRICT,
        docstring='A parameters collections.')  # type: ParametersCollection

    def __init__(self, TxFrequency=None, RefFreqIndex=None, Waveform=None,
                 TxPolarization=None, TxSequence=None, RcvChannels=None,
                 Area=None, Parameters=None, **kwargs):
        """

        Parameters
        ----------
        TxFrequency : TxFrequencyType|numpy.ndarray|list|tuple
        RefFreqIndex : int
        Waveform : SerializableArray|List[WaveformParametersType]
        TxPolarization : str
        TxSequence : SerializableArray|List[TxStepType]
        RcvChannels : SerializableArray|List[ChanParametersType]
        Area : AreaType
        Parameters : ParametersCollection|dict
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.TxFrequency = TxFrequency
        self.RefFreqIndex = RefFreqIndex
        self.Waveform = Waveform
        self.TxPolarization = TxPolarization
        self.TxSequence = TxSequence
        self.RcvChannels = RcvChannels
        self.Area = Area
        self.Parameters = Parameters
        super(RadarCollectionType, self).__init__(**kwargs)

    def derive(self):
        """
        Populates derived data in RadarCollection. Expected to be called by `SICD` parent.

        Returns
        -------
        None
        """

        self._derive_tx_polarization()
        if self.Area is not None:
            self.Area.derive()
        if self.Waveform is not None:
            for entry in self.Waveform:
                entry.derive()
        self._derive_tx_frequency()  # call after waveform entry derive call
        self._derive_wf_params()

    def _derive_tx_polarization(self):
        def check_sequence():
            unique_entries = set(entry.TxPolarization for entry in self.TxSequence)
            if len(unique_entries) == 1:
                self.TxPolarization = self.TxSequence[0].TxPolarization
            else:
                self.TxPolarization = 'SEQUENCE'

        # TxPolarization was optional prior to SICD 1.0. It may need to be derived.
        if self.TxSequence is not None:
            check_sequence()
            return
        if self.TxPolarization is not None:
            return  # nothing to be done

        if self.RcvChannels is None:
            return  # nothing to derive from

        if len(self.RcvChannels) > 1:
            # TxSequence may need to be derived from RCvChannels, for SICD before 1.0 or poorly formed
            if self.TxSequence is not None or self.RcvChannels is None or len(self.RcvChannels) < 2:
                return

            tx_pols = list(chan_param.get_transmit_polarization() for chan_param in self.RcvChannels)

            if len(tx_pols) > 1:
                self.TxSequence = [TxStepType(index=i+1, TxPolarization=tx_pol) for i, tx_pol in enumerate(tx_pols)]
                check_sequence()
            else:
                self.TxPolarization = tx_pols[0]
        else:
            self.TxPolarization = self.RcvChannels[0].get_transmit_polarization()

    def _derive_tx_frequency(self):
        if self.Waveform is None or self.Waveform.size == 0:
            return  # nothing to be done
        if not(self.TxFrequency is None or self.TxFrequency.Min is None or self.TxFrequency.Max is None):
            return  # no need to do anything

        if self.TxFrequency is None:
            self.TxFrequency = TxFrequencyType()
        if self.TxFrequency.Min is None:
            self.TxFrequency.Min = min(
                entry.TxFreqStart for entry in self.Waveform if entry.TxFreqStart is not None)
        if self.TxFrequency.Max is None:
            self.TxFrequency.Max = max(
                (entry.TxFreqStart + entry.TxRFBandwidth) for entry in self.Waveform if
                entry.TxFreqStart is not None and entry.TxRFBandwidth is not None)

    def _derive_wf_params(self):
        if self.TxFrequency is None or self.TxFrequency.Min is None or self.TxFrequency.Max is None:
            return  # nothing that we can do
        if self.Waveform is None or self.Waveform.size != 1:
            return  # nothing to be done

        entry = self.Waveform[0]  # only true for single waveform definition
        if entry.TxFreqStart is None:
            entry.TxFreqStart = self.TxFrequency.Min
        if entry.TxRFBandwidth is None:
            entry.TxRFBandwidth = self.TxFrequency.Max - self.TxFrequency.Min

    def _apply_reference_frequency(self, reference_frequency):
        """
        If the reference frequency is used, adjust the necessary fields accordingly.
        Expected to be called by `SICD` parent.

        Parameters
        ----------
        reference_frequency : float
            The reference frequency.

        Returns
        -------
        None
        """

        if self.TxFrequency is not None:
            # noinspection PyProtectedMember
            self.TxFrequency._apply_reference_frequency(reference_frequency)
        if self.Waveform is not None:
            for entry in self.Waveform:
                # noinspection PyProtectedMember
                entry._apply_reference_frequency(reference_frequency)
        self.RefFreqIndex = 0

    def get_polarization_abbreviation(self):
        """
        Gets the polarization collection abbreviation for the suggested name.

        Returns
        -------
        str
        """

        if self.RcvChannels is None:
            pol_count = 0
        else:
            pol_count = len(self.RcvChannels)
        if pol_count == 1:
            return 'S'
        elif pol_count == 2:
            return 'D'
        elif pol_count == 3:
            return 'T'
        elif pol_count > 3:
            return 'Q'
        else:
            return 'U'

    def _check_frequency(self):
        # type: () -> bool

        if self.RefFreqIndex is not None:
            return True

        if self.TxFrequency is not None and self.TxFrequency.Min is not None \
                and self.TxFrequency.Min <= 0:
            self.log_validity_error(
                'TxFrequency.Min is negative, but RefFreqIndex is not populated.')
            return False
        return True

    def _check_tx_sequence(self):
        # type: () -> bool

        cond = True
        if self.TxPolarization == 'SEQUENCE' and self.TxSequence is None:
            self.log_validity_error(
                'TxPolarization is populated as "SEQUENCE", but TxSequence is not populated.')
            cond = False
        if self.TxSequence is not None:
            if self.TxPolarization != 'SEQUENCE':
                self.log_validity_error(
                    'TxSequence is populated, but TxPolarization is populated as {}'.format(self.TxPolarization))
                cond = False
            tx_pols = list(set([entry.TxPolarization for entry in self.TxSequence]))
            if len(tx_pols) == 1:
                self.log_validity_error(
                    'TxSequence is populated, but the only unique TxPolarization '
                    'among the entries is {}'.format(tx_pols[0]))
                cond = False
        return cond

    def _check_waveform_parameters(self):
        """
        Validate the waveform parameters for consistency.

        Returns
        -------
        bool
        """

        def validate_entry(index, waveform):
            # type: (int, WaveformParametersType) -> bool
            this_cond = True
            try:
                if abs(waveform.TxRFBandwidth/(waveform.TxPulseLength*waveform.TxFMRate) - 1) > 1e-3:
                    self.log_validity_error(
                        'The TxRFBandwidth, TxPulseLength, and TxFMRate parameters of Waveform '
                        'entry {} are inconsistent'.format(index+1))
                    this_cond = False
            except (AttributeError, ValueError, TypeError):
                pass

            if waveform.RcvDemodType == 'CHIRP' and waveform.RcvFMRate != 0:
                self.log_validity_error(
                    'RcvDemodType == "CHIRP" and RcvFMRate != 0 in Waveform entry {}'.format(index+1))
                this_cond = False
            if waveform.RcvDemodType == 'STRETCH' and \
                    waveform.RcvFMRate is not None and waveform.TxFMRate is not None and \
                    abs(waveform.RcvFMRate/waveform.TxFMRate - 1) > 1e-3:
                self.log_validity_warning(
                    'RcvDemodType = "STRETCH", RcvFMRate = {}, and TxFMRate = {} in '
                    'Waveform entry {}. The RcvFMRate and TxFMRate should very likely '
                    'be the same.'.format(waveform.RcvFMRate, waveform.TxFMRate, index+1))

            if self.RefFreqIndex is None:
                if waveform.TxFreqStart <= 0:
                    self.log_validity_error(
                        'TxFreqStart is negative in Waveform entry {}, but RefFreqIndex '
                        'is not populated.'.format(index+1))
                    this_cond = False
                if waveform.RcvFreqStart is not None and waveform.RcvFreqStart <= 0:
                    self.log_validity_error(
                        'RcvFreqStart is negative in Waveform entry {}, but RefFreqIndex '
                        'is not populated.'.format(index + 1))
                    this_cond = False
            if waveform.TxPulseLength is not None and waveform.RcvWindowLength is not None and \
                    waveform.TxPulseLength > waveform.RcvWindowLength:
                self.log_validity_error(
                    'TxPulseLength ({}) is longer than RcvWindowLength ({}) in '
                    'Waveform entry {}'.format(waveform.TxPulseLength, waveform.RcvWindowLength, index+1))
                this_cond = False
            if waveform.RcvIFBandwidth is not None and waveform.ADCSampleRate is not None and \
                    waveform.RcvIFBandwidth > waveform.ADCSampleRate:
                self.log_validity_error(
                    'RcvIFBandwidth ({}) is longer than ADCSampleRate ({}) in '
                    'Waveform entry {}'.format(waveform.RcvIFBandwidth, waveform.ADCSampleRate, index+1))
                this_cond = False
            if waveform.RcvDemodType is not None and waveform.RcvDemodType == 'CHIRP' \
                    and waveform.TxRFBandwidth is not None and waveform.ADCSampleRate is not None \
                    and (waveform.TxRFBandwidth > waveform.ADCSampleRate):
                self.log_validity_error(
                    'RcvDemodType is "CHIRP" and TxRFBandwidth ({}) is larger than ADCSampleRate ({}) '
                    'in Waveform entry {}'.format(waveform.TxRFBandwidth, waveform.ADCSampleRate, index+1))
                this_cond = False
            if waveform.RcvWindowLength is not None and waveform.TxPulseLength is not None and \
                    waveform.TxFMRate is not None and waveform.RcvFreqStart is not None and \
                    waveform.TxFreqStart is not None and waveform.TxRFBandwidth is not None:
                freq_tol = (waveform.RcvWindowLength - waveform.TxPulseLength)*waveform.TxFMRate
                if waveform.RcvFreqStart >= waveform.TxFreqStart + waveform.TxRFBandwidth + freq_tol:
                    self.log_validity_error(
                        'RcvFreqStart ({}), TxFreqStart ({}), and TxRfBandwidth ({}) parameters are inconsistent '
                        'in Waveform entry {}'.format(
                            waveform.RcvFreqStart, waveform.TxFreqStart, waveform.TxRFBandwidth, index + 1))
                    this_cond = False
                if waveform.RcvFreqStart <= waveform.TxFreqStart - freq_tol:
                    self.log_validity_error(
                        'RcvFreqStart ({}) and TxFreqStart ({}) parameters are inconsistent '
                        'in Waveform entry {}'.format(waveform.RcvFreqStart, waveform.TxFreqStart, index + 1))
                    this_cond = False

            return this_cond

        if self.Waveform is None or len(self.Waveform) < 1:
            return True

        cond = True
        # fetch min/max TxFreq observed
        wf_min_freq = None
        wf_max_freq = None
        for entry in self.Waveform:
            freq_start = entry.TxFreqStart
            freq_bw = entry.TxRFBandwidth
            if freq_start is not None:
                wf_min_freq = freq_start if wf_min_freq is None else \
                    min(wf_min_freq, freq_start)
                if entry.TxRFBandwidth is not None:
                    wf_max_freq = freq_start + freq_bw if wf_max_freq is None else \
                        max(wf_max_freq, freq_start + freq_bw)

        if wf_min_freq is not None and self.TxFrequency is not None and self.TxFrequency.Min is not None:
            if abs(self.TxFrequency.Min/wf_min_freq - 1) > 1e-3:
                self.log_validity_error(
                    'The stated TxFrequency.Min is {}, but the minimum populated in a '
                    'Waveform entry is {}'.format(self.TxFrequency.Min, wf_min_freq))
                cond = False
        if wf_max_freq is not None and self.TxFrequency is not None and self.TxFrequency.Max is not None:
            if abs(self.TxFrequency.Max/wf_max_freq - 1) > 1e-3:
                self.log_validity_error(
                    'The stated TxFrequency.Max is {}, but the maximum populated in a '
                    'Waveform entry is {}'.format(self.TxFrequency.Max, wf_max_freq))
                cond = False
        for t_index, t_waveform in enumerate(self.Waveform):
            cond &= validate_entry(t_index, t_waveform)
        return cond

    def _basic_validity_check(self):
        valid = super(RadarCollectionType, self)._basic_validity_check()
        valid &= self._check_frequency()
        valid &= self._check_tx_sequence()
        valid &= self._check_waveform_parameters()
        return valid

    def permits_version_1_1(self):
        """
        Does this value permit storage in SICD version 1.1?

        Returns
        -------
        bool
        """

        if self.RcvChannels is None:
            return True

        cond = True
        for entry in self.RcvChannels:
            cond &= entry.permits_version_1_1()
        return cond
Example #14
0
class ReferencePlaneType(Serializable):
    """
    The reference plane.
    """

    _fields = ('RefPt', 'XDir', 'YDir', 'SegmentList', 'Orientation')
    _required = ('RefPt', 'XDir', 'YDir')
    _collections_tags = {'SegmentList': {'array': True, 'child_tag': 'Segment'}}
    # other class variable
    _ORIENTATION_VALUES = ('UP', 'DOWN', 'LEFT', 'RIGHT', 'ARBITRARY')
    # descriptors
    RefPt = SerializableDescriptor(
        'RefPt', ReferencePointType, _required, strict=DEFAULT_STRICT,
        docstring='The reference point.')  # type: ReferencePointType
    XDir = SerializableDescriptor(
        'XDir', XDirectionType, _required, strict=DEFAULT_STRICT,
        docstring='The X direction collection plane parameters.')  # type: XDirectionType
    YDir = SerializableDescriptor(
        'YDir', YDirectionType, _required, strict=DEFAULT_STRICT,
        docstring='The Y direction collection plane parameters.')  # type: YDirectionType
    SegmentList = SerializableArrayDescriptor(
        'SegmentList', SegmentArrayElement, _collections_tags, _required, strict=DEFAULT_STRICT,
        docstring='The segment array.')  # type: Union[SerializableArray, List[SegmentArrayElement]]
    Orientation = StringEnumDescriptor(
        'Orientation', _ORIENTATION_VALUES, _required, strict=DEFAULT_STRICT,
        docstring='Describes the shadow intent of the display plane.')  # type: str

    def __init__(self, RefPt=None, XDir=None, YDir=None, SegmentList=None, Orientation=None, **kwargs):
        """

        Parameters
        ----------
        RefPt : ReferencePointType
        XDir : XDirectionType
        YDir : YDirectionType
        SegmentList : SerializableArray|List[SegmentArrayElement]
        Orientation : str
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.RefPt = RefPt
        self.XDir, self.YDir = XDir, YDir
        self.SegmentList = SegmentList
        self.Orientation = Orientation
        super(ReferencePlaneType, self).__init__(**kwargs)

    def get_ecf_corner_array(self):
        """
        Use the XDir and YDir definitions to return the corner points in ECF coordinates as a `4x3` array.

        Returns
        -------
        numpy.ndarray
            The corner points of the collection area, with order following the AreaType order convention.
        """

        ecf_ref = self.RefPt.ECF.get_array()
        x_shift = self.XDir.UVectECF.get_array() * self.XDir.LineSpacing
        y_shift = self.YDir.UVectECF.get_array() * self.YDir.SampleSpacing
        # order convention
        x_offset = numpy.array(
            [self.XDir.FirstLine, self.XDir.FirstLine, self.XDir.NumLines, self.XDir.NumLines])
        y_offset = numpy.array(
            [self.YDir.FirstSample, self.YDir.NumSamples, self.YDir.NumSamples, self.YDir.FirstSample])
        corners = numpy.zeros((4, 3), dtype=numpy.float64)
        for i in range(4):
            corners[i, :] = \
                ecf_ref + x_shift*(x_offset[i] - self.RefPt.Line) + y_shift*(y_offset[i] - self.RefPt.Sample)
        return corners
Example #15
0
class GeoDataType(Serializable):
    """
    Container specifying the image coverage area in geographic coordinates.

    .. Note: The SICD.GeoData class is an extension of this class. Implementation remain
        separate to allow the possibility of different functionality.
    """

    _fields = ('EarthModel', 'ImageCorners', 'ValidData')
    _required = ('EarthModel', 'ImageCorners', 'ValidData')
    _collections_tags = {
        'ValidData': {'array': True, 'child_tag': 'Vertex'},
        'ImageCorners': {'array': True, 'child_tag': 'ICP'}}
    _numeric_format = {'ImageCorners': '0.16G', 'ValidData': '0.16G'}
    # other class variables
    _EARTH_MODEL_VALUES = ('WGS_84', )
    # descriptors
    EarthModel = StringEnumDescriptor(
        'EarthModel', _EARTH_MODEL_VALUES, _required, strict=True, default_value='WGS_84',
        docstring='Identifies the earth model used for latitude, longitude and height parameters. '
                  'All height values are *Height Above The Ellipsoid '
                  '(HAE)*.'.format(_EARTH_MODEL_VALUES))  # type: str
    ImageCorners = SerializableCPArrayDescriptor(
        'ImageCorners', LatLonCornerStringType, _collections_tags, _required, strict=DEFAULT_STRICT,
        docstring='The geographic image corner points array. Image corners points projected to the '
                  'ground/surface level. Points may be projected to the same height as the SCP if ground/surface '
                  'height data is not available. The corner positions are approximate geographic locations and '
                  'not intended for analytical '
                  'use.')  # type: Union[SerializableCPArray, List[LatLonCornerStringType]]
    ValidData = SerializableArrayDescriptor(
        'ValidData', LatLonArrayElementType, _collections_tags, _required,
        strict=DEFAULT_STRICT, minimum_length=3,
        docstring='The full image array includes both valid data and some zero filled pixels. Simple convex '
                  'polygon enclosed the valid data (may include some zero filled pixels for simplification). '
                  'Vertices in clockwise order.')  # type: Union[SerializableArray, List[LatLonArrayElementType]]

    def __init__(self, EarthModel='WGS_84', ImageCorners=None, ValidData=None, GeoInfos=None, **kwargs):
        """

        Parameters
        ----------
        EarthModel : str
        ImageCorners : SerializableCPArray|List[LatLonCornerStringType]|numpy.ndarray|list|tuple
        ValidData : SerializableArray|List[LatLonArrayElementType]|numpy.ndarray|list|tuple
        GeoInfos : List[GeoInfoType]
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.EarthModel = EarthModel
        self.ImageCorners = ImageCorners
        self.ValidData = ValidData

        self._GeoInfos = []
        if GeoInfos is None:
            pass
        elif isinstance(GeoInfos, GeoInfoType):
            self.setGeoInfo(GeoInfos)
        elif isinstance(GeoInfos, (list, tuple)):
            for el in GeoInfos:
                self.setGeoInfo(el)
        else:
            raise ('GeoInfos got unexpected type {}'.format(type(GeoInfos)))
        super(GeoDataType, self).__init__(**kwargs)

    @property
    def GeoInfos(self):
        """
        List[GeoInfoType]: list of GeoInfos.
        """

        return self._GeoInfos

    def getGeoInfo(self, key):
        """
        Get the GeoInfo(s) with name attribute == `key`

        Parameters
        ----------
        key : str

        Returns
        -------
        List[GeoInfoType]
        """

        return [entry for entry in self._GeoInfos if entry.name == key]

    def setGeoInfo(self, value):
        """
        Add the given GeoInfo to the GeoInfos list.

        Parameters
        ----------
        value : GeoInfoType

        Returns
        -------
        None
        """

        if isinstance(value, ElementTree.Element):
            gi_key = self._child_xml_ns_key.get('GeoInfos', self._xml_ns_key)
            value = GeoInfoType.from_node(value, self._xml_ns, ns_key=gi_key)
        elif isinstance(value, dict):
            value = GeoInfoType.from_dict(value)

        if isinstance(value, GeoInfoType):
            self._GeoInfos.append(value)
        else:
            raise TypeError('Trying to set GeoInfo element with unexpected type {}'.format(type(value)))

    @classmethod
    def from_node(cls, node, xml_ns, ns_key=None, kwargs=None):
        if kwargs is None:
            kwargs = OrderedDict()
        gkey = cls._child_xml_ns_key.get('GeoInfos', ns_key)
        kwargs['GeoInfos'] = find_children(node, 'GeoInfo', xml_ns, gkey)
        return super(GeoDataType, cls).from_node(node, xml_ns, ns_key=ns_key, kwargs=kwargs)

    def to_node(self, doc, tag, ns_key=None, parent=None, check_validity=False, strict=DEFAULT_STRICT, exclude=()):
        node = super(GeoDataType, self).to_node(
            doc, tag, ns_key=ns_key, parent=parent, check_validity=check_validity, strict=strict, exclude=exclude)
        # slap on the GeoInfo children
        for entry in self._GeoInfos:
            entry.to_node(doc, 'GeoInfo', ns_key=ns_key, parent=node, strict=strict)
        return node

    def to_dict(self, check_validity=False, strict=DEFAULT_STRICT, exclude=()):
        out = super(GeoDataType, self).to_dict(
            check_validity=check_validity, strict=strict, exclude=exclude)
        # slap on the GeoInfo children
        if len(self.GeoInfos) > 0:
            out['GeoInfos'] = [entry.to_dict(check_validity=check_validity, strict=strict) for entry in self._GeoInfos]
        return out
Example #16
0
class GeoInfoType(Serializable):
    """
    A geographic feature.
    """

    _fields = ('name', 'Descriptions', 'Point', 'Line', 'Polygon')
    _required = ('name', )
    _set_as_attribute = ('name', )
    _choice = ({
        'required': False,
        'collection': ('Point', 'Line', 'Polygon')
    }, )
    _collections_tags = {
        'Descriptions': {
            'array': False,
            'child_tag': 'Desc'
        },
        'Line': {
            'array': True,
            'child_tag': 'Endpoint'
        },
        'Polygon': {
            'array': True,
            'child_tag': 'Vertex'
        },
    }
    # descriptors
    name = StringDescriptor('name',
                            _required,
                            strict=DEFAULT_STRICT,
                            docstring='The name.')  # type: str
    Descriptions = ParametersDescriptor(
        'Descriptions',
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Descriptions of the geographic feature.'
    )  # type: ParametersCollection
    Point = SerializableDescriptor(
        'Point',
        LatLonRestrictionType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='A geographic point with WGS-84 coordinates.'
    )  # type: LatLonRestrictionType
    Line = SerializableArrayDescriptor(
        'Line',
        LatLonArrayElementType,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        minimum_length=2,
        docstring='A geographic line (array) with WGS-84 coordinates.'
    )  # type: Union[SerializableArray, List[LatLonArrayElementType]]
    Polygon = SerializableArrayDescriptor(
        'Polygon',
        LatLonArrayElementType,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        minimum_length=3,
        docstring='A geographic polygon (array) with WGS-84 coordinates.'
    )  # type: Union[SerializableArray, List[LatLonArrayElementType]]

    def __init__(self,
                 name=None,
                 Descriptions=None,
                 Point=None,
                 Line=None,
                 Polygon=None,
                 GeoInfos=None,
                 **kwargs):
        """

        Parameters
        ----------
        name : str
        Descriptions : ParametersCollection|dict
        Point : LatLonRestrictionType|numpy.ndarray|list|tuple
        Line : SerializableArray|List[LatLonArrayElementType]|numpy.ndarray|list|tuple
        Polygon : SerializableArray|List[LatLonArrayElementType]|numpy.ndarray|list|tuple
        GeoInfos : Dict[GeoInfoTpe]
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.name = name
        self.Descriptions = Descriptions
        self.Point = Point
        self.Line = Line
        self.Polygon = Polygon

        self._GeoInfos = []
        if GeoInfos is None:
            pass
        elif isinstance(GeoInfos, GeoInfoType):
            self.addGeoInfo(GeoInfos)
        elif isinstance(GeoInfos, (list, tuple)):
            for el in GeoInfos:
                self.addGeoInfo(el)
        else:
            raise ValueError('GeoInfos got unexpected type {}'.format(
                type(GeoInfos)))
        super(GeoInfoType, self).__init__(**kwargs)

    @property
    def FeatureType(self):  # type: () -> Union[None, str]
        """
        str: READ ONLY attribute. Identifies the feature type among. This is determined by
        returning the (first) attribute among `Point`, `Line`, `Polygon` which is populated.
        `None` will be returned if none of them are populated.
        """

        for attribute in self._choice[0]['collection']:
            if getattr(self, attribute) is not None:
                return attribute
        return None

    @property
    def GeoInfos(self):
        """
        List[GeoInfoType]: list of GeoInfos.
        """

        return self._GeoInfos

    def getGeoInfo(self, key):
        """
        Get GeoInfo(s) with name attribute == `key`.

        Parameters
        ----------
        key : str

        Returns
        -------
        List[GeoInfoType]
        """

        return [entry for entry in self._GeoInfos if entry.name == key]

    def addGeoInfo(self, value):
        """
        Add the given GeoInfo to the GeoInfos list.

        Parameters
        ----------
        value : GeoInfoType

        Returns
        -------
        None
        """

        if isinstance(value, ElementTree.Element):
            gi_key = self._child_xml_ns_key.get('GeoInfos', self._xml_ns_key)
            value = GeoInfoType.from_node(value, self._xml_ns, ns_key=gi_key)
        elif isinstance(value, dict):
            value = GeoInfoType.from_dict(value)

        if isinstance(value, GeoInfoType):
            self._GeoInfos.append(value)
        else:
            raise TypeError(
                'Trying to set GeoInfo element with unexpected type {}'.format(
                    type(value)))

    def _validate_features(self):
        if self.Line is not None and self.Line.size < 2:
            self.log_validity_error(
                'GeoInfo has a Line feature with {} points defined.'.format(
                    self.Line.size))
            return False
        if self.Polygon is not None and self.Polygon.size < 3:
            self.log_validity_error(
                'GeoInfo has a Polygon feature with {} points defined.'.format(
                    self.Polygon.size))
            return False
        return True

    def _basic_validity_check(self):
        condition = super(GeoInfoType, self)._basic_validity_check()
        return condition & self._validate_features()

    @classmethod
    def from_node(cls, node, xml_ns, ns_key=None, kwargs=None):
        if kwargs is None:
            kwargs = OrderedDict()
        gi_key = cls._child_xml_ns_key.get('GeoInfos', ns_key)
        kwargs['GeoInfos'] = find_children(node, 'GeoInfo', xml_ns, gi_key)
        return super(GeoInfoType, cls).from_node(node,
                                                 xml_ns,
                                                 ns_key=ns_key,
                                                 kwargs=kwargs)

    def to_node(self,
                doc,
                tag,
                ns_key=None,
                parent=None,
                check_validity=False,
                strict=DEFAULT_STRICT,
                exclude=()):
        node = super(GeoInfoType, self).to_node(doc,
                                                tag,
                                                ns_key=ns_key,
                                                parent=parent,
                                                check_validity=check_validity,
                                                strict=strict,
                                                exclude=exclude)
        # slap on the GeoInfo children
        for entry in self._GeoInfos:
            entry.to_node(doc, tag, ns_key=ns_key, parent=node, strict=strict)
        return node

    def to_dict(self, check_validity=False, strict=DEFAULT_STRICT, exclude=()):
        out = super(GeoInfoType, self).to_dict(check_validity=check_validity,
                                               strict=strict,
                                               exclude=exclude)
        # slap on the GeoInfo children
        if len(self.GeoInfos) > 0:
            out['GeoInfos'] = [
                entry.to_dict(check_validity=check_validity, strict=strict)
                for entry in self._GeoInfos
            ]
        return out
Example #17
0
class GeoInfoType(Serializable):
    """
    A geographic feature.
    """

    _fields = ('name', 'Descriptions', 'Point', 'Line', 'Polygon', 'GeoInfo')
    _required = ('name', )
    _set_as_attribute = ('name', )
    _collections_tags = {
        'Descriptions': {'array': False, 'child_tag': 'Desc'},
        'Point': {'array': True, 'child_tag': 'Point'},
        'Line': {'array': True, 'child_tag': 'Line'},
        'Polygon': {'array': True, 'child_tag': 'Polygon'},
        'GeoInfo': {'array': False, 'child_tag': 'GeoInfo'}
    }
    # descriptors
    name = StringDescriptor(
        'name', _required, strict=DEFAULT_STRICT,
        docstring='The name.')  # type: str
    Descriptions = ParametersDescriptor(
        'Descriptions', _collections_tags, _required, strict=DEFAULT_STRICT,
        docstring='Descriptions of the geographic feature.')  # type: ParametersCollection
    Point = SerializableArrayDescriptor(
        'Point', LatLonRestrictionType, _collections_tags, _required, strict=DEFAULT_STRICT,
        docstring='Geographic points with WGS-84 coordinates.'
    )  # type: Union[SerializableArray, List[LatLonRestrictionType]]
    Line = SerializableArrayDescriptor(
        'Line', LineType, _collections_tags, _required, strict=DEFAULT_STRICT,
        docstring='Geographic lines (array) with WGS-84 coordinates.'
    )  # type: Union[SerializableArray, List[LineType]]
    Polygon = SerializableArrayDescriptor(
        'Polygon', PolygonType, _collections_tags, _required, strict=DEFAULT_STRICT,
        docstring='Geographic polygons (array) with WGS-84 coordinates.'
    )  # type: Union[SerializableArray, List[PolygonType]]

    def __init__(self, name=None, Descriptions=None, Point=None, Line=None,
                 Polygon=None, GeoInfo=None, **kwargs):
        """

        Parameters
        ----------
        name : str
        Descriptions : ParametersCollection|dict
        Point : SerializableArray|List[LatLonRestrictionType]|numpy.ndarray|list|tuple
        Line : SerializableArray|List[LineType]
        Polygon : SerializableArray|List[PolygonType]
        GeoInfo : Dict[GeoInfoTpe]
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.name = name
        self.Descriptions = Descriptions
        self.Point = Point
        self.Line = Line
        self.Polygon = Polygon

        self._GeoInfo = []
        if GeoInfo is None:
            pass
        elif isinstance(GeoInfo, GeoInfoType):
            self.addGeoInfo(GeoInfo)
        elif isinstance(GeoInfo, (list, tuple)):
            for el in GeoInfo:
                self.addGeoInfo(el)
        else:
            raise ValueError('GeoInfo got unexpected type {}'.format(type(GeoInfo)))
        super(GeoInfoType, self).__init__(**kwargs)

    @property
    def GeoInfo(self):
        """
        List[GeoInfoType]: list of GeoInfo objects.
        """

        return self._GeoInfo

    def getGeoInfo(self, key):
        """
        Get GeoInfo(s) with name attribute == `key`.

        Parameters
        ----------
        key : str

        Returns
        -------
        List[GeoInfoType]
        """

        return [entry for entry in self._GeoInfo if entry.name == key]

    def addGeoInfo(self, value):
        """
        Add the given GeoInfo to the GeoInfo list.

        Parameters
        ----------
        value : GeoInfoType

        Returns
        -------
        None
        """

        if isinstance(value, ElementTree.Element):
            gi_key = self._child_xml_ns_key.get('GeoInfo', self._xml_ns_key)
            value = GeoInfoType.from_node(value, self._xml_ns, ns_key=gi_key)
        elif isinstance(value, dict):
            value = GeoInfoType.from_dict(value)

        if isinstance(value, GeoInfoType):
            self._GeoInfo.append(value)
        else:
            raise TypeError('Trying to set GeoInfo element with unexpected type {}'.format(type(value)))

    @classmethod
    def from_node(cls, node, xml_ns, ns_key=None, kwargs=None):
        if kwargs is None:
            kwargs = OrderedDict()
        gi_key = cls._child_xml_ns_key.get('GeoInfo', ns_key)
        kwargs['GeoInfo'] = find_children(node, 'GeoInfo', xml_ns, gi_key)
        return super(GeoInfoType, cls).from_node(node, xml_ns, ns_key=ns_key, kwargs=kwargs)

    def to_node(self, doc, tag, ns_key=None, parent=None, check_validity=False, strict=DEFAULT_STRICT, exclude=()):
        node = super(GeoInfoType, self).to_node(
            doc, tag, ns_key=ns_key, parent=parent, check_validity=check_validity,
            strict=strict, exclude=exclude+('GeoInfo', ))
        # slap on the GeoInfo children
        if self._GeoInfo is not None and len(self._GeoInfo) > 0:
            for entry in self._GeoInfo:
                entry.to_node(doc, tag, ns_key=ns_key, parent=node, strict=strict)
        return node

    def to_dict(self, check_validity=False, strict=DEFAULT_STRICT, exclude=()):
        out = super(GeoInfoType, self).to_dict(
            check_validity=check_validity, strict=strict, exclude=exclude+('GeoInfo', ))
        # slap on the GeoInfo children
        if self.GeoInfo is not None and len(self.GeoInfo) > 0:
            out['GeoInfo'] = [entry.to_dict(check_validity=check_validity, strict=strict) for entry in self._GeoInfo]
        return out
Example #18
0
class GeoDataType(Serializable):
    """Container specifying the image coverage area in geographic coordinates."""
    _fields = ('EarthModel', 'SCP', 'ImageCorners', 'ValidData')
    _required = ('EarthModel', 'SCP', 'ImageCorners')
    _collections_tags = {
        'ValidData': {
            'array': True,
            'child_tag': 'Vertex'
        },
        'ImageCorners': {
            'array': True,
            'child_tag': 'ICP'
        },
    }
    # other class variables
    _EARTH_MODEL_VALUES = ('WGS_84', )
    # descriptors
    EarthModel = StringEnumDescriptor(
        'EarthModel',
        _EARTH_MODEL_VALUES,
        _required,
        strict=True,
        default_value='WGS_84',
        docstring='The Earth Model.')  # type: str
    SCP = SerializableDescriptor(
        'SCP',
        SCPType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'The Scene Center Point *(SCP)* in full (global) image. This is the '
        'precise location.')  # type: SCPType
    ImageCorners = SerializableCPArrayDescriptor(
        'ImageCorners',
        LatLonCornerStringType,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'The geographic image corner points array. Image corners points projected to the '
        'ground/surface level. Points may be projected to the same height as the SCP if ground/surface '
        'height data is not available. The corner positions are approximate geographic locations and '
        'not intended for analytical use.'
    )  # type: Union[SerializableCPArray, List[LatLonCornerStringType]]
    ValidData = SerializableArrayDescriptor(
        'ValidData',
        LatLonArrayElementType,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        minimum_length=3,
        docstring=
        'The full image array includes both valid data and some zero filled pixels.'
    )  # type: Union[SerializableArray, List[LatLonArrayElementType]]

    def __init__(self,
                 EarthModel='WGS_84',
                 SCP=None,
                 ImageCorners=None,
                 ValidData=None,
                 GeoInfos=None,
                 **kwargs):
        """

        Parameters
        ----------
        EarthModel : str
        SCP : SCPType
        ImageCorners : SerializableCPArray|List[LatLonCornerStringType]|numpy.ndarray|list|tuple
        ValidData : SerializableArray|List[LatLonArrayElementType]|numpy.ndarray|list|tuple
        GeoInfos : List[GeoInfoType]
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.EarthModel = EarthModel
        self.SCP = SCP
        self.ImageCorners = ImageCorners
        self.ValidData = ValidData

        self._GeoInfos = []
        if GeoInfos is None:
            pass
        elif isinstance(GeoInfos, GeoInfoType):
            self.setGeoInfo(GeoInfos)
        elif isinstance(GeoInfos, (list, tuple)):
            for el in GeoInfos:
                self.setGeoInfo(el)
        else:
            raise ValueError('GeoInfos got unexpected type {}'.format(
                type(GeoInfos)))
        super(GeoDataType, self).__init__(**kwargs)

    def derive(self):
        """
        Populates any potential derived data in GeoData. Is expected to be called by
        the `SICD` parent as part of a more extensive derived data effort.

        Returns
        -------
        None
        """

        pass

    @property
    def GeoInfos(self):
        """
        List[GeoInfoType]: list of GeoInfos.
        """

        return self._GeoInfos

    def getGeoInfo(self, key):
        """
        Get the GeoInfo(s) with name attribute == `key`

        Parameters
        ----------
        key : str

        Returns
        -------
        List[GeoInfoType]
        """

        return [entry for entry in self._GeoInfos if entry.name == key]

    def setGeoInfo(self, value):
        """
        Add the given GeoInfo to the GeoInfos list.

        Parameters
        ----------
        value : GeoInfoType

        Returns
        -------
        None
        """

        if isinstance(value, ElementTree.Element):
            gi_key = self._child_xml_ns_key.get('GeoInfos', self._xml_ns_key)
            value = GeoInfoType.from_node(value, self._xml_ns, ns_key=gi_key)
        elif isinstance(value, dict):
            value = GeoInfoType.from_dict(value)

        if isinstance(value, GeoInfoType):
            self._GeoInfos.append(value)
        else:
            raise TypeError(
                'Trying to set GeoInfo element with unexpected type {}'.format(
                    type(value)))

    @classmethod
    def from_node(cls, node, xml_ns, ns_key=None, kwargs=None):
        if kwargs is None:
            kwargs = OrderedDict()
        gi_key = cls._child_xml_ns_key.get('GeoInfos', ns_key)
        kwargs['GeoInfos'] = find_children(node, 'GeoInfo', xml_ns, gi_key)
        return super(GeoDataType, cls).from_node(node,
                                                 xml_ns,
                                                 ns_key=ns_key,
                                                 kwargs=kwargs)

    def to_node(self,
                doc,
                tag,
                ns_key=None,
                parent=None,
                check_validity=False,
                strict=DEFAULT_STRICT,
                exclude=()):
        node = super(GeoDataType, self).to_node(doc,
                                                tag,
                                                ns_key=ns_key,
                                                parent=parent,
                                                check_validity=check_validity,
                                                strict=strict,
                                                exclude=exclude)
        # slap on the GeoInfo children
        for entry in self._GeoInfos:
            entry.to_node(doc,
                          'GeoInfo',
                          ns_key=ns_key,
                          parent=node,
                          strict=strict)
        return node

    def to_dict(self, check_validity=False, strict=DEFAULT_STRICT, exclude=()):
        out = super(GeoDataType, self).to_dict(check_validity=check_validity,
                                               strict=strict,
                                               exclude=exclude)
        # slap on the GeoInfo children
        if len(self.GeoInfos) > 0:
            out['GeoInfos'] = [
                entry.to_dict(check_validity=check_validity, strict=strict)
                for entry in self._GeoInfos
            ]
        return out

    def _basic_validity_check(self):
        condition = super(GeoDataType, self)._basic_validity_check()
        return condition
Example #19
0
class TimelineType(Serializable):
    """
    The details for the imaging collection timeline.
    """

    _fields = ('CollectStart', 'CollectDuration', 'IPP')
    _required = (
        'CollectStart',
        'CollectDuration',
    )
    _collections_tags = {'IPP': {'array': True, 'child_tag': 'Set'}}
    _numeric_format = {
        'CollectDuration': FLOAT_FORMAT,
    }
    # descriptors
    CollectStart = DateTimeDescriptor(
        'CollectStart',
        _required,
        strict=DEFAULT_STRICT,
        numpy_datetime_units='us',
        docstring=
        'The collection start time. The default precision will be microseconds.'
    )  # type: numpy.datetime64
    CollectDuration = FloatDescriptor(
        'CollectDuration',
        _required,
        strict=DEFAULT_STRICT,
        docstring='The duration of the collection in seconds.')  # type: float
    IPP = SerializableArrayDescriptor(
        'IPP',
        IPPSetType,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        minimum_length=1,
        docstring="The Inter-Pulse Period (IPP) parameters array."
    )  # type: Union[SerializableArray, List[IPPSetType]]

    def __init__(self,
                 CollectStart=None,
                 CollectDuration=None,
                 IPP=None,
                 **kwargs):
        """

        Parameters
        ----------
        CollectStart : numpy.datetime64|datetime|date|str
        CollectDuration : float
        IPP : List[IPPSetType]
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.CollectStart = CollectStart
        self.CollectDuration = CollectDuration
        self.IPP = IPP
        super(TimelineType, self).__init__(**kwargs)

    @property
    def CollectEnd(self):
        """
        None|numpy.datetime64: The collection end time, inferred from `CollectEnd` and `CollectDuration`,
        provided that both are populated.
        """

        if self.CollectStart is None or self.CollectDuration is None:
            return None

        return self.CollectStart + numpy.timedelta64(
            int(self.CollectDuration * 1e6), 'us')

    def _check_ipp_consecutive(self):
        if self.IPP is None or len(self.IPP) < 2:
            return True
        cond = True
        for i in range(len(self.IPP) - 1):
            el1 = self.IPP[i]
            el2 = self.IPP[i + 1]
            if el1.IPPEnd + 1 != el2.IPPStart:
                self.log_validity_error(
                    'IPP entry {} IPPEnd ({}) is not consecutive with '
                    'entry {} IPPStart ({})'.format(i, el1.IPPEnd, i + 1,
                                                    el2.IPPStart))
                cond = False
            if el1.TEnd >= el2.TStart:
                self.log_validity_error(
                    'IPP entry {} TEnd ({}) is greater than entry {} TStart ({})'
                    .format(i, el1.TEnd, i + 1, el2.TStart))
        return cond

    def _check_ipp_times(self):
        if self.IPP is None:
            return True

        cond = True
        min_time = self.IPP[0].TStart
        max_time = self.IPP[0].TEnd
        for i in range(len(self.IPP)):
            entry = self.IPP[i]
            if entry.TStart < 0:
                self.log_validity_error(
                    'IPP entry {} has negative TStart ({})'.format(
                        i, entry.TStart))
                cond = False
            if entry.TEnd > self.CollectDuration + 1e-2:
                self.log_validity_error(
                    'IPP entry {} has TEnd ({}) appreciably larger than '
                    'CollectDuration ({})'.format(i, entry.TEnd,
                                                  self.CollectDuration))
                cond = False
            min_time = min(min_time, entry.TStart)
            max_time = max(max_time, entry.TEnd)
        if abs(max_time - min_time - self.CollectDuration) > 1e-2:
            self.log_validity_error(
                'time range in IPP entries ({}) not in keeping with populated '
                'CollectDuration ({})'.format(max_time - min_time,
                                              self.CollectDuration))
            cond = False
        return cond

    def _basic_validity_check(self):
        condition = super(TimelineType, self)._basic_validity_check()
        condition &= self._check_ipp_consecutive()
        condition &= self._check_ipp_times()
        return condition
Example #20
0
class SRPTyp(Serializable):
    """
    """

    _fields = ('SRPType', 'NumSRPs', 'FIXEDPT', 'PVTPOLY', 'PVVPOLY')
    _required = ('SRPType', 'NumSRPs')
    _collections_tags = {
        'FIXEDPT': {
            'array': True,
            'child_tag': 'SRPPT'
        },
        'PVTPOLY': {
            'array': True,
            'child_tag': 'SRPPVTPoly'
        },
        'PVVPOLY': {
            'array': True,
            'child_tag': 'SRPPVVPoly'
        }
    }
    _choice = ({
        'required': False,
        'collection': ('FIXEDPT', 'PVTPOLY', 'PVVPOLY')
    }, )
    # descriptors
    FIXEDPT = SerializableArrayDescriptor(
        'FIXEDPT',
        XYZType,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        array_extension=PlainArrayType,
        docstring='')  # type: Union[None, PlainArrayType, List[XYZType]]
    PVTPOLY = SerializableArrayDescriptor(
        'PVTPOLY',
        XYZPolyType,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        array_extension=PlainArrayType,
        docstring='')  # type: Union[None, PlainArrayType, List[XYZPolyType]]
    PVVPOLY = SerializableArrayDescriptor(
        'PVVPOLY',
        XYZPolyType,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        array_extension=PlainArrayType,
        docstring='')  # type: Union[None, PlainArrayType, List[XYZPolyType]]

    def __init__(self,
                 SRPType=None,
                 NumSRPs=None,
                 FIXEDPT=None,
                 PVTPOLY=None,
                 PVVPOLY=None,
                 **kwargs):
        """

        Parameters
        ----------
        SRPType : str
        NumSRPs : int
        FIXEDPT : None|PlainArrayType|List[XYZType]
        PVTPOLY : None|PlainArrayType|List[XYZPolyType]
        PVVPOLY : None|PlainArrayType|List[XYZPolyType]
        kwargs
        """

        self._SRPType = None
        self._NumSRPs = None
        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.FIXEDPT = FIXEDPT
        self.PVTPOLY = PVTPOLY
        self.PVVPOLY = PVVPOLY
        self.SRPType = SRPType
        self.NumSRPs = NumSRPs
        super(SRPTyp, self).__init__(**kwargs)

    @property
    def SRPType(self):
        """
        str: The type of SRP.
        """
        if self.FIXEDPT is not None:
            return 'FIXEDPT'
        elif self.PVTPOLY is not None:
            return 'PVTPOLY'
        elif self.PVVPOLY is not None:
            return 'PVVPOLY'
        else:
            return self._SRPType

    @SRPType.setter
    def SRPType(self, value):
        if self.FIXEDPT is not None or self.PVTPOLY is not None or self.PVVPOLY is not None:
            self._SRPType = None
        else:
            value = parse_str(value, 'SRPType', self).upper()
            if value in ('FIXEDPT', 'PVTPOLY', 'PVVPOLY', 'STEPPED'):
                self._SRPType = value
            else:
                logger.warning(
                    'Got {} for the SRPType field of class SRPTyp.\n\t'
                    'It is required to be one of {}.\n\t'
                    'Setting to None, which is required to be fixed.'.format(
                        value, ('FIXEDPT', 'PVTPOLY', 'PVVPOLY', 'STEPPED')))
                self._SRPType = None

    @property
    def NumSRPs(self):
        """
        None|int: The number of SRPs.
        """

        if self.FIXEDPT is not None:
            return self.FIXEDPT.size
        elif self.PVTPOLY is not None:
            return self.PVTPOLY.size
        elif self.PVVPOLY is not None:
            return self.PVVPOLY.size
        else:
            return self._NumSRPs

    @NumSRPs.setter
    def NumSRPs(self, value):
        if self.FIXEDPT is not None or self.PVTPOLY is not None or self.PVVPOLY is not None:
            self._NumSRPs = None
        else:
            self._NumSRPs = parse_int(value, 'NumSRPs', self)