Module vcd.types

Module to handle the different data types in VCD library.

This module implements classes to allocate data of different types.

Expand source code
"""
Module to handle the different data types in VCD library.

This module implements classes to allocate data of different types.
"""
# VCD (Video Content Description) library.
#
# Project website: http://vcd.vicomtech.org
#
# Copyright (C) 2023, Vicomtech (http://www.vicomtech.es/),
# (Spain) all rights reserved.
#
# VCD is a Python library to create and manage OpenLABEL content.
# VCD is distributed under MIT License. See LICENSE.

from __future__ import annotations

from builtins import bool
from enum import Enum
from typing import Any

from vcd import poly2d as poly


class CoordinateSystemType(Enum):
    sensor_cs = 1  # the coordinate system of a certain sensor
    local_cs = 2  # e.g. vehicle-ISO8855 in OpenLABEL, or "base_link" in ROS
    scene_cs = 3  # e.g. "odom" in ROS; starting as the first local-ls
    geo_utm = 4  # In UTM coordinates
    geo_wgs84 = 5  # In WGS84 elliptical Earth coordinates
    custom = 6  # Any other coordinate system


class FisheyeModel(Enum):
    """
    List of the different camera fisheye models.

    Any 3D point in space P=(X,Y,Z,1)^T has a radius with respect to the optical axis Z:

    r = ||X^2 + Y^2||

    The angle of incidence to the optical center is a = atan(r/Z).
    The angle of incidence then spans from 0 to pi/2.

    The model of the lens relates the angle of incidence with the radius of the point in the
    image plane (in pixels):

    - "radial_poly" (4 distortion coefficients) rp = k1*a + k2*a^2 + k3*a^3 + k4*a^4
    - "kannala" (5 distortion coefficients)     rp = k1*a + k2*a^3 + k3*a^5 + k4*a^7 + k5*a^9
    - "opencv_fisheye" (4 distortion coefficients, equivalent to Kannala with k1=1.0, so only the
    last 4 terms are used)                    rp = a + k1*a^3 + k2*a^5 + k3*a^7 + k4*a^9
    """

    radial_poly = 1
    kannala = 2
    opencv_fisheye = 3


class Intrinsics:
    def __init__(self):
        self.data = {}


class IntrinsicsPinhole(Intrinsics):
    def __init__(
        self,
        width_px: int,
        height_px: int,
        camera_matrix_3x4: list,
        distortion_coeffs_1xN: list | None = None,
        **additional_items: Any,
    ):
        Intrinsics.__init__(self)
        if not isinstance(width_px, int):
            raise TypeError("Argument 'width_px' must be of type 'int'")
        if not isinstance(height_px, int):
            raise TypeError("Argument 'height_px' must be of type 'int'")

        self.data["intrinsics_pinhole"] = {}
        self.data["intrinsics_pinhole"]["width_px"] = width_px
        self.data["intrinsics_pinhole"]["height_px"] = height_px
        if not isinstance(camera_matrix_3x4, list):
            raise TypeError("Argument 'camera_matrix_3x4' must be of type 'list'")

        if len(camera_matrix_3x4) != 12:
            raise ValueError("Argument 'camera_matrix_3x4' must have 12 elements")
        self.data["intrinsics_pinhole"]["camera_matrix_3x4"] = camera_matrix_3x4

        if distortion_coeffs_1xN is None:
            distortion_coeffs_1xN = []
        else:
            if not isinstance(distortion_coeffs_1xN, list):
                raise TypeError("Argument 'distortion_coeffs_1xN' must be of type 'list'")
            num_coeffs = len(distortion_coeffs_1xN)
            if not 4 <= num_coeffs <= 14:
                raise ValueError("Invalid value for argument 'num_coeffs'")
        self.data["intrinsics_pinhole"]["distortion_coeffs_1xN"] = distortion_coeffs_1xN

        if additional_items is not None:
            self.data["intrinsics_pinhole"].update(additional_items)


class IntrinsicsFisheye(Intrinsics):
    def __init__(
        self,
        width_px: int,
        height_px: int,
        lens_coeffs_1xN: list,
        center_x: float | None,
        center_y: float | None,
        focal_length_x: float | None,
        focal_length_y: float | None,
        fisheye_model: FisheyeModel | None = None,
        **additional_items: Any,
    ):
        Intrinsics.__init__(self)
        if not isinstance(width_px, int):
            raise TypeError("Argument 'width_px' must be of type 'int'")
        if not isinstance(height_px, int):
            raise TypeError("Argument 'height_px' must be of type 'int'")
        self.data["intrinsics_fisheye"] = {}
        self.data["intrinsics_fisheye"]["width_px"] = width_px
        self.data["intrinsics_fisheye"]["height_px"] = height_px
        if not isinstance(lens_coeffs_1xN, list):
            raise TypeError("Argument 'lens_coeffs_1xN' must be of type 'list'")
        if not isinstance(center_x, (float, type(None))):
            raise TypeError("Argument 'center_x' must be of type 'float' or 'None'")
        if not isinstance(center_y, (float, type(None))):
            raise TypeError("Argument 'center_y' must be of type 'float' or 'None'")
        if not isinstance(focal_length_x, (float, type(None))):
            raise TypeError("Argument 'focal_length_x' must be of type 'float' or 'None'")
        if not isinstance(focal_length_y, (float, type(None))):
            raise TypeError("Argument 'focal_length_y' must be of type 'float' or 'None'")

        self.data["intrinsics_fisheye"]["center_x"] = center_x
        self.data["intrinsics_fisheye"]["center_y"] = center_y
        self.data["intrinsics_fisheye"]["focal_length_x"] = focal_length_x
        self.data["intrinsics_fisheye"]["focal_length_y"] = focal_length_y
        self.data["intrinsics_fisheye"]["lens_coeffs_1xN"] = lens_coeffs_1xN

        if fisheye_model is not None:
            if not isinstance(fisheye_model, FisheyeModel):
                raise TypeError("Argument 'fisheye_model' must be of type 'FisheyeModel'")
            if fisheye_model is FisheyeModel.radial_poly:
                if len(lens_coeffs_1xN) != 4:
                    raise ValueError(
                        "Argument 'lens_coeffs_1xN' for 'FisheyeModel' type "
                        "'radial_poly' must have 4 elements "
                    )
            elif fisheye_model is FisheyeModel.kannala:
                if len(lens_coeffs_1xN) != 5:
                    raise ValueError(
                        "Argument 'lens_coeffs_1xN' for 'FisheyeModel' type "
                        "'kannala' must have 5 elements "
                    )
            elif fisheye_model is FisheyeModel.opencv_fisheye:
                if len(lens_coeffs_1xN) != 4:
                    raise ValueError(
                        "Argument 'lens_coeffs_1xN' for 'FisheyeModel' type "
                        "'opencv_fisheye' must have 4 elements "
                    )
            else:
                raise RuntimeError(
                    "ERROR: Fisheyemodel not supported. See types.FisheyeModel enum."
                )
            self.data["intrinsics_fisheye"]["model"] = fisheye_model.name
        else:
            if len(lens_coeffs_1xN) == 4:
                self.data["intrinsics_fisheye"][
                    "model"
                ] = FisheyeModel.opencv_fisheye.name
            elif len(lens_coeffs_1xN) == 5:
                self.data["intrinsics_fisheye"]["model"] = FisheyeModel.kannala.name

        if additional_items is not None:
            self.data["intrinsics_fisheye"].update(additional_items)


class IntrinsicsCylindrical(Intrinsics):
    def __init__(
        self,
        width_px: int,
        height_px: int,
        fov_horz_rad: float,
        fov_vert_rad: float,
        **additional_items: Any,
    ):
        Intrinsics.__init__(self)
        if not isinstance(width_px, int):
            raise TypeError("Argument 'width_px' must be of type 'int'")
        if not isinstance(height_px, int):
            raise TypeError("Argument 'height_px' must be of type 'int'")
        self.data["intrinsics_cylindrical"] = {}
        self.data["intrinsics_cylindrical"]["width_px"] = width_px
        self.data["intrinsics_cylindrical"]["height_px"] = height_px
        if not isinstance(fov_horz_rad, float):
            raise TypeError("Argument 'fov_horz_rad' must be of type 'float'")
        if not isinstance(fov_vert_rad, float):
            raise TypeError("Argument 'fov_vert_rad' must be of type 'float'")
        self.data["intrinsics_cylindrical"]["fov_horz_rad"] = fov_horz_rad
        self.data["intrinsics_cylindrical"]["fov_vert_rad"] = fov_vert_rad

        if additional_items is not None:
            self.data["intrinsics_cylindrical"].update(additional_items)


class IntrinsicsOrthographic(Intrinsics):
    def __init__(
        self,
        width_px: int,
        height_px: int,
        xmin: float,
        xmax: float,
        ymin: float,
        ymax: float,
        **additional_items: Any,
    ):
        Intrinsics.__init__(self)
        if not isinstance(width_px, int):
            raise TypeError("Argument 'width_px' must be of type 'int'")
        if not isinstance(height_px, int):
            raise TypeError("Argument 'height_px' must be of type 'int'")
        self.data["intrinsics_orthographic"] = {}
        self.data["intrinsics_orthographic"]["width_px"] = width_px
        self.data["intrinsics_orthographic"]["height_px"] = height_px
        if not isinstance(xmin, float):
            raise TypeError("Argument 'xmin' must be of type 'float'")
        if not isinstance(xmax, float):
            raise TypeError("Argument 'xmax' must be of type 'float'")
        if not isinstance(ymin, float):
            raise TypeError("Argument 'ymin' must be of type 'float'")
        if not isinstance(ymax, float):
            raise TypeError("Argument 'ymax' must be of type 'float'")
        self.data["intrinsics_orthographic"]["xmin"] = xmin
        self.data["intrinsics_orthographic"]["xmax"] = xmax
        self.data["intrinsics_orthographic"]["ymin"] = ymin
        self.data["intrinsics_orthographic"]["ymax"] = ymax

        if additional_items is not None:
            self.data["intrinsics_orthographic"].update(additional_items)


class IntrinsicsCubemap(Intrinsics):
    def __init__(
        self,
        width_px: int,
        height_px: int,
        camera_matrix_3x4: list,
        distortion_coeffs_1xN: list | None = None,
        **additional_items: Any,
    ):
        Intrinsics.__init__(self)
        if not isinstance(width_px, int):
            raise TypeError("Argument 'width_px' must be of type 'int'")
        if not isinstance(height_px, int):
            raise TypeError("Argument 'height_px' must be of type 'int'")
        self.data["intrinsics_cubemap"] = {}
        self.data["intrinsics_cubemap"]["width_px"] = width_px
        self.data["intrinsics_cubemap"]["height_px"] = height_px
        if not isinstance(camera_matrix_3x4, list):
            raise TypeError("Argument 'camera_matrix_3x4' must be of type 'list'")

        if len(camera_matrix_3x4) != 12:
            raise TypeError("Argument 'camera_matrix_3x4' must have 12 elements")
        self.data["intrinsics_cubemap"]["camera_matrix_3x4"] = camera_matrix_3x4

        if distortion_coeffs_1xN is None:
            distortion_coeffs_1xN = []
        else:
            if not isinstance(distortion_coeffs_1xN, list):
                raise TypeError("Argument 'distortion_coeffs_1xN' must be of type 'list'")
            num_coeffs = len(distortion_coeffs_1xN)
            if not 4 <= num_coeffs <= 14:
                raise ValueError("Invalid value for argument 'num_coeffs'")
        self.data["intrinsics_cubemap"]["distortion_coeffs_1xN"] = distortion_coeffs_1xN

        if additional_items is not None:
            self.data["intrinsics_cubemap"].update(additional_items)


class IntrinsicsCustom(Intrinsics):
    def __init__(self, **additional_items: Any):
        Intrinsics.__init__(self)
        self.data["intrinsics_custom"] = {}
        if additional_items is not None:
            self.data["intrinsics_custom"].update(additional_items)


class TransformDataType(Enum):
    matrix_4x4 = 1
    quat_and_trans_7x1 = 2
    euler_and_trans_6x1 = 3
    custom = 4


class TransformData:
    """
    Define methods to obtain different coordinate system transformation representations.

    This class encodes the transform data in the form of 4x4 matrix, quaternion + translation,
    or Euler angles + translation.
    """

    def __init__(self, val: list, t_type: TransformDataType, **additional_items: Any):
        if not isinstance(val, list):
            raise TypeError("Argument 'val' must be of type 'list'")
        if not isinstance(t_type, TransformDataType):
            raise TypeError("Argument 't_type' must be of type 'TransformDataType'")

        self.data = {}
        if t_type == TransformDataType.matrix_4x4:
            self.data["matrix4x4"] = val
        elif t_type == TransformDataType.quat_and_trans_7x1:
            if len(val) != 7:
                raise ValueError(
                    "Argument 'val' for 'TransformDataType' type 'quat_and_trans_7x1' "
                    "must have 7 elements "
                )
            self.data["quaternion"] = val[0:4]
            self.data["translation"] = val[4:7]
        elif t_type == TransformDataType.euler_and_trans_6x1:
            if len(val) != 6:
                raise ValueError(
                    "Argument 'val' for 'TransformDataType' type 'euler_and_trans_6x1' "
                    "must have 7 elements "
                )
            self.data["euler_angles"] = val[0:3]
            self.data["translation"] = val[3:6]

        if additional_items is not None:
            self.data.update(additional_items)


class PoseData(TransformData):
    """
    Subclass of TransformData.

    Equivalent to TransformData, but intended to be used when passive rotation and translation
    values are provided.
    """

    def __init__(self, val: list, t_type: TransformDataType, **additional_items: Any):
        TransformData.__init__(self, val, t_type, **additional_items)


class Transform:
    def __init__(
        self,
        src_name: str,
        dst_name: str,
        transform_src_to_dst: TransformData,
        **additional_items: Any,
    ):
        if not isinstance(src_name, str):
            raise TypeError("Argument 'src_name' must be of type 'str'")
        if not isinstance(dst_name, str):
            raise TypeError("Argument 'dst_name' must be of type 'str'")
        if not isinstance(transform_src_to_dst, TransformData):
            raise TypeError(
                "Argument 'transform_src_to_dst' must be of type 'TransformData'"
            )
        self.data: dict = {}
        name = src_name + "_to_" + dst_name
        self.data[name] = {}
        transform_name: dict = self.data[name]
        self.data_additional = {}  # this is useful to append only the additional_items
        transform_name["src"] = src_name
        transform_name["dst"] = dst_name
        transform_name["transform_src_to_dst"] = transform_src_to_dst.data
        if additional_items is not None:
            transform_name.update(additional_items)
            self.data_additional.update(additional_items)


class StreamSync:
    def __init__(
        self,
        frame_vcd: int | None = None,
        frame_stream: int | None = None,
        timestamp_ISO8601: str | None = None,
        frame_shift: int | None = None,
        **additional_items: Any,
    ):
        self.data: dict = {}
        self.data["sync"] = {}
        # This is the master frame at vcd (if it is None, frame_shift specifies constant shift
        self.frame_vcd = frame_vcd

        if frame_shift is not None:
            if not isinstance(frame_shift, int):
                raise TypeError("Argument 'frame_shift' must be of type 'int'")
            if not (
                frame_stream is None and timestamp_ISO8601 is None and frame_vcd is None
            ):
                raise ValueError(
                    "At least one of these arguments needs to be different than None: "
                    "'frame_stream', 'timestamp_ISO8601', 'frame_vcd'"
                )
            self.data["sync"]["frame_shift"] = frame_shift
        else:
            if not isinstance(frame_vcd, int):
                raise TypeError("Argument 'frame_vcd' must be of type 'int'")
            if frame_stream is not None:
                if not isinstance(frame_stream, int):
                    raise TypeError("Argument 'frame_stream' must be of type 'int'")
                self.data["sync"]["frame_stream"] = frame_stream
            if timestamp_ISO8601 is not None:
                if not isinstance(timestamp_ISO8601, str):
                    raise TypeError("Argument 'timestamp_ISO8601' must be of type 'str'")
                self.data["sync"]["timestamp"] = timestamp_ISO8601
        if additional_items is not None:
            self.data["sync"].update(additional_items)


class ObjectDataType(Enum):
    generic = 0
    bbox = 1
    rbbox = 2
    num = 3
    text = 4
    boolean = 5
    poly2d = 6
    poly3d = 7
    cuboid = 8
    image = 9
    mat = 10
    binary = 11
    point2d = 12
    point3d = 13
    vec = 14
    line_reference = 15
    area_reference = 16
    mesh = 17


class Poly2DType(Enum):
    MODE_POLY2D_ABSOLUTE = 0
    # MODE_POLY2D_BBOX = 1
    # MODE_POLY2D_BBOX_DIST = 2
    # MODE_POLY2D_F8DCC = 3
    # MODE_POLY2D_RF8DCC = 4
    MODE_POLY2D_SRF6DCC = 5
    MODE_POLY2D_RS6FCC = 6


class ObjectData:
    # pylint: disable=redefined-builtin
    def __init__(
        self,
        name: str,
        coordinate_system: str | None = None,
        properties: dict | None = None,
        type: str | None = None,
    ):
        self.data: dict = {}
        self.type = ObjectDataType.generic
        if name is not None:
            if not isinstance(name, str):
                raise TypeError("Argument 'name' must be of type 'str'")
            self.data["name"] = name
        if coordinate_system is not None:
            if not isinstance(coordinate_system, str):
                raise TypeError("Argument 'coordinate_system' must be of type 'str'")
            self.data["coordinate_system"] = coordinate_system
        if properties is not None:
            if not isinstance(properties, dict):
                raise TypeError("Argument 'properties' must be of type 'dict'")
            self.data.update(properties)
        if type is not None:
            if not isinstance(type, str):
                raise TypeError("Argument 'type' must be of type 'str'")
            self.data["type"] = type

    def add_attribute(self, object_data: ObjectData | ObjectDataGeometry):
        if not isinstance(object_data, (ObjectData, ObjectDataGeometry)):
            raise TypeError(
                "Argument 'object_data' must be of type 'ObjectData' or "
                "'ObjectDataGeometry'"
            )

        # Creates 'attributes' if it does not exist
        self.data.setdefault("attributes", {})

        if object_data.type.name in self.data["attributes"]:
            # Find if element_data already there, if so, replace, otherwise, append
            list_aux = self.data["attributes"][object_data.type.name]
            pos_list = [
                idx
                for idx, val in enumerate(list_aux)
                if val["name"] == object_data.data["name"]
            ]
            if len(pos_list) == 0:
                # No: then, just push this new object data
                self.data["attributes"][object_data.type.name].append(object_data.data)
            else:
                # Ok, exists, so let's substitute
                pos = pos_list[0]
                self.data["attributes"][object_data.type.name][pos] = object_data.data
        else:
            self.data["attributes"][object_data.type.name] = [object_data.data]


class ObjectDataGeometry(ObjectData):
    def __init__(
        self,
        name: str,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        # Calling parent class
        ObjectData.__init__(self, name, coordinate_system, properties)


class bbox(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if len(val) != 4:
            raise TypeError("Argument 'val' must have 4 elements")
        if isinstance(val, tuple):
            self.data["val"] = val
        elif isinstance(val, list):
            self.data["val"] = tuple(val)
        self.type = ObjectDataType.bbox


class rbbox(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if len(val) != 5:
            raise TypeError("Argument 'val' must have 5 elements")
        if isinstance(val, tuple):
            self.data["val"] = val
        elif isinstance(val, list):
            self.data["val"] = tuple(val)
        self.type = ObjectDataType.rbbox


class num(ObjectData):  # noqa: N801  # remove capitals in class name warning
    # pylint: disable=redefined-builtin
    def __init__(
        self,
        name: str,
        val: int | float,
        coordinate_system: str | None = None,
        properties: dict | None = None,
        type: str | None = None,
    ):
        ObjectData.__init__(self, name, coordinate_system, properties, type)
        if not isinstance(val, (int, float)):
            raise TypeError("Argument 'val' must be of type 'int' or 'float'")
        self.data["val"] = val
        self.type = ObjectDataType.num


class text(ObjectData):  # noqa: N801  # remove capitals in class name warning
    # pylint: disable=redefined-builtin
    def __init__(
        self,
        name: str,
        val: str,
        coordinate_system: str | None = None,
        properties: dict | None = None,
        type: str | None = None,
    ):
        ObjectData.__init__(self, name, coordinate_system, properties, type)
        if not isinstance(val, str):
            raise TypeError("Argument 'val' must be of type 'str'")
        self.data["val"] = val
        self.type = ObjectDataType.text


class boolean(ObjectData):  # noqa: N801  # remove capitals in class name warning
    # pylint: disable=redefined-builtin
    def __init__(
        self,
        name: str,
        val: bool,
        coordinate_system: str | None = None,
        properties: dict | None = None,
        type: str | None = None,
    ):
        ObjectData.__init__(self, name, coordinate_system, properties, type)
        if not isinstance(val, bool):
            raise TypeError("Argument 'val' must be of type 'bool'")
        self.data["val"] = val
        self.type = ObjectDataType.boolean


class poly2d(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        mode: Poly2DType,
        closed: bool,
        hierarchy: list | None = None,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if not isinstance(mode, Poly2DType):
            raise TypeError("Argument 'mode' must be of type 'Poly2DType'")
        if not isinstance(closed, bool):
            raise TypeError("Argument 'closed' must be of type 'bool'")
        if isinstance(val, (list, tuple)):
            if mode == Poly2DType.MODE_POLY2D_SRF6DCC:
                srf6, xinit, yinit = poly.compute_srf6dcc(val)
                encoded_poly, rest = poly.chaincode_base64_encoder(srf6, 3)
                self.data["val"] = [str(xinit), str(yinit), str(rest), encoded_poly]
            elif mode == Poly2DType.MODE_POLY2D_RS6FCC:
                rs6, low, high, xinit, yinit = poly.compute_rs6fcc(val)
                encoded_poly, rest = poly.chaincode_base64_encoder(rs6, 3)
                self.data["val"] = [
                    str(xinit),
                    str(yinit),
                    str(low),
                    str(high),
                    str(rest),
                    encoded_poly,
                ]
            else:
                self.data["val"] = list(val)
        self.data["mode"] = mode.name
        self.data["closed"] = closed
        self.type = ObjectDataType.poly2d
        if hierarchy is not None:
            if not isinstance(hierarchy, list):
                raise TypeError("Argument 'hierarchy' must be of type 'list'")
            if not all(isinstance(x, int) for x in hierarchy):
                raise TypeError(
                    "All values of argument 'hierarchy' must be of type 'int'"
                )
            self.data["hierarchy"] = hierarchy


class poly3d(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        closed: bool,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if not isinstance(closed, bool):
            raise TypeError("Argument 'closed' must be of type 'bool'")
        if isinstance(val, tuple):
            self.data["val"] = val
        elif isinstance(val, list):
            self.data["val"] = tuple(val)
        self.data["closed"] = closed
        self.type = ObjectDataType.poly3d


class cuboid(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if val is not None:
            if not isinstance(val, (tuple, list)):
                raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
            if len(val) not in (9, 10):
                raise ValueError("Argument 'val' must have 9 or 10 elements")
            if len(val) == 9:
                self.use_quaternion = False
            else:
                self.use_quaternion = True
        if isinstance(val, tuple):
            self.data["val"] = list(val)
        elif isinstance(val, list):
            self.data["val"] = val
        else:
            self.data["val"] = None
        self.type = ObjectDataType.cuboid


class image(ObjectData):  # noqa: N801  # remove capitals in class name warning
    """
    Host the image data in buffer format.

    It can be used with any codification, although base64 and webp are suggested.

    mimeType: "image/png", "image/jpeg", .. as in
                                https://www.sitepoint.com/mime-types-complete-list/
    encoding: "base64", "ascii", .. as in
                            https://docs.python.org/2.4/lib/standard-encodings.html

    Default is base64

    OpenCV can be used to encode:
    img = cv2.imread(file_name, 1)
    compr_params = [int(cv2.IMWRITE_PNG_COMPRESSION), 9]
    result, payload = cv2.imencode('.png', img, compr_params)
    """

    def __init__(
        self,
        name: str,
        val: str,
        mime_type: str,
        encoding: str,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectData.__init__(self, name, coordinate_system, properties, None)
        if not isinstance(val, str):
            raise TypeError("Argument 'val' must be of type 'str'")
        if not isinstance(mime_type, str):
            raise TypeError("Argument 'mime_type' must be of type 'str'")
        if not isinstance(encoding, str):
            raise TypeError("Argument 'encoding' must be of type 'str'")
        self.data["val"] = val
        self.data["mime_type"] = mime_type
        self.data["encoding"] = encoding
        self.type = ObjectDataType.image


class mat(ObjectData):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        channels: int,
        width: int,
        height: int,
        data_type: str,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectData.__init__(self, name, coordinate_system, properties, None)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if not isinstance(width, int):
            raise TypeError("Argument 'width' must be of type 'int'")
        if not isinstance(height, int):
            raise TypeError("Argument 'height' must be of type 'int'")
        if not isinstance(channels, int):
            raise TypeError("Argument 'channels' must be of type 'int'")
        if not isinstance(data_type, str):
            raise TypeError("Argument 'dataType' must be of type 'str'")
        if len(val) != width * height * channels:
            raise ValueError("len(val) != width * height * channels")

        if isinstance(val, tuple):
            self.data["val"] = val
        elif isinstance(val, list):
            self.data["val"] = tuple(val)
        self.data["channels"] = channels
        self.data["width"] = width
        self.data["height"] = height
        self.data["data_type"] = data_type
        self.type = ObjectDataType.mat


class binary(ObjectData):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: str,
        data_type: str,
        encoding: str,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectData.__init__(self, name, coordinate_system, properties, None)
        if not isinstance(val, str):
            raise TypeError("Argument 'val' must be of type 'str'")
        if not isinstance(data_type, str):
            raise TypeError("Argument 'dataType' must be of type 'str'")
        if not isinstance(encoding, str):
            raise TypeError("Argument 'encoding' must be of type 'str'")
        self.data["val"] = val
        self.data["data_type"] = data_type
        self.data["encoding"] = encoding
        self.type = ObjectDataType.binary


class vec(ObjectData):  # noqa: N801  # remove capitals in class name warning
    # pylint: disable=redefined-builtin
    def __init__(
        self,
        name: str,
        val: tuple | list,
        coordinate_system: str | None = None,
        properties: dict | None = None,
        type: str | None = None,
    ):
        ObjectData.__init__(self, name, coordinate_system, properties, type)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if isinstance(val, tuple):
            self.data["val"] = val
        elif isinstance(val, list):
            self.data["val"] = tuple(val)
        self.type = ObjectDataType.vec


class point2d(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        point_id: int | None = None,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if len(val) != 2:
            raise ValueError("Argument 'val' must have 2 elements")
        if isinstance(val, tuple):
            self.data["val"] = val
        elif isinstance(val, list):
            self.data["val"] = tuple(val)
        if point_id is not None:
            if not isinstance(point_id, int):
                raise TypeError("Argument 'point_id' must be of type 'int'")
            self.data["id"] = point_id
        self.type = ObjectDataType.point2d


class point3d(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        point_id: int | None = None,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if len(val) != 3:
            raise ValueError("Argument 'val' must have 3 elements")

        if isinstance(val, tuple):
            self.data["val"] = val
        elif isinstance(val, list):
            self.data["val"] = tuple(val)
        if point_id is not None:
            if not isinstance(point_id, int):
                raise TypeError("Argument 'id' must be of type 'int'")
            self.data["id"] = point_id
        self.type = ObjectDataType.point3d


class GeometricReference(ObjectDataGeometry):
    def __init__(
        self,
        name: str,
        val: list,
        reference_type: ObjectDataType,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if not isinstance(reference_type, ObjectDataType):
            raise TypeError("Argument 'reference_type' must be of type ' ObjectDataType'")
        self.data["reference_type"] = reference_type.name
        if val is not None:
            if not isinstance(val, list):
                raise TypeError("Argument 'val' must be of type ' list'")
            self.data["val"] = val


class LineReference(GeometricReference):
    def __init__(
        self,
        name: str,
        val: list,
        reference_type: ObjectDataType,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        GeometricReference.__init__(
            self, name, val, reference_type, coordinate_system, properties
        )


class AreaReference(GeometricReference):
    def __init__(
        self,
        name: str,
        val: list,
        reference_type: ObjectDataType,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        GeometricReference.__init__(
            self, name, val, reference_type, coordinate_system, properties
        )


class VolumeReference(GeometricReference):
    def __init__(
        self,
        name: str,
        val: list,
        reference_type: ObjectDataType,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        GeometricReference.__init__(
            self, name, val, reference_type, coordinate_system, properties
        )


class mesh(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        self.pid = "0"
        self.eid = "0"
        self.aid = "0"
        self.vid = "0"
        self.data["point3d"] = {}
        self.data["line_reference"] = {}
        self.data["area_reference"] = {}
        self.type = ObjectDataType.mesh

    # Vertex
    def add_vertex(
        self, p3d: point3d, id: str | None = None  # pylint: disable=redefined-builtin
    ) -> str:
        if not isinstance(p3d, point3d):
            raise TypeError("Argument 'p3d' must be of type ' point3d'")

        # If an id is provided use it
        if id is not None:
            # If it already exists, this is an update call
            if id in self.data["point3d"]:
                # The id already exists: substitute
                idx = id
            else:
                idx = id
                self.pid = str(int(idx) + 1)
        else:
            idx = self.pid
            self.pid = str(int(self.pid) + 1)

        self.data.setdefault("point3d", {})
        self.data["point3d"][idx] = p3d.data
        return idx

    # Edge
    def add_edge(
        self,
        lref: LineReference,
        id: str | None = None,  # pylint: disable=redefined-builtin
    ) -> str:
        if not isinstance(lref, LineReference):
            raise TypeError("Argument 'lref' must be of type ' LineReference'")

        if id is not None:
            if id in self.data["line_reference"]:
                idx = id
            else:
                idx = id
                self.eid = str(int(idx) + 1)
        else:
            idx = self.eid
            self.eid = str(int(self.eid) + 1)

        self.data.setdefault("line_reference", {})
        self.data["line_reference"][idx] = lref.data
        return idx

    # Area
    def add_area(
        self,
        aref: AreaReference,
        id: str | None = None,  # pylint: disable=redefined-builtin
    ) -> str:
        if not isinstance(aref, AreaReference):
            raise TypeError("Argument 'aref' must be of type ' AreaReference'")

        if id is not None:
            if id in self.data["area_reference"]:
                idx = id
            else:
                idx = id
                self.aid = str(int(idx) + 1)
        else:
            idx = self.aid
            self.aid = str(int(self.aid) + 1)

        self.data.setdefault("area_reference", {})
        self.data["area_reference"][idx] = aref.data
        return idx

    def get_mesh_geometry_as_string(self) -> str:
        result = "[["
        for vertex in self.data["point3d"].values():
            val = vertex["val"]
            result += "["
            for _, elem in enumerate(val):
                result += str(elem) + ","
            result += "],"
        result += "],["

        for edge in self.data["line_reference"].values():
            val = edge["val"]
            result += "["
            for _, elem in enumerate(val):
                result += str(elem) + ","
            result += "],"
        result += "],["

        for area in self.data["area_reference"].values():
            val = area["val"]
            result += "["
            for _, elem in enumerate(val):
                result += str(elem) + ","
            result += "],"
        result += "]]"

        # Clean-out commas
        result = result.replace(",]", "]")

        return result

Classes

class AreaReference (name: str, val: list, reference_type: ObjectDataType, coordinate_system: str | None = None, properties: dict | None = None)
Expand source code
class AreaReference(GeometricReference):
    def __init__(
        self,
        name: str,
        val: list,
        reference_type: ObjectDataType,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        GeometricReference.__init__(
            self, name, val, reference_type, coordinate_system, properties
        )

Ancestors

class CoordinateSystemType (*args, **kwds)

Create a collection of name/value pairs.

Example enumeration:

>>> class Color(Enum):
...     RED = 1
...     BLUE = 2
...     GREEN = 3

Access them by:

  • attribute access::
>>> Color.RED
<Color.RED: 1>
  • value lookup:
>>> Color(1)
<Color.RED: 1>
  • name lookup:
>>> Color['RED']
<Color.RED: 1>

Enumerations can be iterated over, and know how many members they have:

>>> len(Color)
3
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.

Expand source code
class CoordinateSystemType(Enum):
    sensor_cs = 1  # the coordinate system of a certain sensor
    local_cs = 2  # e.g. vehicle-ISO8855 in OpenLABEL, or "base_link" in ROS
    scene_cs = 3  # e.g. "odom" in ROS; starting as the first local-ls
    geo_utm = 4  # In UTM coordinates
    geo_wgs84 = 5  # In WGS84 elliptical Earth coordinates
    custom = 6  # Any other coordinate system

Ancestors

  • enum.Enum

Class variables

var custom
var geo_utm
var geo_wgs84
var local_cs
var scene_cs
var sensor_cs
class FisheyeModel (*args, **kwds)

List of the different camera fisheye models.

Any 3D point in space P=(X,Y,Z,1)^T has a radius with respect to the optical axis Z:

r = ||X^2 + Y^2||

The angle of incidence to the optical center is a = atan(r/Z). The angle of incidence then spans from 0 to pi/2.

The model of the lens relates the angle of incidence with the radius of the point in the image plane (in pixels):

  • "radial_poly" (4 distortion coefficients) rp = k1a + k2a^2 + k3a^3 + k4a^4
  • "kannala" (5 distortion coefficients) rp = k1a + k2a^3 + k3a^5 + k4a^7 + k5*a^9
  • "opencv_fisheye" (4 distortion coefficients, equivalent to Kannala with k1=1.0, so only the last 4 terms are used) rp = a + k1a^3 + k2a^5 + k3a^7 + k4a^9
Expand source code
class FisheyeModel(Enum):
    """
    List of the different camera fisheye models.

    Any 3D point in space P=(X,Y,Z,1)^T has a radius with respect to the optical axis Z:

    r = ||X^2 + Y^2||

    The angle of incidence to the optical center is a = atan(r/Z).
    The angle of incidence then spans from 0 to pi/2.

    The model of the lens relates the angle of incidence with the radius of the point in the
    image plane (in pixels):

    - "radial_poly" (4 distortion coefficients) rp = k1*a + k2*a^2 + k3*a^3 + k4*a^4
    - "kannala" (5 distortion coefficients)     rp = k1*a + k2*a^3 + k3*a^5 + k4*a^7 + k5*a^9
    - "opencv_fisheye" (4 distortion coefficients, equivalent to Kannala with k1=1.0, so only the
    last 4 terms are used)                    rp = a + k1*a^3 + k2*a^5 + k3*a^7 + k4*a^9
    """

    radial_poly = 1
    kannala = 2
    opencv_fisheye = 3

Ancestors

  • enum.Enum

Class variables

var kannala
var opencv_fisheye
var radial_poly
class GeometricReference (name: str, val: list, reference_type: ObjectDataType, coordinate_system: str | None = None, properties: dict | None = None)
Expand source code
class GeometricReference(ObjectDataGeometry):
    def __init__(
        self,
        name: str,
        val: list,
        reference_type: ObjectDataType,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if not isinstance(reference_type, ObjectDataType):
            raise TypeError("Argument 'reference_type' must be of type ' ObjectDataType'")
        self.data["reference_type"] = reference_type.name
        if val is not None:
            if not isinstance(val, list):
                raise TypeError("Argument 'val' must be of type ' list'")
            self.data["val"] = val

Ancestors

Subclasses

class Intrinsics
Expand source code
class Intrinsics:
    def __init__(self):
        self.data = {}

Subclasses

class IntrinsicsCubemap (width_px: int, height_px: int, camera_matrix_3x4: list, distortion_coeffs_1xN: list | None = None, **additional_items: Any)
Expand source code
class IntrinsicsCubemap(Intrinsics):
    def __init__(
        self,
        width_px: int,
        height_px: int,
        camera_matrix_3x4: list,
        distortion_coeffs_1xN: list | None = None,
        **additional_items: Any,
    ):
        Intrinsics.__init__(self)
        if not isinstance(width_px, int):
            raise TypeError("Argument 'width_px' must be of type 'int'")
        if not isinstance(height_px, int):
            raise TypeError("Argument 'height_px' must be of type 'int'")
        self.data["intrinsics_cubemap"] = {}
        self.data["intrinsics_cubemap"]["width_px"] = width_px
        self.data["intrinsics_cubemap"]["height_px"] = height_px
        if not isinstance(camera_matrix_3x4, list):
            raise TypeError("Argument 'camera_matrix_3x4' must be of type 'list'")

        if len(camera_matrix_3x4) != 12:
            raise TypeError("Argument 'camera_matrix_3x4' must have 12 elements")
        self.data["intrinsics_cubemap"]["camera_matrix_3x4"] = camera_matrix_3x4

        if distortion_coeffs_1xN is None:
            distortion_coeffs_1xN = []
        else:
            if not isinstance(distortion_coeffs_1xN, list):
                raise TypeError("Argument 'distortion_coeffs_1xN' must be of type 'list'")
            num_coeffs = len(distortion_coeffs_1xN)
            if not 4 <= num_coeffs <= 14:
                raise ValueError("Invalid value for argument 'num_coeffs'")
        self.data["intrinsics_cubemap"]["distortion_coeffs_1xN"] = distortion_coeffs_1xN

        if additional_items is not None:
            self.data["intrinsics_cubemap"].update(additional_items)

Ancestors

class IntrinsicsCustom (**additional_items: Any)
Expand source code
class IntrinsicsCustom(Intrinsics):
    def __init__(self, **additional_items: Any):
        Intrinsics.__init__(self)
        self.data["intrinsics_custom"] = {}
        if additional_items is not None:
            self.data["intrinsics_custom"].update(additional_items)

Ancestors

class IntrinsicsCylindrical (width_px: int, height_px: int, fov_horz_rad: float, fov_vert_rad: float, **additional_items: Any)
Expand source code
class IntrinsicsCylindrical(Intrinsics):
    def __init__(
        self,
        width_px: int,
        height_px: int,
        fov_horz_rad: float,
        fov_vert_rad: float,
        **additional_items: Any,
    ):
        Intrinsics.__init__(self)
        if not isinstance(width_px, int):
            raise TypeError("Argument 'width_px' must be of type 'int'")
        if not isinstance(height_px, int):
            raise TypeError("Argument 'height_px' must be of type 'int'")
        self.data["intrinsics_cylindrical"] = {}
        self.data["intrinsics_cylindrical"]["width_px"] = width_px
        self.data["intrinsics_cylindrical"]["height_px"] = height_px
        if not isinstance(fov_horz_rad, float):
            raise TypeError("Argument 'fov_horz_rad' must be of type 'float'")
        if not isinstance(fov_vert_rad, float):
            raise TypeError("Argument 'fov_vert_rad' must be of type 'float'")
        self.data["intrinsics_cylindrical"]["fov_horz_rad"] = fov_horz_rad
        self.data["intrinsics_cylindrical"]["fov_vert_rad"] = fov_vert_rad

        if additional_items is not None:
            self.data["intrinsics_cylindrical"].update(additional_items)

Ancestors

class IntrinsicsFisheye (width_px: int, height_px: int, lens_coeffs_1xN: list, center_x: float | None, center_y: float | None, focal_length_x: float | None, focal_length_y: float | None, fisheye_model: FisheyeModel | None = None, **additional_items: Any)
Expand source code
class IntrinsicsFisheye(Intrinsics):
    def __init__(
        self,
        width_px: int,
        height_px: int,
        lens_coeffs_1xN: list,
        center_x: float | None,
        center_y: float | None,
        focal_length_x: float | None,
        focal_length_y: float | None,
        fisheye_model: FisheyeModel | None = None,
        **additional_items: Any,
    ):
        Intrinsics.__init__(self)
        if not isinstance(width_px, int):
            raise TypeError("Argument 'width_px' must be of type 'int'")
        if not isinstance(height_px, int):
            raise TypeError("Argument 'height_px' must be of type 'int'")
        self.data["intrinsics_fisheye"] = {}
        self.data["intrinsics_fisheye"]["width_px"] = width_px
        self.data["intrinsics_fisheye"]["height_px"] = height_px
        if not isinstance(lens_coeffs_1xN, list):
            raise TypeError("Argument 'lens_coeffs_1xN' must be of type 'list'")
        if not isinstance(center_x, (float, type(None))):
            raise TypeError("Argument 'center_x' must be of type 'float' or 'None'")
        if not isinstance(center_y, (float, type(None))):
            raise TypeError("Argument 'center_y' must be of type 'float' or 'None'")
        if not isinstance(focal_length_x, (float, type(None))):
            raise TypeError("Argument 'focal_length_x' must be of type 'float' or 'None'")
        if not isinstance(focal_length_y, (float, type(None))):
            raise TypeError("Argument 'focal_length_y' must be of type 'float' or 'None'")

        self.data["intrinsics_fisheye"]["center_x"] = center_x
        self.data["intrinsics_fisheye"]["center_y"] = center_y
        self.data["intrinsics_fisheye"]["focal_length_x"] = focal_length_x
        self.data["intrinsics_fisheye"]["focal_length_y"] = focal_length_y
        self.data["intrinsics_fisheye"]["lens_coeffs_1xN"] = lens_coeffs_1xN

        if fisheye_model is not None:
            if not isinstance(fisheye_model, FisheyeModel):
                raise TypeError("Argument 'fisheye_model' must be of type 'FisheyeModel'")
            if fisheye_model is FisheyeModel.radial_poly:
                if len(lens_coeffs_1xN) != 4:
                    raise ValueError(
                        "Argument 'lens_coeffs_1xN' for 'FisheyeModel' type "
                        "'radial_poly' must have 4 elements "
                    )
            elif fisheye_model is FisheyeModel.kannala:
                if len(lens_coeffs_1xN) != 5:
                    raise ValueError(
                        "Argument 'lens_coeffs_1xN' for 'FisheyeModel' type "
                        "'kannala' must have 5 elements "
                    )
            elif fisheye_model is FisheyeModel.opencv_fisheye:
                if len(lens_coeffs_1xN) != 4:
                    raise ValueError(
                        "Argument 'lens_coeffs_1xN' for 'FisheyeModel' type "
                        "'opencv_fisheye' must have 4 elements "
                    )
            else:
                raise RuntimeError(
                    "ERROR: Fisheyemodel not supported. See types.FisheyeModel enum."
                )
            self.data["intrinsics_fisheye"]["model"] = fisheye_model.name
        else:
            if len(lens_coeffs_1xN) == 4:
                self.data["intrinsics_fisheye"][
                    "model"
                ] = FisheyeModel.opencv_fisheye.name
            elif len(lens_coeffs_1xN) == 5:
                self.data["intrinsics_fisheye"]["model"] = FisheyeModel.kannala.name

        if additional_items is not None:
            self.data["intrinsics_fisheye"].update(additional_items)

Ancestors

class IntrinsicsOrthographic (width_px: int, height_px: int, xmin: float, xmax: float, ymin: float, ymax: float, **additional_items: Any)
Expand source code
class IntrinsicsOrthographic(Intrinsics):
    def __init__(
        self,
        width_px: int,
        height_px: int,
        xmin: float,
        xmax: float,
        ymin: float,
        ymax: float,
        **additional_items: Any,
    ):
        Intrinsics.__init__(self)
        if not isinstance(width_px, int):
            raise TypeError("Argument 'width_px' must be of type 'int'")
        if not isinstance(height_px, int):
            raise TypeError("Argument 'height_px' must be of type 'int'")
        self.data["intrinsics_orthographic"] = {}
        self.data["intrinsics_orthographic"]["width_px"] = width_px
        self.data["intrinsics_orthographic"]["height_px"] = height_px
        if not isinstance(xmin, float):
            raise TypeError("Argument 'xmin' must be of type 'float'")
        if not isinstance(xmax, float):
            raise TypeError("Argument 'xmax' must be of type 'float'")
        if not isinstance(ymin, float):
            raise TypeError("Argument 'ymin' must be of type 'float'")
        if not isinstance(ymax, float):
            raise TypeError("Argument 'ymax' must be of type 'float'")
        self.data["intrinsics_orthographic"]["xmin"] = xmin
        self.data["intrinsics_orthographic"]["xmax"] = xmax
        self.data["intrinsics_orthographic"]["ymin"] = ymin
        self.data["intrinsics_orthographic"]["ymax"] = ymax

        if additional_items is not None:
            self.data["intrinsics_orthographic"].update(additional_items)

Ancestors

class IntrinsicsPinhole (width_px: int, height_px: int, camera_matrix_3x4: list, distortion_coeffs_1xN: list | None = None, **additional_items: Any)
Expand source code
class IntrinsicsPinhole(Intrinsics):
    def __init__(
        self,
        width_px: int,
        height_px: int,
        camera_matrix_3x4: list,
        distortion_coeffs_1xN: list | None = None,
        **additional_items: Any,
    ):
        Intrinsics.__init__(self)
        if not isinstance(width_px, int):
            raise TypeError("Argument 'width_px' must be of type 'int'")
        if not isinstance(height_px, int):
            raise TypeError("Argument 'height_px' must be of type 'int'")

        self.data["intrinsics_pinhole"] = {}
        self.data["intrinsics_pinhole"]["width_px"] = width_px
        self.data["intrinsics_pinhole"]["height_px"] = height_px
        if not isinstance(camera_matrix_3x4, list):
            raise TypeError("Argument 'camera_matrix_3x4' must be of type 'list'")

        if len(camera_matrix_3x4) != 12:
            raise ValueError("Argument 'camera_matrix_3x4' must have 12 elements")
        self.data["intrinsics_pinhole"]["camera_matrix_3x4"] = camera_matrix_3x4

        if distortion_coeffs_1xN is None:
            distortion_coeffs_1xN = []
        else:
            if not isinstance(distortion_coeffs_1xN, list):
                raise TypeError("Argument 'distortion_coeffs_1xN' must be of type 'list'")
            num_coeffs = len(distortion_coeffs_1xN)
            if not 4 <= num_coeffs <= 14:
                raise ValueError("Invalid value for argument 'num_coeffs'")
        self.data["intrinsics_pinhole"]["distortion_coeffs_1xN"] = distortion_coeffs_1xN

        if additional_items is not None:
            self.data["intrinsics_pinhole"].update(additional_items)

Ancestors

class LineReference (name: str, val: list, reference_type: ObjectDataType, coordinate_system: str | None = None, properties: dict | None = None)
Expand source code
class LineReference(GeometricReference):
    def __init__(
        self,
        name: str,
        val: list,
        reference_type: ObjectDataType,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        GeometricReference.__init__(
            self, name, val, reference_type, coordinate_system, properties
        )

Ancestors

class ObjectData (name: str, coordinate_system: str | None = None, properties: dict | None = None, type: str | None = None)
Expand source code
class ObjectData:
    # pylint: disable=redefined-builtin
    def __init__(
        self,
        name: str,
        coordinate_system: str | None = None,
        properties: dict | None = None,
        type: str | None = None,
    ):
        self.data: dict = {}
        self.type = ObjectDataType.generic
        if name is not None:
            if not isinstance(name, str):
                raise TypeError("Argument 'name' must be of type 'str'")
            self.data["name"] = name
        if coordinate_system is not None:
            if not isinstance(coordinate_system, str):
                raise TypeError("Argument 'coordinate_system' must be of type 'str'")
            self.data["coordinate_system"] = coordinate_system
        if properties is not None:
            if not isinstance(properties, dict):
                raise TypeError("Argument 'properties' must be of type 'dict'")
            self.data.update(properties)
        if type is not None:
            if not isinstance(type, str):
                raise TypeError("Argument 'type' must be of type 'str'")
            self.data["type"] = type

    def add_attribute(self, object_data: ObjectData | ObjectDataGeometry):
        if not isinstance(object_data, (ObjectData, ObjectDataGeometry)):
            raise TypeError(
                "Argument 'object_data' must be of type 'ObjectData' or "
                "'ObjectDataGeometry'"
            )

        # Creates 'attributes' if it does not exist
        self.data.setdefault("attributes", {})

        if object_data.type.name in self.data["attributes"]:
            # Find if element_data already there, if so, replace, otherwise, append
            list_aux = self.data["attributes"][object_data.type.name]
            pos_list = [
                idx
                for idx, val in enumerate(list_aux)
                if val["name"] == object_data.data["name"]
            ]
            if len(pos_list) == 0:
                # No: then, just push this new object data
                self.data["attributes"][object_data.type.name].append(object_data.data)
            else:
                # Ok, exists, so let's substitute
                pos = pos_list[0]
                self.data["attributes"][object_data.type.name][pos] = object_data.data
        else:
            self.data["attributes"][object_data.type.name] = [object_data.data]

Subclasses

Methods

def add_attribute(self, object_data: ObjectData | ObjectDataGeometry)
Expand source code
def add_attribute(self, object_data: ObjectData | ObjectDataGeometry):
    if not isinstance(object_data, (ObjectData, ObjectDataGeometry)):
        raise TypeError(
            "Argument 'object_data' must be of type 'ObjectData' or "
            "'ObjectDataGeometry'"
        )

    # Creates 'attributes' if it does not exist
    self.data.setdefault("attributes", {})

    if object_data.type.name in self.data["attributes"]:
        # Find if element_data already there, if so, replace, otherwise, append
        list_aux = self.data["attributes"][object_data.type.name]
        pos_list = [
            idx
            for idx, val in enumerate(list_aux)
            if val["name"] == object_data.data["name"]
        ]
        if len(pos_list) == 0:
            # No: then, just push this new object data
            self.data["attributes"][object_data.type.name].append(object_data.data)
        else:
            # Ok, exists, so let's substitute
            pos = pos_list[0]
            self.data["attributes"][object_data.type.name][pos] = object_data.data
    else:
        self.data["attributes"][object_data.type.name] = [object_data.data]
class ObjectDataGeometry (name: str, coordinate_system: str | None = None, properties: dict | None = None)
Expand source code
class ObjectDataGeometry(ObjectData):
    def __init__(
        self,
        name: str,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        # Calling parent class
        ObjectData.__init__(self, name, coordinate_system, properties)

Ancestors

Subclasses

class ObjectDataType (*args, **kwds)

Create a collection of name/value pairs.

Example enumeration:

>>> class Color(Enum):
...     RED = 1
...     BLUE = 2
...     GREEN = 3

Access them by:

  • attribute access::
>>> Color.RED
<Color.RED: 1>
  • value lookup:
>>> Color(1)
<Color.RED: 1>
  • name lookup:
>>> Color['RED']
<Color.RED: 1>

Enumerations can be iterated over, and know how many members they have:

>>> len(Color)
3
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.

Expand source code
class ObjectDataType(Enum):
    generic = 0
    bbox = 1
    rbbox = 2
    num = 3
    text = 4
    boolean = 5
    poly2d = 6
    poly3d = 7
    cuboid = 8
    image = 9
    mat = 10
    binary = 11
    point2d = 12
    point3d = 13
    vec = 14
    line_reference = 15
    area_reference = 16
    mesh = 17

Ancestors

  • enum.Enum

Class variables

var area_reference
var bbox
var binary
var boolean
var cuboid
var generic
var image
var line_reference
var mat
var mesh
var num
var point2d
var point3d
var poly2d
var poly3d
var rbbox
var text
var vec
class Poly2DType (*args, **kwds)

Create a collection of name/value pairs.

Example enumeration:

>>> class Color(Enum):
...     RED = 1
...     BLUE = 2
...     GREEN = 3

Access them by:

  • attribute access::
>>> Color.RED
<Color.RED: 1>
  • value lookup:
>>> Color(1)
<Color.RED: 1>
  • name lookup:
>>> Color['RED']
<Color.RED: 1>

Enumerations can be iterated over, and know how many members they have:

>>> len(Color)
3
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.

Expand source code
class Poly2DType(Enum):
    MODE_POLY2D_ABSOLUTE = 0
    # MODE_POLY2D_BBOX = 1
    # MODE_POLY2D_BBOX_DIST = 2
    # MODE_POLY2D_F8DCC = 3
    # MODE_POLY2D_RF8DCC = 4
    MODE_POLY2D_SRF6DCC = 5
    MODE_POLY2D_RS6FCC = 6

Ancestors

  • enum.Enum

Class variables

var MODE_POLY2D_ABSOLUTE
var MODE_POLY2D_RS6FCC
var MODE_POLY2D_SRF6DCC
class PoseData (val: list, t_type: TransformDataType, **additional_items: Any)

Subclass of TransformData.

Equivalent to TransformData, but intended to be used when passive rotation and translation values are provided.

Expand source code
class PoseData(TransformData):
    """
    Subclass of TransformData.

    Equivalent to TransformData, but intended to be used when passive rotation and translation
    values are provided.
    """

    def __init__(self, val: list, t_type: TransformDataType, **additional_items: Any):
        TransformData.__init__(self, val, t_type, **additional_items)

Ancestors

class StreamSync (frame_vcd: int | None = None, frame_stream: int | None = None, timestamp_ISO8601: str | None = None, frame_shift: int | None = None, **additional_items: Any)
Expand source code
class StreamSync:
    def __init__(
        self,
        frame_vcd: int | None = None,
        frame_stream: int | None = None,
        timestamp_ISO8601: str | None = None,
        frame_shift: int | None = None,
        **additional_items: Any,
    ):
        self.data: dict = {}
        self.data["sync"] = {}
        # This is the master frame at vcd (if it is None, frame_shift specifies constant shift
        self.frame_vcd = frame_vcd

        if frame_shift is not None:
            if not isinstance(frame_shift, int):
                raise TypeError("Argument 'frame_shift' must be of type 'int'")
            if not (
                frame_stream is None and timestamp_ISO8601 is None and frame_vcd is None
            ):
                raise ValueError(
                    "At least one of these arguments needs to be different than None: "
                    "'frame_stream', 'timestamp_ISO8601', 'frame_vcd'"
                )
            self.data["sync"]["frame_shift"] = frame_shift
        else:
            if not isinstance(frame_vcd, int):
                raise TypeError("Argument 'frame_vcd' must be of type 'int'")
            if frame_stream is not None:
                if not isinstance(frame_stream, int):
                    raise TypeError("Argument 'frame_stream' must be of type 'int'")
                self.data["sync"]["frame_stream"] = frame_stream
            if timestamp_ISO8601 is not None:
                if not isinstance(timestamp_ISO8601, str):
                    raise TypeError("Argument 'timestamp_ISO8601' must be of type 'str'")
                self.data["sync"]["timestamp"] = timestamp_ISO8601
        if additional_items is not None:
            self.data["sync"].update(additional_items)
class Transform (src_name: str, dst_name: str, transform_src_to_dst: TransformData, **additional_items: Any)
Expand source code
class Transform:
    def __init__(
        self,
        src_name: str,
        dst_name: str,
        transform_src_to_dst: TransformData,
        **additional_items: Any,
    ):
        if not isinstance(src_name, str):
            raise TypeError("Argument 'src_name' must be of type 'str'")
        if not isinstance(dst_name, str):
            raise TypeError("Argument 'dst_name' must be of type 'str'")
        if not isinstance(transform_src_to_dst, TransformData):
            raise TypeError(
                "Argument 'transform_src_to_dst' must be of type 'TransformData'"
            )
        self.data: dict = {}
        name = src_name + "_to_" + dst_name
        self.data[name] = {}
        transform_name: dict = self.data[name]
        self.data_additional = {}  # this is useful to append only the additional_items
        transform_name["src"] = src_name
        transform_name["dst"] = dst_name
        transform_name["transform_src_to_dst"] = transform_src_to_dst.data
        if additional_items is not None:
            transform_name.update(additional_items)
            self.data_additional.update(additional_items)
class TransformData (val: list, t_type: TransformDataType, **additional_items: Any)

Define methods to obtain different coordinate system transformation representations.

This class encodes the transform data in the form of 4x4 matrix, quaternion + translation, or Euler angles + translation.

Expand source code
class TransformData:
    """
    Define methods to obtain different coordinate system transformation representations.

    This class encodes the transform data in the form of 4x4 matrix, quaternion + translation,
    or Euler angles + translation.
    """

    def __init__(self, val: list, t_type: TransformDataType, **additional_items: Any):
        if not isinstance(val, list):
            raise TypeError("Argument 'val' must be of type 'list'")
        if not isinstance(t_type, TransformDataType):
            raise TypeError("Argument 't_type' must be of type 'TransformDataType'")

        self.data = {}
        if t_type == TransformDataType.matrix_4x4:
            self.data["matrix4x4"] = val
        elif t_type == TransformDataType.quat_and_trans_7x1:
            if len(val) != 7:
                raise ValueError(
                    "Argument 'val' for 'TransformDataType' type 'quat_and_trans_7x1' "
                    "must have 7 elements "
                )
            self.data["quaternion"] = val[0:4]
            self.data["translation"] = val[4:7]
        elif t_type == TransformDataType.euler_and_trans_6x1:
            if len(val) != 6:
                raise ValueError(
                    "Argument 'val' for 'TransformDataType' type 'euler_and_trans_6x1' "
                    "must have 7 elements "
                )
            self.data["euler_angles"] = val[0:3]
            self.data["translation"] = val[3:6]

        if additional_items is not None:
            self.data.update(additional_items)

Subclasses

class TransformDataType (*args, **kwds)

Create a collection of name/value pairs.

Example enumeration:

>>> class Color(Enum):
...     RED = 1
...     BLUE = 2
...     GREEN = 3

Access them by:

  • attribute access::
>>> Color.RED
<Color.RED: 1>
  • value lookup:
>>> Color(1)
<Color.RED: 1>
  • name lookup:
>>> Color['RED']
<Color.RED: 1>

Enumerations can be iterated over, and know how many members they have:

>>> len(Color)
3
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.

Expand source code
class TransformDataType(Enum):
    matrix_4x4 = 1
    quat_and_trans_7x1 = 2
    euler_and_trans_6x1 = 3
    custom = 4

Ancestors

  • enum.Enum

Class variables

var custom
var euler_and_trans_6x1
var matrix_4x4
var quat_and_trans_7x1
class VolumeReference (name: str, val: list, reference_type: ObjectDataType, coordinate_system: str | None = None, properties: dict | None = None)
Expand source code
class VolumeReference(GeometricReference):
    def __init__(
        self,
        name: str,
        val: list,
        reference_type: ObjectDataType,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        GeometricReference.__init__(
            self, name, val, reference_type, coordinate_system, properties
        )

Ancestors

class bbox (name: str, val: tuple | list, coordinate_system: str | None = None, properties: dict | None = None)
Expand source code
class bbox(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if len(val) != 4:
            raise TypeError("Argument 'val' must have 4 elements")
        if isinstance(val, tuple):
            self.data["val"] = val
        elif isinstance(val, list):
            self.data["val"] = tuple(val)
        self.type = ObjectDataType.bbox

Ancestors

class binary (name: str, val: str, data_type: str, encoding: str, coordinate_system: str | None = None, properties: dict | None = None)
Expand source code
class binary(ObjectData):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: str,
        data_type: str,
        encoding: str,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectData.__init__(self, name, coordinate_system, properties, None)
        if not isinstance(val, str):
            raise TypeError("Argument 'val' must be of type 'str'")
        if not isinstance(data_type, str):
            raise TypeError("Argument 'dataType' must be of type 'str'")
        if not isinstance(encoding, str):
            raise TypeError("Argument 'encoding' must be of type 'str'")
        self.data["val"] = val
        self.data["data_type"] = data_type
        self.data["encoding"] = encoding
        self.type = ObjectDataType.binary

Ancestors

class boolean (name: str, val: bool, coordinate_system: str | None = None, properties: dict | None = None, type: str | None = None)
Expand source code
class boolean(ObjectData):  # noqa: N801  # remove capitals in class name warning
    # pylint: disable=redefined-builtin
    def __init__(
        self,
        name: str,
        val: bool,
        coordinate_system: str | None = None,
        properties: dict | None = None,
        type: str | None = None,
    ):
        ObjectData.__init__(self, name, coordinate_system, properties, type)
        if not isinstance(val, bool):
            raise TypeError("Argument 'val' must be of type 'bool'")
        self.data["val"] = val
        self.type = ObjectDataType.boolean

Ancestors

class cuboid (name: str, val: tuple | list, coordinate_system: str | None = None, properties: dict | None = None)
Expand source code
class cuboid(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if val is not None:
            if not isinstance(val, (tuple, list)):
                raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
            if len(val) not in (9, 10):
                raise ValueError("Argument 'val' must have 9 or 10 elements")
            if len(val) == 9:
                self.use_quaternion = False
            else:
                self.use_quaternion = True
        if isinstance(val, tuple):
            self.data["val"] = list(val)
        elif isinstance(val, list):
            self.data["val"] = val
        else:
            self.data["val"] = None
        self.type = ObjectDataType.cuboid

Ancestors

class image (name: str, val: str, mime_type: str, encoding: str, coordinate_system: str | None = None, properties: dict | None = None)

Host the image data in buffer format.

It can be used with any codification, although base64 and webp are suggested.

mimeType: "image/png", "image/jpeg", .. as in https://www.sitepoint.com/mime-types-complete-list/ encoding: "base64", "ascii", .. as in https://docs.python.org/2.4/lib/standard-encodings.html

Default is base64

OpenCV can be used to encode: img = cv2.imread(file_name, 1) compr_params = [int(cv2.IMWRITE_PNG_COMPRESSION), 9] result, payload = cv2.imencode('.png', img, compr_params)

Expand source code
class image(ObjectData):  # noqa: N801  # remove capitals in class name warning
    """
    Host the image data in buffer format.

    It can be used with any codification, although base64 and webp are suggested.

    mimeType: "image/png", "image/jpeg", .. as in
                                https://www.sitepoint.com/mime-types-complete-list/
    encoding: "base64", "ascii", .. as in
                            https://docs.python.org/2.4/lib/standard-encodings.html

    Default is base64

    OpenCV can be used to encode:
    img = cv2.imread(file_name, 1)
    compr_params = [int(cv2.IMWRITE_PNG_COMPRESSION), 9]
    result, payload = cv2.imencode('.png', img, compr_params)
    """

    def __init__(
        self,
        name: str,
        val: str,
        mime_type: str,
        encoding: str,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectData.__init__(self, name, coordinate_system, properties, None)
        if not isinstance(val, str):
            raise TypeError("Argument 'val' must be of type 'str'")
        if not isinstance(mime_type, str):
            raise TypeError("Argument 'mime_type' must be of type 'str'")
        if not isinstance(encoding, str):
            raise TypeError("Argument 'encoding' must be of type 'str'")
        self.data["val"] = val
        self.data["mime_type"] = mime_type
        self.data["encoding"] = encoding
        self.type = ObjectDataType.image

Ancestors

class mat (name: str, val: tuple | list, channels: int, width: int, height: int, data_type: str, coordinate_system: str | None = None, properties: dict | None = None)
Expand source code
class mat(ObjectData):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        channels: int,
        width: int,
        height: int,
        data_type: str,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectData.__init__(self, name, coordinate_system, properties, None)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if not isinstance(width, int):
            raise TypeError("Argument 'width' must be of type 'int'")
        if not isinstance(height, int):
            raise TypeError("Argument 'height' must be of type 'int'")
        if not isinstance(channels, int):
            raise TypeError("Argument 'channels' must be of type 'int'")
        if not isinstance(data_type, str):
            raise TypeError("Argument 'dataType' must be of type 'str'")
        if len(val) != width * height * channels:
            raise ValueError("len(val) != width * height * channels")

        if isinstance(val, tuple):
            self.data["val"] = val
        elif isinstance(val, list):
            self.data["val"] = tuple(val)
        self.data["channels"] = channels
        self.data["width"] = width
        self.data["height"] = height
        self.data["data_type"] = data_type
        self.type = ObjectDataType.mat

Ancestors

class mesh (name: str, coordinate_system: str | None = None, properties: dict | None = None)
Expand source code
class mesh(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        self.pid = "0"
        self.eid = "0"
        self.aid = "0"
        self.vid = "0"
        self.data["point3d"] = {}
        self.data["line_reference"] = {}
        self.data["area_reference"] = {}
        self.type = ObjectDataType.mesh

    # Vertex
    def add_vertex(
        self, p3d: point3d, id: str | None = None  # pylint: disable=redefined-builtin
    ) -> str:
        if not isinstance(p3d, point3d):
            raise TypeError("Argument 'p3d' must be of type ' point3d'")

        # If an id is provided use it
        if id is not None:
            # If it already exists, this is an update call
            if id in self.data["point3d"]:
                # The id already exists: substitute
                idx = id
            else:
                idx = id
                self.pid = str(int(idx) + 1)
        else:
            idx = self.pid
            self.pid = str(int(self.pid) + 1)

        self.data.setdefault("point3d", {})
        self.data["point3d"][idx] = p3d.data
        return idx

    # Edge
    def add_edge(
        self,
        lref: LineReference,
        id: str | None = None,  # pylint: disable=redefined-builtin
    ) -> str:
        if not isinstance(lref, LineReference):
            raise TypeError("Argument 'lref' must be of type ' LineReference'")

        if id is not None:
            if id in self.data["line_reference"]:
                idx = id
            else:
                idx = id
                self.eid = str(int(idx) + 1)
        else:
            idx = self.eid
            self.eid = str(int(self.eid) + 1)

        self.data.setdefault("line_reference", {})
        self.data["line_reference"][idx] = lref.data
        return idx

    # Area
    def add_area(
        self,
        aref: AreaReference,
        id: str | None = None,  # pylint: disable=redefined-builtin
    ) -> str:
        if not isinstance(aref, AreaReference):
            raise TypeError("Argument 'aref' must be of type ' AreaReference'")

        if id is not None:
            if id in self.data["area_reference"]:
                idx = id
            else:
                idx = id
                self.aid = str(int(idx) + 1)
        else:
            idx = self.aid
            self.aid = str(int(self.aid) + 1)

        self.data.setdefault("area_reference", {})
        self.data["area_reference"][idx] = aref.data
        return idx

    def get_mesh_geometry_as_string(self) -> str:
        result = "[["
        for vertex in self.data["point3d"].values():
            val = vertex["val"]
            result += "["
            for _, elem in enumerate(val):
                result += str(elem) + ","
            result += "],"
        result += "],["

        for edge in self.data["line_reference"].values():
            val = edge["val"]
            result += "["
            for _, elem in enumerate(val):
                result += str(elem) + ","
            result += "],"
        result += "],["

        for area in self.data["area_reference"].values():
            val = area["val"]
            result += "["
            for _, elem in enumerate(val):
                result += str(elem) + ","
            result += "],"
        result += "]]"

        # Clean-out commas
        result = result.replace(",]", "]")

        return result

Ancestors

Methods

def add_area(self, aref: AreaReference, id: str | None = None) ‑> str
Expand source code
def add_area(
    self,
    aref: AreaReference,
    id: str | None = None,  # pylint: disable=redefined-builtin
) -> str:
    if not isinstance(aref, AreaReference):
        raise TypeError("Argument 'aref' must be of type ' AreaReference'")

    if id is not None:
        if id in self.data["area_reference"]:
            idx = id
        else:
            idx = id
            self.aid = str(int(idx) + 1)
    else:
        idx = self.aid
        self.aid = str(int(self.aid) + 1)

    self.data.setdefault("area_reference", {})
    self.data["area_reference"][idx] = aref.data
    return idx
def add_edge(self, lref: LineReference, id: str | None = None) ‑> str
Expand source code
def add_edge(
    self,
    lref: LineReference,
    id: str | None = None,  # pylint: disable=redefined-builtin
) -> str:
    if not isinstance(lref, LineReference):
        raise TypeError("Argument 'lref' must be of type ' LineReference'")

    if id is not None:
        if id in self.data["line_reference"]:
            idx = id
        else:
            idx = id
            self.eid = str(int(idx) + 1)
    else:
        idx = self.eid
        self.eid = str(int(self.eid) + 1)

    self.data.setdefault("line_reference", {})
    self.data["line_reference"][idx] = lref.data
    return idx
def add_vertex(self, p3d: point3d, id: str | None = None) ‑> str
Expand source code
def add_vertex(
    self, p3d: point3d, id: str | None = None  # pylint: disable=redefined-builtin
) -> str:
    if not isinstance(p3d, point3d):
        raise TypeError("Argument 'p3d' must be of type ' point3d'")

    # If an id is provided use it
    if id is not None:
        # If it already exists, this is an update call
        if id in self.data["point3d"]:
            # The id already exists: substitute
            idx = id
        else:
            idx = id
            self.pid = str(int(idx) + 1)
    else:
        idx = self.pid
        self.pid = str(int(self.pid) + 1)

    self.data.setdefault("point3d", {})
    self.data["point3d"][idx] = p3d.data
    return idx
def get_mesh_geometry_as_string(self) ‑> str
Expand source code
def get_mesh_geometry_as_string(self) -> str:
    result = "[["
    for vertex in self.data["point3d"].values():
        val = vertex["val"]
        result += "["
        for _, elem in enumerate(val):
            result += str(elem) + ","
        result += "],"
    result += "],["

    for edge in self.data["line_reference"].values():
        val = edge["val"]
        result += "["
        for _, elem in enumerate(val):
            result += str(elem) + ","
        result += "],"
    result += "],["

    for area in self.data["area_reference"].values():
        val = area["val"]
        result += "["
        for _, elem in enumerate(val):
            result += str(elem) + ","
        result += "],"
    result += "]]"

    # Clean-out commas
    result = result.replace(",]", "]")

    return result
class num (name: str, val: int | float, coordinate_system: str | None = None, properties: dict | None = None, type: str | None = None)
Expand source code
class num(ObjectData):  # noqa: N801  # remove capitals in class name warning
    # pylint: disable=redefined-builtin
    def __init__(
        self,
        name: str,
        val: int | float,
        coordinate_system: str | None = None,
        properties: dict | None = None,
        type: str | None = None,
    ):
        ObjectData.__init__(self, name, coordinate_system, properties, type)
        if not isinstance(val, (int, float)):
            raise TypeError("Argument 'val' must be of type 'int' or 'float'")
        self.data["val"] = val
        self.type = ObjectDataType.num

Ancestors

class point2d (name: str, val: tuple | list, point_id: int | None = None, coordinate_system: str | None = None, properties: dict | None = None)
Expand source code
class point2d(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        point_id: int | None = None,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if len(val) != 2:
            raise ValueError("Argument 'val' must have 2 elements")
        if isinstance(val, tuple):
            self.data["val"] = val
        elif isinstance(val, list):
            self.data["val"] = tuple(val)
        if point_id is not None:
            if not isinstance(point_id, int):
                raise TypeError("Argument 'point_id' must be of type 'int'")
            self.data["id"] = point_id
        self.type = ObjectDataType.point2d

Ancestors

class point3d (name: str, val: tuple | list, point_id: int | None = None, coordinate_system: str | None = None, properties: dict | None = None)
Expand source code
class point3d(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        point_id: int | None = None,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if len(val) != 3:
            raise ValueError("Argument 'val' must have 3 elements")

        if isinstance(val, tuple):
            self.data["val"] = val
        elif isinstance(val, list):
            self.data["val"] = tuple(val)
        if point_id is not None:
            if not isinstance(point_id, int):
                raise TypeError("Argument 'id' must be of type 'int'")
            self.data["id"] = point_id
        self.type = ObjectDataType.point3d

Ancestors

class poly2d (name: str, val: tuple | list, mode: Poly2DType, closed: bool, hierarchy: list | None = None, coordinate_system: str | None = None, properties: dict | None = None)
Expand source code
class poly2d(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        mode: Poly2DType,
        closed: bool,
        hierarchy: list | None = None,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if not isinstance(mode, Poly2DType):
            raise TypeError("Argument 'mode' must be of type 'Poly2DType'")
        if not isinstance(closed, bool):
            raise TypeError("Argument 'closed' must be of type 'bool'")
        if isinstance(val, (list, tuple)):
            if mode == Poly2DType.MODE_POLY2D_SRF6DCC:
                srf6, xinit, yinit = poly.compute_srf6dcc(val)
                encoded_poly, rest = poly.chaincode_base64_encoder(srf6, 3)
                self.data["val"] = [str(xinit), str(yinit), str(rest), encoded_poly]
            elif mode == Poly2DType.MODE_POLY2D_RS6FCC:
                rs6, low, high, xinit, yinit = poly.compute_rs6fcc(val)
                encoded_poly, rest = poly.chaincode_base64_encoder(rs6, 3)
                self.data["val"] = [
                    str(xinit),
                    str(yinit),
                    str(low),
                    str(high),
                    str(rest),
                    encoded_poly,
                ]
            else:
                self.data["val"] = list(val)
        self.data["mode"] = mode.name
        self.data["closed"] = closed
        self.type = ObjectDataType.poly2d
        if hierarchy is not None:
            if not isinstance(hierarchy, list):
                raise TypeError("Argument 'hierarchy' must be of type 'list'")
            if not all(isinstance(x, int) for x in hierarchy):
                raise TypeError(
                    "All values of argument 'hierarchy' must be of type 'int'"
                )
            self.data["hierarchy"] = hierarchy

Ancestors

class poly3d (name: str, val: tuple | list, closed: bool, coordinate_system: str | None = None, properties: dict | None = None)
Expand source code
class poly3d(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        closed: bool,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if not isinstance(closed, bool):
            raise TypeError("Argument 'closed' must be of type 'bool'")
        if isinstance(val, tuple):
            self.data["val"] = val
        elif isinstance(val, list):
            self.data["val"] = tuple(val)
        self.data["closed"] = closed
        self.type = ObjectDataType.poly3d

Ancestors

class rbbox (name: str, val: tuple | list, coordinate_system: str | None = None, properties: dict | None = None)
Expand source code
class rbbox(ObjectDataGeometry):  # noqa: N801  # remove capitals in class name warning
    def __init__(
        self,
        name: str,
        val: tuple | list,
        coordinate_system: str | None = None,
        properties: dict | None = None,
    ):
        ObjectDataGeometry.__init__(self, name, coordinate_system, properties)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if len(val) != 5:
            raise TypeError("Argument 'val' must have 5 elements")
        if isinstance(val, tuple):
            self.data["val"] = val
        elif isinstance(val, list):
            self.data["val"] = tuple(val)
        self.type = ObjectDataType.rbbox

Ancestors

class text (name: str, val: str, coordinate_system: str | None = None, properties: dict | None = None, type: str | None = None)
Expand source code
class text(ObjectData):  # noqa: N801  # remove capitals in class name warning
    # pylint: disable=redefined-builtin
    def __init__(
        self,
        name: str,
        val: str,
        coordinate_system: str | None = None,
        properties: dict | None = None,
        type: str | None = None,
    ):
        ObjectData.__init__(self, name, coordinate_system, properties, type)
        if not isinstance(val, str):
            raise TypeError("Argument 'val' must be of type 'str'")
        self.data["val"] = val
        self.type = ObjectDataType.text

Ancestors

class vec (name: str, val: tuple | list, coordinate_system: str | None = None, properties: dict | None = None, type: str | None = None)
Expand source code
class vec(ObjectData):  # noqa: N801  # remove capitals in class name warning
    # pylint: disable=redefined-builtin
    def __init__(
        self,
        name: str,
        val: tuple | list,
        coordinate_system: str | None = None,
        properties: dict | None = None,
        type: str | None = None,
    ):
        ObjectData.__init__(self, name, coordinate_system, properties, type)
        if not isinstance(val, (tuple, list)):
            raise TypeError("Argument 'val' must be of type 'tuple' or 'list'")
        if isinstance(val, tuple):
            self.data["val"] = val
        elif isinstance(val, list):
            self.data["val"] = tuple(val)
        self.type = ObjectDataType.vec

Ancestors