Examples

Each example below is a complete script; the test suite runs every one of them.

A static box

A CZML document is a list of packets, which have several properties. Recreating the blue box from Cesium sandcastle’s CZML Box:

from czml3 import (
    Box,
    BoxDimensions,
    Color,
    Document,
    Material,
    Packet,
    Position,
    SolidColorMaterial,
)

packet_box = Packet(
    id="my_id",
    position=Position(cartographicDegrees=[-114.0, 40.0, 300000.0]),
    box=Box(
        dimensions=BoxDimensions(cartesian=[400000.0, 300000.0, 500000.0]),
        material=Material(
            solidColor=SolidColorMaterial(color=Color(rgba=[0, 0, 255, 255]))
        ),
    ),
)
doc = Document(packets=[packet_box])
print(doc)

Document adds the required preamble packet (id="document") because none was given. Plain lists are converted to the matching CZML value type, so cartesian=[...] is equivalent to cartesian=Cartesian3Value(values=[...]).

The most common classes, such as Document, Packet and Position, can be imported from czml3 directly; everything else lives in czml3.properties, czml3.types and czml3.enums. The czml3 package lists the top-level imports.

Coercion of data

czml3 uses pydantic for all classes, so data is coerced to the right type. For example, this creates a Position of doubles from a numpy array of integers:

import numpy as np

from czml3 import Position

print(Position(cartographicDegrees=np.array([-114, 40, 300000], dtype=int)))

An object moving over time

Time-dynamic values pair an epoch with interleaved [seconds_since_epoch, value, ...] samples. Here an aircraft flies between two points over ten minutes, with its trail drawn behind it. Use timezone-aware datetimes: naive ones are treated as UTC and emit a czml3.NaiveDatetimeWarning.

import datetime as dt

from czml3 import (
    Clock,
    CZML_VERSION,
    Document,
    Packet,
    Path,
    Point,
    Position,
    TimeInterval,
)
from czml3.enums import ClockRanges

start = dt.datetime(2024, 1, 1, 12, tzinfo=dt.timezone.utc)
end = start + dt.timedelta(minutes=10)

doc = Document(
    packets=[
        Packet(
            id="document",
            name="flight",
            version=CZML_VERSION,
            clock=Clock(
                interval=TimeInterval(start=start, end=end),
                currentTime=start,
                multiplier=10,
                range=ClockRanges.LOOP_STOP,
            ),
        ),
        Packet(
            id="aircraft",
            availability=TimeInterval(start=start, end=end),
            position=Position(
                epoch=start,
                cartographicDegrees=[
                    0, -122.39, 37.62, 0,
                    600, -121.93, 37.36, 3000,
                ],
            ),
            point=Point(pixelSize=8),
            path=Path(leadTime=0, trailTime=600, width=2),
        ),
    ]
)
print(doc)

The clock in the preamble sets the scene’s time span, where playback starts, how fast it runs (multiplier) and what happens at the end (range).

A satellite in an inertial frame

Positions can also be given in Cartesian coordinates, in the Earth-fixed (default) or inertial frame, and interpolated between samples. This tracks the International Space Station using Lagrange interpolation:

from czml3 import CZML_VERSION, Document, Packet, Path, Point, Position
from czml3.enums import InterpolationAlgorithms, ReferenceFrames
from czml3.types import TimeInterval

packet_iss = Packet(
    id="InternationalSpaceStation",
    availability=TimeInterval(
        start="2024-01-01T00:00:00Z", end="2024-01-01T00:01:00Z"
    ),
    position=Position(
        epoch="2024-01-01T00:00:00Z",
        interpolationAlgorithm=InterpolationAlgorithms.LAGRANGE,
        referenceFrame=ReferenceFrames.INERTIAL,
        cartesian=[
            0.0,  -6668447.2, 1201886.5, 146789.4,
            60.0, -6711432.8,  919677.7, -214047.6,
        ],
    ),
    point=Point(pixelSize=5.0),
    path=Path(show=True, width=1.0),
)
doc = Document(
    packets=[
        Packet(id="document", name="ISS", version=CZML_VERSION),
        packet_iss,
    ]
)
print(doc)

Load, edit and save

Document.load reads a CZML file into validated objects, and save writes it back (pass indent=None for compact output):

from czml3 import Color, Document, Packet, Point, Position

Document(
    packets=[
        Packet(id="site", position=Position(cartographicDegrees=[34.8, 32.1, 0]))
    ]
).save("scene.czml")

doc = Document.load("scene.czml")
site = next(packet for packet in doc.packets if packet.id == "site")
site.point = Point(pixelSize=10, color=Color(rgba=[255, 0, 0, 255]))
doc.save("scene.czml", indent=None)

Previewing a document

In Jupyter, a Document displays as an interactive Cesium viewer. Anywhere else, write the same viewer to a standalone HTML page and open it in a browser:

from czml3 import Document, Packet, Point, Position

doc = Document(
    packets=[
        Packet(
            id="site",
            position=Position(cartographicDegrees=[34.8, 32.1, 0]),
            point=Point(pixelSize=10),
        )
    ]
)
doc.save_html("scene.html")

Without a Cesium ion access token the page uses the low-resolution Natural Earth II imagery that ships with CesiumJS; pass ion_token=... to use Cesium’s imagery and world terrain, and cesium_version=... to choose the CesiumJS release.

Browsers block Cesium’s web workers on pages opened straight from disk (file://), so geometry such as polylines and polygons would not be drawn. Serve the file over HTTP instead, for example with python -m http.server, and open http://localhost:8000/scene.html.

The page records its load status as attributes on its <body>: data-czml3-entities (the number of entities loaded), data-czml3-zoomed (set once the document’s geometry is built and the camera has zoomed to it), data-czml3-globe (set once the first globe tiles have loaded) and data-czml3-error (the latest error, including failed imagery tiles). When the page is embedded in an iframe, as in Jupyter, it also posts each update to the embedding page as a message such as {"czml3": {"czml3Entities": "3"}}.