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 typing import Optional

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: Optional[float] longitude: Optional[float] altitude: Optional[float] def __init__( self, ns: Optional[str] = None, name_spaces: Optional[dict[str, str]] = None, id: Optional[str] = None, target_id: Optional[str] = None, altitude: Optional[float] = None, latitude: Optional[float] = None, longitude: Optional[float] = 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) -> Optional[Point]: """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: Optional[float] tilt: Optional[float] roll: Optional[float] def __init__( self, ns: Optional[str] = None, name_spaces: Optional[dict[str, str]] = None, id: Optional[str] = None, target_id: Optional[str] = None, heading: Optional[float] = None, tilt: Optional[float] = None, roll: Optional[float] = 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: Optional[float] y: Optional[float] z: Optional[float] def __init__( self, ns: Optional[str] = None, name_spaces: Optional[dict[str, str]] = None, id: Optional[str] = None, target_id: Optional[str] = None, x: Optional[float] = None, y: Optional[float] = None, z: Optional[float] = 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: Optional[str] source_href: Optional[str] def __init__( self, ns: Optional[str] = None, name_spaces: Optional[dict[str, str]] = None, id: Optional[str] = None, target_id: Optional[str] = None, target_href: Optional[str] = None, source_href: Optional[str] = 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: Optional[str] = None, name_spaces: Optional[dict[str, str]] = None, id: Optional[str] = None, target_id: Optional[str] = None, aliases: Optional[Iterable[Alias]] = 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: Optional[AltitudeMode] location: Optional[Location] orientation: Optional[Orientation] scale: Optional[Scale] link: Optional[Link] resource_map: Optional[ResourceMap] def __init__( self, ns: Optional[str] = None, name_spaces: Optional[dict[str, str]] = None, id: Optional[str] = None, target_id: Optional[str] = None, altitude_mode: Optional[AltitudeMode] = None, location: Optional[Location] = None, orientation: Optional[Orientation] = None, scale: Optional[Scale] = None, link: Optional[Link] = None, resource_map: Optional[ResourceMap] = 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) -> Optional[Point]: """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, ), )