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)
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)
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)
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)
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)
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)
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)
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)
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
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
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))
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
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
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
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
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
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
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
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
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)