From fa1c72e58baf3efdc8b4e8231c84b24f66c3d31c Mon Sep 17 00:00:00 2001 From: Daniel Stoops Date: Tue, 11 Feb 2025 17:13:46 +0200 Subject: [PATCH 1/4] Add reference check to all classes + update and add tests --- src/czml3/properties.py | 128 ++++++++- tests/test_properties.py | 514 +++++++++++++++++++++++++--------- tests/test_rectangle_image.py | 26 +- 3 files changed, 527 insertions(+), 141 deletions(-) diff --git a/src/czml3/properties.py b/src/czml3/properties.py index 4cb298f..1f9f277 100644 --- a/src/czml3/properties.py +++ b/src/czml3/properties.py @@ -298,6 +298,14 @@ class Color(BaseCZMLObject, Interpolatable, Deletable): ) """The color specified as a reference to another property. See `here `__ for it's definition.""" + @model_validator(mode="after") + def checks(self): + if self.delete: + return self + if sum(val is not None for val in (self.rgba, self.rgbaf, self.reference)) != 1: + raise TypeError("Only one of rgba, rgbaf or reference must be given") + return self + @field_validator("rgba") @classmethod def validate_rgba(cls, c): @@ -527,13 +535,23 @@ class EllipsoidRadii(BaseCZMLObject, Interpolatable, Deletable): See `here `__ for it's definition.""" - cartesian: Cartesian3Value | list[float] | TimeIntervalCollection = Field() + cartesian: Cartesian3Value | list[float] | TimeIntervalCollection = Field( + default=None + ) """The radii specified as a three-dimensional Cartesian value `[X, Y, Z]`, in world coordinates in meters. See `here `__ for it's definition.""" reference: None | ReferenceValue | str | TimeIntervalCollection = Field( default=None ) """The radii specified as a reference to another property. See `here `__ for it's definition.""" + @model_validator(mode="after") + def checks(self): + if self.delete: + return self + if sum(val is not None for val in (self.cartesian, self.reference)) != 1: + raise TypeError("Only one of cartesian or reference must be given") + return self + @field_validator("cartesian") @classmethod def validate_cartesian(cls, r): @@ -818,6 +836,14 @@ class ArcType(BaseCZMLObject, Deletable): ) """The arc type specified as a reference to another property. See `here `__ for it's definition.""" + @model_validator(mode="after") + def checks(self): + if self.delete: + return self + if sum(val is not None for val in (self.arcType, self.reference)) != 1: + raise TypeError("Only one of arcType or reference must be given") + return self + @field_validator("reference") @classmethod def validate_reference(cls, r): @@ -838,6 +864,14 @@ class ShadowMode(BaseCZMLObject, Deletable): ) """The shadow mode specified as a reference to another property. See `here `__ for it's definition.""" + @model_validator(mode="after") + def checks(self): + if self.delete: + return self + if sum(val is not None for val in (self.shadowMode, self.reference)) != 1: + raise TypeError("Only one of shadowMode or reference must be given") + return self + @field_validator("reference") @classmethod def validate_reference(cls, r): @@ -860,6 +894,17 @@ class ClassificationType(BaseCZMLObject, Deletable): ) """The classification type specified as a reference to another property. See `here `__ for it's definition.""" + @model_validator(mode="after") + def checks(self): + if self.delete: + return self + if ( + sum(val is not None for val in (self.classificationType, self.reference)) + != 1 + ): + raise TypeError("Only one of classificationType or reference must be given") + return self + @field_validator("reference") @classmethod def validate_reference(cls, r): @@ -882,6 +927,22 @@ class DistanceDisplayCondition(BaseCZMLObject, Interpolatable, Deletable): ) """The value specified as a reference to another property. See `here `__ for it's definition.""" + @model_validator(mode="after") + def checks(self): + if self.delete: + return self + if ( + sum( + val is not None + for val in (self.distanceDisplayCondition, self.reference) + ) + != 1 + ): + raise TypeError( + "Only one of distanceDisplayCondition or reference must be given" + ) + return self + @field_validator("reference") @classmethod def validate_reference(cls, r): @@ -1178,6 +1239,14 @@ class BoxDimensions(BaseCZMLObject, Interpolatable, Deletable): ) """The dimensions specified as a reference to another property. See `here `__ for it's definition.""" + @model_validator(mode="after") + def checks(self): + if self.delete: + return self + if sum(val is not None for val in (self.cartesian, self.reference)) != 1: + raise TypeError("Only one of cartesian or reference must be given") + return self + @field_validator("cartesian") @classmethod def validate_cartesian(cls, r): @@ -1226,8 +1295,13 @@ class RectangleCoordinates(BaseCZMLObject, Interpolatable, Deletable): def checks(self): if self.delete: return self - if sum(val is not None for val in (self.wsen, self.wsenDegrees)) != 1: - raise TypeError("One of wsen or wsenDegrees must be given") + if ( + sum( + val is not None for val in (self.wsen, self.wsenDegrees, self.reference) + ) + != 1 + ): + raise TypeError("Only one of wsen, wsenDegrees or reference must be given") return self @field_validator("reference") @@ -1252,6 +1326,14 @@ class EyeOffset(BaseCZMLObject, Deletable): ) """The eye offset specified as a reference to another property. See `here `__ for it's definition.""" + @model_validator(mode="after") + def checks(self): + if self.delete: + return self + if sum(val is not None for val in (self.cartesian, self.reference)) != 1: + raise TypeError("Only one of cartesian or reference must be given") + return self + @field_validator("cartesian") @classmethod def validate_cartesian(cls, r): @@ -1281,6 +1363,14 @@ class HeightReference(BaseCZMLObject, Deletable): ) """The height reference specified as a reference to another property. See `here `__ for it's definition.""" + @model_validator(mode="after") + def checks(self): + if self.delete: + return self + if sum(val is not None for val in (self.heightReference, self.reference)) != 1: + raise TypeError("Only one of heightReference or reference must be given") + return self + @field_validator("reference") @classmethod def validate_reference(cls, r): @@ -1303,6 +1393,14 @@ class ColorBlendMode(BaseCZMLObject, Deletable): ) """The color blend mode specified as a reference to another property. See `here `__ for it's definition.""" + @model_validator(mode="after") + def checks(self): + if self.delete: + return self + if sum(val is not None for val in (self.colorBlendMode, self.reference)) != 1: + raise TypeError("Only one of colorBlendMode or reference must be given") + return self + @field_validator("reference") @classmethod def validate_reference(cls, r): @@ -1323,6 +1421,14 @@ class CornerType(BaseCZMLObject, Deletable): ) """The corner style specified as a reference to another property. See `here `__ for it's definition.""" + @model_validator(mode="after") + def checks(self): + if self.delete: + return self + if sum(val is not None for val in (self.cornerType, self.reference)) != 1: + raise TypeError("Only one of cornerType or reference must be given") + return self + @field_validator("reference") @classmethod def validate_reference(cls, r): @@ -1478,6 +1584,14 @@ class NearFarScalar(BaseCZMLObject, Interpolatable, Deletable): ) """The value specified as a reference to another property. See `here `__ for it's definition.""" + @model_validator(mode="after") + def checks(self): + if self.delete: + return self + if sum(val is not None for val in (self.nearFarScalar, self.reference)) != 1: + raise TypeError("Only one of nearFarScalar or reference must be given") + return self + @field_validator("reference") @classmethod def validate_reference(cls, r): @@ -1550,6 +1664,14 @@ class Orientation(BaseCZMLObject, Interpolatable, Deletable): velocityReference: None | str | TimeIntervalCollection = Field(default=None) """The orientation specified as the normalized velocity vector of a position property. The reference must be to a position property. See `here `__ for it's definition.""" + @model_validator(mode="after") + def checks(self): + if self.delete: + return self + if sum(val is not None for val in (self.unitQuaternion, self.reference)) != 1: + raise TypeError("Only one of unitQuaternion or reference must be given") + return self + @field_validator("reference") @classmethod def validate_reference(cls, r): diff --git a/tests/test_properties.py b/tests/test_properties.py index cf2ca3a..8e44936 100644 --- a/tests/test_properties.py +++ b/tests/test_properties.py @@ -191,36 +191,6 @@ def test_polyline(): assert str(pol) == expected_result -def test_material_solid_color_with_reference(): - expected_result = """{ - "solidColor": { - "color": { - "rgba": [ - 200.0, - 100.0, - 30.0, - 255.0 - ], - "reference": "this#that" - } - } -}""" - pol_mat = PolylineMaterial( - solidColor=SolidColorMaterial( - color=Color(rgba=[200, 100, 30], reference="this#that") - ) - ) - assert str(pol_mat) == expected_result - pol_mat = PolylineMaterial( - solidColor=SolidColorMaterial( - color=Color( - rgba=[200, 100, 30], reference=ReferenceValue(value="this#that") - ) - ) - ) - assert str(pol_mat) == expected_result - - def test_material_solid_color(): expected_result = """{ "solidColor": { @@ -1157,18 +1127,9 @@ def test_check_classes_with_references_ViewFrom(): def test_check_classes_with_references_EllipsoidRadii(): assert ( - str(EllipsoidRadii(cartesian=[0, 0, 0], reference="this#that")) - == str( - EllipsoidRadii( - cartesian=[0, 0, 0], reference=ReferenceValue(value="this#that") - ) - ) + str(EllipsoidRadii(reference="this#that")) + == str(EllipsoidRadii(reference=ReferenceValue(value="this#that"))) == """{ - "cartesian": [ - 0.0, - 0.0, - 0.0 - ], "reference": "this#that" }""" ) @@ -1176,14 +1137,9 @@ def test_check_classes_with_references_EllipsoidRadii(): def test_check_classes_with_references_ArcType(): assert ( - str(ArcType(arcType=ArcTypes.GEODESIC, reference="this#that")) - == str( - ArcType( - arcType=ArcTypes.GEODESIC, reference=ReferenceValue(value="this#that") - ) - ) + str(ArcType(reference="this#that")) + == str(ArcType(reference=ReferenceValue(value="this#that"))) == """{ - "arcType": "GEODESIC", "reference": "this#that" }""" ) @@ -1200,19 +1156,9 @@ def test_check_classes_with_references_Position(): def test_check_classes_with_references_Orientation(): assert ( - str(Orientation(unitQuaternion=[0, 0, 0, 0], reference="this#that")) - == str( - Orientation( - unitQuaternion=[0, 0, 0, 0], reference=ReferenceValue(value="this#that") - ) - ) + str(Orientation(reference="this#that")) + == str(Orientation(reference=ReferenceValue(value="this#that"))) == """{ - "unitQuaternion": [ - 0.0, - 0.0, - 0.0, - 0.0 - ], "reference": "this#that" }""" ) @@ -1220,19 +1166,9 @@ def test_check_classes_with_references_Orientation(): def test_check_classes_with_references_NearFarScalar(): assert ( - str(NearFarScalar(nearFarScalar=[0, 0, 0, 0], reference="this#that")) - == str( - NearFarScalar( - nearFarScalar=[0, 0, 0, 0], reference=ReferenceValue(value="this#that") - ) - ) + str(NearFarScalar(reference="this#that")) + == str(NearFarScalar(reference=ReferenceValue(value="this#that"))) == """{ - "nearFarScalar": [ - 0.0, - 0.0, - 0.0, - 0.0 - ], "reference": "this#that" }""" ) @@ -1240,15 +1176,13 @@ def test_check_classes_with_references_NearFarScalar(): def test_check_classes_with_references_CornerType(): assert ( - str(CornerType(cornerType=CornerTypes.BEVELED, reference="this#that")) + str(CornerType(reference="this#that")) == str( CornerType( - cornerType=CornerTypes.BEVELED, reference=ReferenceValue(value="this#that"), ) ) == """{ - "cornerType": "BEVELED", "reference": "this#that" }""" ) @@ -1256,19 +1190,13 @@ def test_check_classes_with_references_CornerType(): def test_check_classes_with_references_ColorBlendMode(): assert ( - str( - ColorBlendMode( - colorBlendMode=ColorBlendModes.HIGHLIGHT, reference="this#that" - ) - ) + str(ColorBlendMode(reference="this#that")) == str( ColorBlendMode( - colorBlendMode=ColorBlendModes.HIGHLIGHT, reference=ReferenceValue(value="this#that"), ) ) == """{ - "colorBlendMode": "HIGHLIGHT", "reference": "this#that" }""" ) @@ -1276,19 +1204,13 @@ def test_check_classes_with_references_ColorBlendMode(): def test_check_classes_with_references_HeightReference(): assert ( - str( - HeightReference( - heightReference=HeightReferences.NONE, reference="this#that" - ) - ) + str(HeightReference(reference="this#that")) == str( HeightReference( - heightReference=HeightReferences.NONE, reference=ReferenceValue(value="this#that"), ) ) == """{ - "heightReference": "NONE", "reference": "this#that" }""" ) @@ -1296,16 +1218,9 @@ def test_check_classes_with_references_HeightReference(): def test_check_classes_with_references_EyeOffset(): assert ( - str(EyeOffset(cartesian=[0, 0, 0], reference="this#that")) - == str( - EyeOffset(cartesian=[0, 0, 0], reference=ReferenceValue(value="this#that")) - ) + str(EyeOffset(reference="this#that")) + == str(EyeOffset(reference=ReferenceValue(value="this#that"))) == """{ - "cartesian": [ - 0.0, - 0.0, - 0.0 - ], "reference": "this#that" }""" ) @@ -1313,33 +1228,22 @@ def test_check_classes_with_references_EyeOffset(): def test_check_classes_with_references_RectangleCoordinates(): assert ( - str(RectangleCoordinates(wsen=[0, 0], reference="this#that")) + str(RectangleCoordinates(reference="this#that")) == """{ - "wsen": [ - 0.0, - 0.0 - ], "reference": "this#that" }""" ) def test_check_classes_with_references_BoxDimensions(): - b1 = BoxDimensions( - cartesian=Cartesian3Value(values=[0, 0, 1]), reference="this#that" - ) - b2 = BoxDimensions(cartesian=[0, 0, 1], reference="this#that") - b3 = BoxDimensions(cartesian=[0, 0, 1], reference=ReferenceValue(value="this#that")) + b1 = BoxDimensions(reference="this#that") + b2 = BoxDimensions(reference="this#that") + b3 = BoxDimensions(reference=ReferenceValue(value="this#that")) assert ( str(b1) == str(b2) == str(b3) == """{ - "cartesian": [ - 0.0, - 0.0, - 1.0 - ], "reference": "this#that" }""" ) @@ -1349,26 +1253,15 @@ def test_check_classes_with_references_DistanceDisplayCondition(): assert ( str( DistanceDisplayCondition( - distanceDisplayCondition=DistanceDisplayConditionValue( - values=[0, 1, 2] - ), reference="this#that", ) ) == str( DistanceDisplayCondition( - distanceDisplayCondition=DistanceDisplayConditionValue( - values=[0, 1, 2] - ), reference=ReferenceValue(value="this#that"), ) ) == """{ - "distanceDisplayCondition": [ - 0.0, - 1.0, - 2.0 - ], "reference": "this#that" }""" ) @@ -1376,19 +1269,13 @@ def test_check_classes_with_references_DistanceDisplayCondition(): def test_check_classes_with_references_ClassificationType(): assert ( - str( - ClassificationType( - classificationType=ClassificationTypes.BOTH, reference="this#that" - ) - ) + str(ClassificationType(reference="this#that")) == str( ClassificationType( - classificationType=ClassificationTypes.BOTH, reference=ReferenceValue(value="this#that"), ) ) == """{ - "classificationType": "BOTH", "reference": "this#that" }""" ) @@ -1396,15 +1283,13 @@ def test_check_classes_with_references_ClassificationType(): def test_check_classes_with_references_ShadowMode(): assert ( - str(ShadowMode(shadowMode=ShadowModes.CAST_ONLY, reference="this#that")) + str(ShadowMode(reference="this#that")) == str( ShadowMode( - shadowMode=ShadowModes.CAST_ONLY, reference=ReferenceValue(value="this#that"), ) ) == """{ - "shadowMode": "CAST_ONLY", "reference": "this#that" }""" ) @@ -1904,3 +1789,364 @@ def test_forbid_extras(): # uri = Uri(uri="file://image.png", reference="this#that") # uri1 = Uri(uri="file://image.png", reference=ReferenceValue(value="this#that")) # assert str(uri) == str(uri1) == expected_result + + +def test_bad_color(): + with pytest.raises(TypeError): + Color(rgba=[0, 0, 0, 0], rgbaf=[0, 0, 0, 0]) + with pytest.raises(TypeError): + Color(rgba=[0, 0, 0, 0], reference="this#that") + with pytest.raises(TypeError): + Color(rgba=[0, 0, 0, 0], reference=ReferenceValue(value="this#that")) + with pytest.raises(TypeError): + Color(rgbaf=[0, 0, 0, 0], reference="this#that") + with pytest.raises(TypeError): + Color(rgbaf=[0, 0, 0, 0], reference=ReferenceValue(value="this#that")) + with pytest.raises(TypeError): + Color(rgbaf=[0, 0, 0, 0], rgba=[0, 0, 0, 0], reference="this#that") + with pytest.raises(TypeError): + Color( + rgbaf=[0, 0, 0, 0], + rgba=[0, 0, 0, 0], + reference=ReferenceValue(value="this#that"), + ) + + +def test_bad_EllipsoidRadii(): + with pytest.raises(TypeError): + EllipsoidRadii(cartesian=[0, 0, 0], reference="this#that") + with pytest.raises(TypeError): + EllipsoidRadii(cartesian=[0, 0, 0], reference=ReferenceValue(value="this#that")) + + +def test_bad_ArcType(): + with pytest.raises(TypeError): + ArcType(arcType=ArcTypes.GEODESIC, reference="this#that") + with pytest.raises(TypeError): + ArcType(arcType=ArcTypes.GEODESIC, reference=ReferenceValue(value="this#that")) + + +def test_bad_ShadowMode(): + with pytest.raises(TypeError): + ShadowMode(shadowMode=ShadowModes.DISABLED, reference="this#that") + with pytest.raises(TypeError): + ShadowMode( + shadowMode=ShadowModes.DISABLED, reference=ReferenceValue(value="this#that") + ) + + +def test_bad_HeightReference(): + with pytest.raises(TypeError): + HeightReference(heightReference=HeightReferences.NONE, reference="this#that") + with pytest.raises(TypeError): + HeightReference( + heightReference=HeightReferences.NONE, + reference=ReferenceValue(value="this#that"), + ) + + +def test_bad_ColorBlendMode(): + with pytest.raises(TypeError): + ColorBlendMode(colorBlendMode=ColorBlendModes.HIGHLIGHT, reference="this#that") + with pytest.raises(TypeError): + ColorBlendMode( + colorBlendMode=ColorBlendModes.HIGHLIGHT, + reference=ReferenceValue(value="this#that"), + ) + + +def test_bad_CornerType(): + with pytest.raises(TypeError): + CornerType(cornerType=CornerTypes.BEVELED, reference="this#that") + with pytest.raises(TypeError): + CornerType( + cornerType=CornerTypes.BEVELED, reference=ReferenceValue(value="this#that") + ) + + +def test_bad_DistanceDisplayCondition(): + with pytest.raises(TypeError): + DistanceDisplayCondition( + distanceDisplayCondition=DistanceDisplayConditionValue(values=[14, 81]), + reference="this#that", + ) + with pytest.raises(TypeError): + DistanceDisplayCondition( + distanceDisplayCondition=DistanceDisplayConditionValue(values=[14, 81]), + reference=ReferenceValue(value="this#that"), + ) + + +def test_bad_BoxDimensions(): + with pytest.raises(TypeError): + BoxDimensions(cartesian=[14, 81, 0], reference="this#that") + with pytest.raises(TypeError): + BoxDimensions( + cartesian=[14, 81, 0], reference=ReferenceValue(value="this#that") + ) + + +def test_bad_EyeOffset(): + with pytest.raises(TypeError): + EyeOffset(cartesian=[14, 81, 0], reference="this#that") + with pytest.raises(TypeError): + EyeOffset(cartesian=[14, 81, 0], reference=ReferenceValue(value="this#that")) + + +def test_bad_Orientation(): + with pytest.raises(TypeError): + Orientation(unitQuaternion=[14, 0, 81, 0], reference="this#that") + with pytest.raises(TypeError): + Orientation( + unitQuaternion=[14, 0, 81, 0], reference=ReferenceValue(value="this#that") + ) + + +def test_bad_Uri(): + with pytest.raises(TypeError): + Uri(uri="https://site.com/image.png", reference="this#that") + with pytest.raises(TypeError): + Uri( + uri="https://site.com/image.png", + reference=ReferenceValue(value="this#that"), + ) + + +def test_bad_NearFarScalar(): + with pytest.raises(TypeError): + NearFarScalar( + nearFarScalar=NearFarScalarValue(values=[350, 2.0, 15000000, 0.5]), + reference="this#that", + ) + with pytest.raises(TypeError): + NearFarScalar( + nearFarScalar=NearFarScalarValue(values=[350, 2.0, 15000000, 0.5]), + reference=ReferenceValue(value="this#that"), + ) + + +def test_bad_RectangleCoordinates(): + with pytest.raises(TypeError): + RectangleCoordinates(wsen=[81, 0], wsenDegrees=[81, 0], reference="this#that") + with pytest.raises(TypeError): + RectangleCoordinates( + wsen=[81, 0], + wsenDegrees=[81, 0], + reference=ReferenceValue(value="this#that"), + ) + with pytest.raises(TypeError): + RectangleCoordinates(wsen=[81, 0], wsenDegrees=[81, 0]) + with pytest.raises(TypeError): + RectangleCoordinates(wsenDegrees=[81, 0], reference="this#that") + with pytest.raises(TypeError): + RectangleCoordinates( + wsenDegrees=[81, 0], reference=ReferenceValue(value="this#that") + ) + with pytest.raises(TypeError): + RectangleCoordinates(wsen=[81, 0], reference="this#that") + with pytest.raises(TypeError): + RectangleCoordinates(wsen=[81, 0], reference=ReferenceValue(value="this#that")) + + +def test_bad_ClassificationType(): + with pytest.raises(TypeError): + ClassificationType( + classificationType=ClassificationTypes.BOTH, reference="this#that" + ) + with pytest.raises(TypeError): + ClassificationType( + classificationType=ClassificationTypes.BOTH, + reference=ReferenceValue(value="this#that"), + ) + + +def test_ReferenceValue_is_reference(): + assert str(ClassificationType(reference="this#that")) == str( + ClassificationType(reference=ReferenceValue(value="this#that")) + ) + assert str(NearFarScalar(reference="this#that")) == str( + NearFarScalar(reference=ReferenceValue(value="this#that")) + ) + assert str(DistanceDisplayCondition(reference="this#that")) == str( + DistanceDisplayCondition(reference=ReferenceValue(value="this#that")) + ) + assert str(Color(reference="this#that")) == str( + Color(reference=ReferenceValue(value="this#that")) + ) + + assert str(Color(reference="this#that")) == str( + Color(reference=ReferenceValue(value="this#that")) + ) + + assert str(Color(reference="this#that")) == str( + Color(reference=ReferenceValue(value="this#that")) + ) + + assert str(EllipsoidRadii(reference="this#that")) == str( + EllipsoidRadii(reference=ReferenceValue(value="this#that")) + ) + + assert str(ArcType(reference="this#that")) == str( + ArcType(reference=ReferenceValue(value="this#that")) + ) + + assert str(ShadowMode(reference="this#that")) == str( + ShadowMode(reference=ReferenceValue(value="this#that")) + ) + + assert str(HeightReference(reference="this#that")) == str( + HeightReference(reference=ReferenceValue(value="this#that")) + ) + + assert str(ColorBlendMode(reference="this#that")) == str( + ColorBlendMode(reference=ReferenceValue(value="this#that")) + ) + + assert str(CornerType(reference="this#that")) == str( + CornerType(reference=ReferenceValue(value="this#that")) + ) + + assert str(BoxDimensions(reference="this#that")) == str( + BoxDimensions(reference=ReferenceValue(value="this#that")) + ) + + assert str(EyeOffset(reference="this#that")) == str( + EyeOffset(reference=ReferenceValue(value="this#that")) + ) + + assert str(Orientation(reference="this#that")) == str( + Orientation(reference=ReferenceValue(value="this#that")) + ) + + assert str(Uri(reference="this#that")) == str( + Uri(reference=ReferenceValue(value="this#that")) + ) + + assert str(RectangleCoordinates(reference="this#that")) == str( + RectangleCoordinates(reference=ReferenceValue(value="this#that")) + ) + + assert str(RectangleCoordinates(reference="this#that")) == str( + RectangleCoordinates(reference=ReferenceValue(value="this#that")) + ) + + assert str(RectangleCoordinates(reference="this#that")) == str( + RectangleCoordinates(reference=ReferenceValue(value="this#that")) + ) + + +def test_EllipsoidRadii_delete(): + expected_result = """{ + "delete": true +}""" + p = EllipsoidRadii(delete=True, cartesian=[0, 0, 0]) + assert p.delete + assert str(p) == expected_result + + +def test_ArcType_delete(): + expected_result = """{ + "delete": true +}""" + p = ArcType(delete=True, reference="this#that") + assert p.delete + assert str(p) == expected_result + + +def test_ShadowMode_delete(): + expected_result = """{ + "delete": true +}""" + p = ShadowMode(delete=True, reference="this#that") + assert p.delete + assert str(p) == expected_result + + +def test_ClassificationType_delete(): + expected_result = """{ + "delete": true +}""" + p = ClassificationType(delete=True, reference="this#that") + assert p.delete + assert str(p) == expected_result + + +def test_DistanceDisplayCondition_delete(): + expected_result = """{ + "delete": true +}""" + p = DistanceDisplayCondition(delete=True, reference="this#that") + assert p.delete + assert str(p) == expected_result + + +def test_BoxDimensions_delete(): + expected_result = """{ + "delete": true +}""" + p = BoxDimensions(delete=True, reference="this#that") + assert p.delete + assert str(p) == expected_result + + +def test_EyeOffset_delete(): + expected_result = """{ + "delete": true +}""" + p = EyeOffset(delete=True, reference="this#that") + assert p.delete + assert str(p) == expected_result + + +def test_HeightReference_delete(): + expected_result = """{ + "delete": true +}""" + p = HeightReference(delete=True, reference="this#that") + assert p.delete + assert str(p) == expected_result + + +def test_ColorBlendMode_delete(): + expected_result = """{ + "delete": true +}""" + p = ColorBlendMode(delete=True, reference="this#that") + assert p.delete + assert str(p) == expected_result + + +def test_CornerType_delete(): + expected_result = """{ + "delete": true +}""" + p = CornerType(delete=True, reference="this#that") + assert p.delete + assert str(p) == expected_result + + +def test_NearFarScalar_delete(): + expected_result = """{ + "delete": true +}""" + p = NearFarScalar(delete=True, reference="this#that") + assert p.delete + assert str(p) == expected_result + + +def test_Orientation_delete(): + expected_result = """{ + "delete": true +}""" + p = Orientation(delete=True, reference="this#that") + assert p.delete + assert str(p) == expected_result + + +def test_Uri_delete(): + expected_result = """{ + "delete": true +}""" + p = Uri(delete=True, reference="this#that") + assert p.delete + assert str(p) == expected_result diff --git a/tests/test_rectangle_image.py b/tests/test_rectangle_image.py index 0a86915..91b4b84 100644 --- a/tests/test_rectangle_image.py +++ b/tests/test_rectangle_image.py @@ -24,11 +24,29 @@ def image(): return base64_data.decode() -def test_rectangle_coordinates_invalid_if_nothing_given(): - with pytest.raises(TypeError) as excinfo: +def test_bad_rectangle_coordinates(): + with pytest.raises( + TypeError, match="Only one of wsen, wsenDegrees or reference must be given" + ): RectangleCoordinates() - - assert "One of wsen or wsenDegrees must be given" in excinfo.exconly() + with pytest.raises( + TypeError, match="Only one of wsen, wsenDegrees or reference must be given" + ): + RectangleCoordinates(wsen=[0, 0, 0], wsenDegrees=[0, 0, 0]) + with pytest.raises( + TypeError, match="Only one of wsen, wsenDegrees or reference must be given" + ): + RectangleCoordinates(wsen=[0, 0, 0], reference="this#that") + with pytest.raises( + TypeError, match="Only one of wsen, wsenDegrees or reference must be given" + ): + RectangleCoordinates(wsenDegrees=[0, 0, 0], reference="this#that") + with pytest.raises( + TypeError, match="Only one of wsen, wsenDegrees or reference must be given" + ): + RectangleCoordinates( + wsenDegrees=[0, 0, 0], wsen=[0, 0, 0], reference="this#that" + ) def test_packet_rectangles(image): From fafefa67fb5be0fe67801c00f33f7023a217a6d9 Mon Sep 17 00:00:00 2001 From: Daniel Stoops Date: Tue, 11 Feb 2025 17:17:43 +0200 Subject: [PATCH 2/4] Implement Uri fully --- src/czml3/properties.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/czml3/properties.py b/src/czml3/properties.py index 1f9f277..dae28ec 100644 --- a/src/czml3/properties.py +++ b/src/czml3/properties.py @@ -1741,10 +1741,18 @@ class Uri(BaseCZMLObject, Deletable): uri: None | str | TimeIntervalCollection = Field(default=None) """The URI value.""" - # reference: None | ReferenceValue | str | TimeIntervalCollection = Field( - # default=None - # ) - # """The color specified as a reference to another property. See `here `__ for it's definition.""" + reference: None | ReferenceValue | str | TimeIntervalCollection = Field( + default=None + ) + """The color specified as a reference to another property. See `here `__ for it's definition.""" + + @model_validator(mode="after") + def checks(self): + if self.delete: + return self + if sum(val is not None for val in (self.uri, self.reference)) != 1: + raise TypeError("Only one of uri or reference must be given") + return self @field_validator("uri") @classmethod @@ -1763,13 +1771,15 @@ def _check_uri(cls, url: str): # "uri must be a URL, a data URI or base64 encoded string." # ) - # @field_validator("reference") - # @classmethod - # def validate_reference(cls, r): - # if isinstance(r, str): - # return ReferenceValue(value=r) - # return r + @field_validator("reference") + @classmethod + def validate_reference(cls, r): + if isinstance(r, str): + return ReferenceValue(value=r) + return r @model_serializer def custom_serializer(self) -> None | str | TimeIntervalCollection: - return self.uri + if self.delete: + return {"delete": True} + return self.uri if self.uri is not None else self.reference From 6b656c88b127ad89be41c4b4cc1b05927a69a4b7 Mon Sep 17 00:00:00 2001 From: Daniel Stoops Date: Tue, 11 Feb 2025 17:17:52 +0200 Subject: [PATCH 3/4] Add test_NearFarScalar_list() --- tests/test_properties.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_properties.py b/tests/test_properties.py index 8e44936..67e7aff 100644 --- a/tests/test_properties.py +++ b/tests/test_properties.py @@ -138,6 +138,30 @@ def test_point(): assert str(pnt) == expected_result +def test_NearFarScalar_list(): + expected_result = """{ + "show": true, + "pixelSize": 10.0, + "scaleByDistance": { + "nearFarScalar": [ + 150.0, + 2.0, + 15000000.0, + 0.5 + ] + }, + "disableDepthTestDistance": 1.2 +}""" + + pnt = Point( + show=True, + pixelSize=10, + scaleByDistance=NearFarScalar(nearFarScalar=[150, 2.0, 15000000, 0.5]), + disableDepthTestDistance=1.2, + ) + assert str(pnt) == expected_result + + def test_arc_type(): expected_result = """{ "arcType": "NONE" From 05f9488aab59bcdd1bd1abfbff31c8eebaf91953 Mon Sep 17 00:00:00 2001 From: Daniel Stoops Date: Tue, 11 Feb 2025 17:23:33 +0200 Subject: [PATCH 4/4] Fix typing --- src/czml3/properties.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/czml3/properties.py b/src/czml3/properties.py index dae28ec..f554467 100644 --- a/src/czml3/properties.py +++ b/src/czml3/properties.py @@ -535,7 +535,7 @@ class EllipsoidRadii(BaseCZMLObject, Interpolatable, Deletable): See `here `__ for it's definition.""" - cartesian: Cartesian3Value | list[float] | TimeIntervalCollection = Field( + cartesian: Cartesian3Value | list[float] | TimeIntervalCollection | None = Field( default=None ) """The radii specified as a three-dimensional Cartesian value `[X, Y, Z]`, in world coordinates in meters. See `here `__ for it's definition.""" @@ -1779,7 +1779,9 @@ def validate_reference(cls, r): return r @model_serializer - def custom_serializer(self) -> None | str | TimeIntervalCollection: + def custom_serializer( + self, + ) -> None | str | dict[str, bool] | TimeIntervalCollection: if self.delete: return {"delete": True} - return self.uri if self.uri is not None else self.reference + return self.uri if self.uri is not None else str(self.reference)