From a3f3a00432529332e8515cfb2f06f73d0eb1abe0 Mon Sep 17 00:00:00 2001 From: Daniel Stoops Date: Fri, 20 Dec 2024 13:35:09 +0200 Subject: [PATCH 1/7] Expand preamble checking in Document() --- src/czml3/core.py | 34 ++- src/czml3/widget.py | 4 +- tests/test_document.py | 15 +- tests/test_packet.py | 417 +++++++++++++++++++++++++++++++++- tests/test_rectangle_image.py | 2 +- 5 files changed, 461 insertions(+), 11 deletions(-) diff --git a/src/czml3/core.py b/src/czml3/core.py index 290ae0e..3fb3b80 100644 --- a/src/czml3/core.py +++ b/src/czml3/core.py @@ -108,10 +108,40 @@ class Document(BaseCZMLObject): @field_validator("packets") @classmethod def validate_packets(cls, packets): - if packets[0].version is None: + if packets[0].version is None or packets[0].name is None: raise ValueError( - "The first packet must be a preamble and include `version`" + "The first packet must be a preamble and include 'version' and 'name' properties." ) + if packets[0].id != "document": + raise ValueError("The first packet must have an ID of 'document'.") + for p in ( + "delete", + "parent", + "availability", + "properties", + "position", + "orientation", + "viewFrom", + "billboard", + "box", + "corridor", + "cylinder", + "ellipse", + "ellipsoid", + "label", + "model", + "path", + "point", + "polygon", + "polyline", + "rectangle", + "tileset", + "wall", + ): + if getattr(packets[0], p) is not None: + raise ValueError( + f"The first packet must not include the '{p}' property" + ) return packets @model_serializer diff --git a/src/czml3/widget.py b/src/czml3/widget.py index 0097db8..7e14190 100644 --- a/src/czml3/widget.py +++ b/src/czml3/widget.py @@ -76,7 +76,9 @@ class CZMLWidget(BaseModel): document: Document = Field( - default=Document(packets=[Packet(id="document", version=CZML_VERSION)]) + default=Document( + packets=[Packet(id="document", name="name", version=CZML_VERSION)] + ) ) cesium_version: str = Field(default="1.88") ion_token: str = Field(default="") diff --git a/tests/test_document.py b/tests/test_document.py index dee7f48..84815fd 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -2,7 +2,7 @@ def test_document_has_expected_packets(): - preamble = Packet(version=CZML_VERSION) + preamble = Packet(version=CZML_VERSION, name="document", id="document") packet0 = Packet(id="id_00") packet1 = Packet(id="id_01") @@ -12,10 +12,11 @@ def test_document_has_expected_packets(): def test_doc_repr(): - packet = Packet(id="id_00", version=CZML_VERSION) + packet = Packet(id="document", name="name", version=CZML_VERSION) expected_result = """[ { - "id": "id_00", + "id": "document", + "name": "name", "version": "CZML_VERSION" } ]""".replace("CZML_VERSION", CZML_VERSION) @@ -26,9 +27,11 @@ def test_doc_repr(): def test_doc_dumps(): - packet = Packet(id="id_00", version=CZML_VERSION) - expected_result = """[{"id":"id_00","version":"CZML_VERSION"}]""".replace( - "CZML_VERSION", CZML_VERSION + packet = Packet(id="document", version=CZML_VERSION, name="name") + expected_result = ( + """[{"id":"document","name":"name","version":"CZML_VERSION"}]""".replace( + "CZML_VERSION", CZML_VERSION + ) ) document = Document(packets=[packet]) diff --git a/tests/test_packet.py b/tests/test_packet.py index 17c1199..d8149cc 100644 --- a/tests/test_packet.py +++ b/tests/test_packet.py @@ -4,15 +4,23 @@ import pytest -from czml3 import CZML_VERSION, Packet +from czml3 import CZML_VERSION, Document, Packet from czml3.enums import InterpolationAlgorithms, ReferenceFrames from czml3.properties import ( Billboard, + Box, + BoxDimensions, Color, + Corridor, + Cylinder, + Ellipse, Ellipsoid, EllipsoidRadii, Label, Material, + Model, + Orientation, + Path, Point, Polygon, Polyline, @@ -27,7 +35,12 @@ PolylineOutlineMaterial, Position, PositionList, + Rectangle, + RectangleCoordinates, SolidColorMaterial, + Tileset, + ViewFrom, + Wall, ) from czml3.types import ( Cartesian3Value, @@ -642,3 +655,405 @@ def test_different_availabilities(): ) assert p1 != p2 assert str(p1) != str(p2) + + +def test_preamble_no_version(): + with pytest.raises( + ValueError, + match="The first packet must be a preamble and include 'version' and 'name' properties.", + ): + Document(packets=[Packet(name="Test Packet", id="document")]) + + +def test_preamble_no_name(): + with pytest.raises( + ValueError, + match="The first packet must be a preamble and include 'version' and 'name' properties.", + ): + Document(packets=[Packet(version="Test Packet", id="document")]) + + +def test_preamble_no_id(): + with pytest.raises( + ValueError, match="The first packet must have an ID of 'document'." + ): + Document(packets=[Packet(version="Test Packet", name="Test Packet")]) + + +def test_preamble_bad_id(): + with pytest.raises( + ValueError, match="The first packet must have an ID of 'document'." + ): + Document(packets=[Packet(version="Test Packet", name="Test Packet", id="name")]) + + +def test_preamble_wall_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'wall' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + wall=Wall(positions=PositionList(cartesian=[0, 0, 0])), + ) + ] + ) + + +def test_preamble_tileset_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'tileset' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + tileset=Tileset(uri="file://tileset.json"), + ) + ] + ) + + +def test_preamble_rectangle_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'rectangle' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + rectangle=Rectangle( + coordinates=RectangleCoordinates(wsen=[0, 0, 0]) + ), + ) + ] + ) + + +def test_preamble_polyline_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'polyline' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + polyline=Polyline( + positions=PositionList(cartographicDegrees=[0, 0, 0]) + ), + ) + ] + ) + + +def test_preamble_polygon_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'polygon' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + polygon=Polygon( + positions=PositionList(cartographicDegrees=[0, 0, 0]) + ), + ) + ] + ) + + +def test_preamble_point_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'point' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + point=Point(), + ) + ] + ) + + +def test_preamble_availability_supplied(): + with pytest.raises( + ValueError, + match="The first packet must not include the 'availability' property", + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + availability=TimeIntervalCollection( + values=[ + TimeInterval( + start=dt.datetime( + 2019, 3, 20, 12, tzinfo=dt.timezone.utc + ), + end=dt.datetime( + 2019, 4, 20, 12, tzinfo=dt.timezone.utc + ), + ) + ] + ), + ) + ] + ) + + +def test_preamble_model_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'model' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + model=Model(gltf="file://model.glb"), + ) + ] + ) + + +def test_preamble_path_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'path' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + path=Path(), + ) + ] + ) + + +def test_preamble_label_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'label' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + label=Label(), + ) + ] + ) + + +def test_preamble_ellipse_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'ellipse' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + ellipse=Ellipse(semiMajorAxis=1000.0, semiMinorAxis=1000.0), + ) + ] + ) + + +def test_preamble_ellipsoid_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'ellipsoid' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + ellipsoid=Ellipsoid(radii=EllipsoidRadii(cartesian=[0, 0, 0])), + ) + ] + ) + + +def test_preamble_cylinder_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'cylinder' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + cylinder=Cylinder(length=1, topRadius=1, bottomRadius=1), + ) + ] + ) + + +def test_preamble_corridor_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'corridor' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + corridor=Corridor( + positions=PositionList(cartographicDegrees=[0, 0, 0]), width=2 + ), + ) + ] + ) + + +def test_preamble_box_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'box' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + box=Box(dimensions=BoxDimensions(cartesian=[0, 0, 0])), + ) + ] + ) + + +def test_preamble_billboard_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'billboard' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + billboard=Billboard(image="file://image.png"), + ) + ] + ) + + +def test_preamble_orientation_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'orientation' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + orientation=Orientation(unitQuaternion=[0, 0, 0, 1]), + ) + ] + ) + + +def test_preamble_viewFrom_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'viewFrom' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + viewFrom=ViewFrom(cartesian=[0, 0, 0]), + ) + ] + ) + + +def test_preamble_delete_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'delete' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + delete=False, + ) + ] + ) + + +def test_preamble_parent_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'parent' property" + ): + Document( + packets=[ + Packet( + version="Test Packet", + name="Test Packet", + id="document", + parent="parent", + ) + ] + ) + + +def test_preamble_position_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'position' property" + ): + Document( + packets=[ + Packet( + id="document", + version="1.0", + name="Test Document", + position={"cartesian": [0, 0, 0]}, + ) + ] + ) + + +def test_preamble_properties_supplied(): + with pytest.raises( + ValueError, match="The first packet must not include the 'properties' property" + ): + Document( + packets=[ + Packet( + id="document", + version="1.0", + name="Test Document", + properties={"non_allowed_property": "value"}, + ) + ] + ) diff --git a/tests/test_rectangle_image.py b/tests/test_rectangle_image.py index 2b7c048..0a86915 100644 --- a/tests/test_rectangle_image.py +++ b/tests/test_rectangle_image.py @@ -94,7 +94,7 @@ def test_make_czml_png_rectangle_file(image): str( Document( packets=[ - Packet(name="document", version=CZML_VERSION), + Packet(id="document", name="document", version=CZML_VERSION), rectangle_packet, ] ) From 763775b2b41e41505ea128d7be6382fcf4e37f86 Mon Sep 17 00:00:00 2001 From: Daniel Stoops Date: Fri, 20 Dec 2024 13:35:52 +0200 Subject: [PATCH 2/7] Box() requires dimensions --- src/czml3/properties.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/czml3/properties.py b/src/czml3/properties.py index 0c27a74..e0416ac 100644 --- a/src/czml3/properties.py +++ b/src/czml3/properties.py @@ -1104,7 +1104,7 @@ class Box(BaseCZMLObject): show: None | bool | TimeIntervalCollection = Field(default=None) """Whether or not the box is shown.""" - dimensions: None | BoxDimensions | TimeIntervalCollection = Field(default=None) + dimensions: BoxDimensions | TimeIntervalCollection = Field() """The dimensions of the box. See `here `__ for it's definition.""" heightReference: None | HeightReference | TimeIntervalCollection = Field( default=None From 68225100f9de02a7134a3063cc4da4175d9095b0 Mon Sep 17 00:00:00 2001 From: Daniel Stoops Date: Fri, 20 Dec 2024 13:36:11 +0200 Subject: [PATCH 3/7] Rectangle() requires coordinates --- src/czml3/properties.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/czml3/properties.py b/src/czml3/properties.py index e0416ac..5156555 100644 --- a/src/czml3/properties.py +++ b/src/czml3/properties.py @@ -1164,9 +1164,7 @@ class Rectangle(BaseCZMLObject, Interpolatable, Deletable): show: None | bool | TimeIntervalCollection = Field(default=None) """Whether or not the rectangle is shown.""" - coordinates: None | RectangleCoordinates | TimeIntervalCollection = Field( - default=None - ) + coordinates: None | RectangleCoordinates | TimeIntervalCollection = Field() """The coordinates of the rectangle. See `here `__ for it's definition.""" fill: None | bool | TimeIntervalCollection = Field(default=None) """Whether or not the rectangle is filled.""" From 4905820248997a58d2c2267affbaa1a0a127f630 Mon Sep 17 00:00:00 2001 From: Daniel Stoops Date: Fri, 20 Dec 2024 13:38:23 +0200 Subject: [PATCH 4/7] Reinstate LICENSE file (required for conda) --- LICENSE.txt | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..2ec51d7 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,27 @@ +BSD-3-Clause license +Copyright (c) 2015-2022, conda-forge contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. From 6f590c729e3494e684c7a726796d0f996ff7b757 Mon Sep 17 00:00:00 2001 From: Daniel Stoops Date: Fri, 20 Dec 2024 13:39:21 +0200 Subject: [PATCH 5/7] Update changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d67a7e4..6237439 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# v2.2.1 + +* Expand preamble checking in Document() +* Box() requires dimensions +* Rectangle() requires coordinates +* Reinstate LICENSE file (required for conda) + # v2.2.0 * Add readthedocs support From 022b8b47693448dc30cd6f7a9a240f697934acd9 Mon Sep 17 00:00:00 2001 From: Daniel Stoops Date: Fri, 20 Dec 2024 13:39:39 +0200 Subject: [PATCH 6/7] Bump to v2.2.1 --- src/czml3/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/czml3/__init__.py b/src/czml3/__init__.py index 3ac2e65..1018406 100644 --- a/src/czml3/__init__.py +++ b/src/czml3/__init__.py @@ -1,5 +1,5 @@ from .core import CZML_VERSION, Document, Packet -__version__ = "2.2.0" +__version__ = "2.2.1" __all__ = ["Document", "Packet", "CZML_VERSION"] From d5ac6d63525e75f6fb79ed5f0a71a67530610a5d Mon Sep 17 00:00:00 2001 From: Daniel Stoops Date: Fri, 20 Dec 2024 13:43:35 +0200 Subject: [PATCH 7/7] Fix test_preamble_position_supplied() --- tests/test_packet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_packet.py b/tests/test_packet.py index d8149cc..347957f 100644 --- a/tests/test_packet.py +++ b/tests/test_packet.py @@ -1037,7 +1037,7 @@ def test_preamble_position_supplied(): id="document", version="1.0", name="Test Document", - position={"cartesian": [0, 0, 0]}, + position=Position(cartesian=[0, 0, 0]), ) ] )