Source code for fastkml.model

# Copyright (C) 2024 Christian Ledermann
#
# This library is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 2.1 of the License, or (at your option)
# any later version.
#
# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
"""
Model element.

The Model element defines a 3D model that is attached to a Placemark.

https://developers.google.com/kml/documentation/models
https://developers.google.com/kml/documentation/kmlreference#model

"""

from collections.abc import Iterable
from typing import Any

from pygeoif.geometry import Point

from fastkml import config
from fastkml.enums import AltitudeMode
from fastkml.helpers import clean_string
from fastkml.helpers import enum_subelement
from fastkml.helpers import float_subelement
from fastkml.helpers import subelement_enum_kwarg
from fastkml.helpers import subelement_float_kwarg
from fastkml.helpers import subelement_text_kwarg
from fastkml.helpers import text_subelement
from fastkml.helpers import xml_subelement
from fastkml.helpers import xml_subelement_kwarg
from fastkml.helpers import xml_subelement_list
from fastkml.helpers import xml_subelement_list_kwarg
from fastkml.kml_base import _BaseObject
from fastkml.links import Link
from fastkml.registry import RegistryItem
from fastkml.registry import registry

__all__ = ["Alias", "Location", "Model", "Orientation", "ResourceMap", "Scale"]


[docs] class Location(_BaseObject): """Represents a location in KML.""" _default_nsid = config.KML latitude: float | None longitude: float | None altitude: float | None def __init__( self, ns: str | None = None, name_spaces: dict[str, str] | None = None, id: str | None = None, target_id: str | None = None, altitude: float | None = None, latitude: float | None = None, longitude: float | None = None, **kwargs: Any, ) -> None: """Create a new Location.""" super().__init__( ns=ns, name_spaces=name_spaces, id=id, target_id=target_id, **kwargs, ) self.altitude = altitude self.latitude = latitude self.longitude = longitude def __bool__(self) -> bool: """Return True if latitude and longitude are set.""" return all((self.latitude is not None, self.longitude is not None)) def __repr__(self) -> str: """Create a string (c)representation for Location.""" return ( f"{self.__class__.__module__}.{self.__class__.__name__}(" f"ns={self.ns!r}, " f"name_spaces={self.name_spaces!r}, " f"id={self.id!r}, " f"target_id={self.target_id!r}, " f"altitude={self.altitude!r}, " f"latitude={self.latitude!r}, " f"longitude={self.longitude!r}, " f"**{self._get_splat()!r}," ")" ) @property def geometry(self) -> Point | None: """Return a Point representation of the geometry.""" if not self: return None assert self.longitude is not None # noqa: S101 assert self.latitude is not None # noqa: S101 return Point(self.longitude, self.latitude, self.altitude)
registry.register( Location, RegistryItem( ns_ids=("kml", ""), attr_name="longitude", node_name="longitude", classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, ), ) registry.register( Location, RegistryItem( ns_ids=("kml", ""), attr_name="latitude", node_name="latitude", classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, ), ) registry.register( Location, RegistryItem( ns_ids=("kml", ""), attr_name="altitude", node_name="altitude", classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, default=0.0, ), )
[docs] class Orientation(_BaseObject): """Represents an orientation in KML.""" _default_nsid = config.KML heading: float | None tilt: float | None roll: float | None def __init__( self, ns: str | None = None, name_spaces: dict[str, str] | None = None, id: str | None = None, target_id: str | None = None, heading: float | None = None, tilt: float | None = None, roll: float | None = None, **kwargs: Any, ) -> None: """Create a new Orientation.""" super().__init__( ns=ns, name_spaces=name_spaces, id=id, target_id=target_id, **kwargs, ) self.heading = heading self.tilt = tilt self.roll = roll def __bool__(self) -> bool: """Return True if heading, tilt, or roll are set.""" return any( (self.heading is not None, self.tilt is not None, self.roll is not None), ) def __repr__(self) -> str: """Create a string (c)representation for Orientation.""" return ( f"{self.__class__.__module__}.{self.__class__.__name__}(" f"ns={self.ns!r}, " f"name_spaces={self.name_spaces!r}, " f"id={self.id!r}, " f"target_id={self.target_id!r}, " f"heading={self.heading!r}, " f"tilt={self.tilt!r}, " f"roll={self.roll!r}, " f"**{self._get_splat()!r}," ")" )
registry.register( Orientation, RegistryItem( ns_ids=("kml", ""), attr_name="heading", node_name="heading", classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, default=0.0, ), ) registry.register( Orientation, RegistryItem( ns_ids=("kml", ""), attr_name="tilt", node_name="tilt", classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, default=0.0, ), ) registry.register( Orientation, RegistryItem( ns_ids=("kml", ""), attr_name="roll", node_name="roll", classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, default=0.0, ), )
[docs] class Scale(_BaseObject): """Represents a scale in KML.""" _default_nsid = config.KML x: float | None y: float | None z: float | None def __init__( self, ns: str | None = None, name_spaces: dict[str, str] | None = None, id: str | None = None, target_id: str | None = None, x: float | None = None, y: float | None = None, z: float | None = None, **kwargs: Any, ) -> None: """Create a new Scale.""" super().__init__( ns=ns, name_spaces=name_spaces, id=id, target_id=target_id, **kwargs, ) self.x = x self.y = y self.z = z def __bool__(self) -> bool: """Return True if x, y, or z are set.""" return any((self.x is not None, self.y is not None, self.z is not None)) def __repr__(self) -> str: """Create a string (c)representation for Scale.""" return ( f"{self.__class__.__module__}.{self.__class__.__name__}(" f"ns={self.ns!r}, " f"name_spaces={self.name_spaces!r}, " f"id={self.id!r}, " f"target_id={self.target_id!r}, " f"x={self.x!r}, " f"y={self.y!r}, " f"z={self.z!r}, " f"**{self._get_splat()!r}," ")" )
registry.register( Scale, RegistryItem( ns_ids=("kml", ""), attr_name="x", node_name="x", classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, default=1.0, ), ) registry.register( Scale, RegistryItem( ns_ids=("kml", ""), attr_name="y", node_name="y", classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, default=1.0, ), ) registry.register( Scale, RegistryItem( ns_ids=("kml", ""), attr_name="z", node_name="z", classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, default=1.0, ), )
[docs] class Alias(_BaseObject): """Represents an alias in KML.""" _default_nsid = config.KML target_href: str | None source_href: str | None def __init__( self, ns: str | None = None, name_spaces: dict[str, str] | None = None, id: str | None = None, target_id: str | None = None, target_href: str | None = None, source_href: str | None = None, **kwargs: Any, ) -> None: """Create a new Alias.""" super().__init__( ns=ns, name_spaces=name_spaces, id=id, target_id=target_id, **kwargs, ) self.target_href = clean_string(target_href) self.source_href = clean_string(source_href) def __bool__(self) -> bool: """Return True if target_href or source_href are set.""" return any((self.target_href is not None, self.source_href is not None)) def __repr__(self) -> str: """Create a string (c)representation for Alias.""" return ( f"{self.__class__.__module__}.{self.__class__.__name__}(" f"ns={self.ns!r}, " f"name_spaces={self.name_spaces!r}, " f"id={self.id!r}, " f"target_id={self.target_id!r}, " f"target_href={self.target_href!r}, " f"source_href={self.source_href!r}, " f"**{self._get_splat()!r}," ")" )
registry.register( Alias, RegistryItem( ns_ids=("kml", ""), attr_name="target_href", node_name="targetHref", classes=(str,), get_kwarg=subelement_text_kwarg, set_element=text_subelement, ), ) registry.register( Alias, RegistryItem( ns_ids=("kml", ""), attr_name="source_href", node_name="sourceHref", classes=(str,), get_kwarg=subelement_text_kwarg, set_element=text_subelement, ), )
[docs] class ResourceMap(_BaseObject): """Represents a resource map in KML.""" _default_nsid = config.KML aliases: list[Alias] def __init__( self, ns: str | None = None, name_spaces: dict[str, str] | None = None, id: str | None = None, target_id: str | None = None, aliases: Iterable[Alias] | None = None, **kwargs: Any, ) -> None: """Create a new ResourceMap.""" super().__init__( ns=ns, name_spaces=name_spaces, id=id, target_id=target_id, **kwargs, ) self.aliases = list(aliases) if aliases is not None else [] def __bool__(self) -> bool: """Return True if aliases are set.""" return bool(self.aliases) def __repr__(self) -> str: """Create a string (c)representation for ResourceMap.""" return ( f"{self.__class__.__module__}.{self.__class__.__name__}(" f"ns={self.ns!r}, " f"name_spaces={self.name_spaces!r}, " f"id={self.id!r}, " f"target_id={self.target_id!r}, " f"aliases={self.aliases!r}, " f"**{self._get_splat()!r}," ")" )
registry.register( ResourceMap, RegistryItem( ns_ids=("kml", ""), attr_name="aliases", node_name="Alias", classes=(Alias,), get_kwarg=xml_subelement_list_kwarg, set_element=xml_subelement_list, ), )
[docs] class Model(_BaseObject): """Represents a model in KML.""" altitude_mode: AltitudeMode | None location: Location | None orientation: Orientation | None scale: Scale | None link: Link | None resource_map: ResourceMap | None def __init__( self, ns: str | None = None, name_spaces: dict[str, str] | None = None, id: str | None = None, target_id: str | None = None, altitude_mode: AltitudeMode | None = None, location: Location | None = None, orientation: Orientation | None = None, scale: Scale | None = None, link: Link | None = None, resource_map: ResourceMap | None = None, **kwargs: Any, ) -> None: """Create a new Model.""" super().__init__( ns=ns, name_spaces=name_spaces, id=id, target_id=target_id, **kwargs, ) self.altitude_mode = altitude_mode self.location = location self.orientation = orientation self.scale = scale self.link = link self.resource_map = resource_map def __bool__(self) -> bool: """Return True if link and location are set.""" return all((self.link, self.location)) def __repr__(self) -> str: """Create a string representation for Model.""" return ( f"{self.__class__.__module__}.{self.__class__.__name__}(" f"ns={self.ns!r}, " f"name_spaces={self.name_spaces!r}, " f"id={self.id!r}, " f"target_id={self.target_id!r}, " f"altitude_mode={self.altitude_mode}, " f"location={self.location!r}, " f"orientation={self.orientation!r}, " f"scale={self.scale!r}, " f"link={self.link!r}, " f"resource_map={self.resource_map!r}, " f"**{self._get_splat()!r}," ")" ) @property def geometry(self) -> Point | None: """Return a Point representation of the geometry.""" return self.location.geometry if self.location else None
registry.register( Model, RegistryItem( ns_ids=("kml", "gx", ""), attr_name="altitude_mode", node_name="altitudeMode", classes=(AltitudeMode,), get_kwarg=subelement_enum_kwarg, set_element=enum_subelement, default=AltitudeMode.clamp_to_ground, ), ) registry.register( Model, RegistryItem( ns_ids=("kml", ""), attr_name="location", node_name="Location", classes=(Location,), get_kwarg=xml_subelement_kwarg, set_element=xml_subelement, ), ) registry.register( Model, RegistryItem( ns_ids=("kml", ""), attr_name="orientation", node_name="Orientation", classes=(Orientation,), get_kwarg=xml_subelement_kwarg, set_element=xml_subelement, ), ) registry.register( Model, RegistryItem( ns_ids=("kml", ""), attr_name="scale", node_name="Scale", classes=(Scale,), get_kwarg=xml_subelement_kwarg, set_element=xml_subelement, ), ) registry.register( Model, RegistryItem( ns_ids=("kml", ""), attr_name="link", node_name="Link", classes=(Link,), get_kwarg=xml_subelement_kwarg, set_element=xml_subelement, ), ) registry.register( Model, RegistryItem( ns_ids=("kml", ""), attr_name="resource_map", node_name="ResourceMap", classes=(ResourceMap,), get_kwarg=xml_subelement_kwarg, set_element=xml_subelement, ), )