From b81c6bfeaf10933c5b7200188cefe8f086f7f7c0 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Tue, 19 Nov 2024 09:36:19 +0000 Subject: [PATCH 01/58] document set_element --- fastkml/registry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fastkml/registry.py b/fastkml/registry.py index 96e53bb1..a2a5957e 100644 --- a/fastkml/registry.py +++ b/fastkml/registry.py @@ -89,6 +89,7 @@ class RegistryItem: - ``attr_name``: The name of the attribute on the Python object that corresponds to the XML object. - ``get_kwarg``: A function that retrieves keyword arguments for the Python object. + - ``set_element``: A function that sets the XML element for the Python object. - ``type``: The type of the XML object. - ``node_name``: The name of the XML node that the mapping applies to. - ``default``: An optional default value for the Python object attribute. From 7b3fc655edee42257b9ba8ca9f439d2c81cb0e12 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Tue, 19 Nov 2024 09:38:39 +0000 Subject: [PATCH 02/58] update version number --- fastkml/about.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastkml/about.py b/fastkml/about.py index ce656dad..aab34882 100644 --- a/fastkml/about.py +++ b/fastkml/about.py @@ -19,7 +19,7 @@ The only purpose of this module is to provide a version number for the package. """ -__version__ = "1.0.0" +__version__ = "1.0.0dev0" """Fastkml version number.""" __all__ = ["__version__"] From 3ad0d4b11e2a474dcee45257d2d01954f4963d98 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Tue, 19 Nov 2024 09:45:24 +0000 Subject: [PATCH 03/58] update history --- docs/HISTORY.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/HISTORY.rst b/docs/HISTORY.rst index 4d742416..01c2cb1d 100644 --- a/docs/HISTORY.rst +++ b/docs/HISTORY.rst @@ -1,7 +1,11 @@ Changelog ========= -1.0 (unreleased) +1.0.0dev0 (unreleased) +---------------------- + + +1.0 (2024/11/19) ----------------- - Drop Python 2 support From 2fc043dc51441e57089c08acf126d7ac08236158 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Tue, 19 Nov 2024 09:56:59 +0000 Subject: [PATCH 04/58] remove ignored linting rules from pyproject.toml --- pyproject.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c525ae04..d7c75537 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -191,8 +191,6 @@ target-version = "py38" [tool.ruff.lint] ignore = [ "A002", - "ANN101", - "ANN102", "ANN401", "D203", "D212", From 16a4015f81e607cdf4639ab9ad20b8a6555c0926 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Tue, 19 Nov 2024 18:08:59 +0000 Subject: [PATCH 05/58] add additional classifiers to pyproject.toml for better package categorization --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index d7c75537..50f8d47d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,11 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Scientific/Engineering :: GIS", + "Topic :: Software Development :: Libraries", + "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Text Processing :: Markup :: XML", "Typing :: Typed", ] From 164c9a85df752418d0fe55af8b5862033dbd4b6c Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Wed, 20 Nov 2024 17:15:54 +0000 Subject: [PATCH 06/58] add notes for reading and saving KML files in quickstart documentation --- docs/quickstart.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 34b2355e..7bf738f8 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -141,6 +141,17 @@ Read in the KML string >>> k = kml.KML.from_string(doc) + +.. note:: + + To read a KML file directly, you can use the parse method: + + .. code-block:: Python + + k = kml.KML.parse("path/to/file.kml") + + + Next we perform some simple sanity checks, such as checking the number of features. .. code-block:: pycon @@ -206,3 +217,11 @@ Finally, print out the KML object as a string: + +.. note:: + + To save the KML object to a file, you can use the write method: + + .. code-block:: Python + + k.write("path/to/file.kml") From a6b5087e5eb75c32e719d7907e88d707439cdbcf Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Thu, 21 Nov 2024 09:58:16 +0000 Subject: [PATCH 07/58] add Conda-Forge badge to README for better visibility --- README.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 3fe99453..34ae1140 100644 --- a/README.rst +++ b/README.rst @@ -50,7 +50,7 @@ Fastkml is continually tested Is Maintained and documented: -|pypi| |status| |license| |doc| |stats| |pyversion| |pyimpl| |dependencies| |downloads| +|pypi| |conda-forge| |status| |license| |doc| |stats| |pyversion| |pyimpl| |dependencies| |downloads| .. |pypi| image:: https://img.shields.io/pypi/v/fastkml.svg :target: https://pypi.python.org/pypi/fastkml @@ -88,6 +88,9 @@ Is Maintained and documented: :target: https://pepy.tech/project/fastkml :alt: Downloads +.. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/fastkml.svg + :target: https://anaconda.org/conda-forge/fastkml + :alt: Conda-Forge Documentation ============= From e8dd1d061114080f94949966f09a1ca611894e14 Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Fri, 22 Nov 2024 13:55:24 -0500 Subject: [PATCH 08/58] Network LInk Control --- fastkml/__init__.py | 1 + fastkml/containers.py | 87 ++++++++++++++++++++++++++++++ fastkml/features.py | 121 +++++++++++++++++++++++++++++++++++++++++- fastkml/kml.py | 7 +-- fastkml/times.py | 5 ++ fastkml/update.py | 36 +++++++++++++ 6 files changed, 253 insertions(+), 4 deletions(-) create mode 100644 fastkml/update.py diff --git a/fastkml/__init__.py b/fastkml/__init__.py index b607e08f..38a11805 100644 --- a/fastkml/__init__.py +++ b/fastkml/__init__.py @@ -61,6 +61,7 @@ __all__ = [ "KML", + "NetworkLinkControl" "Document", "Folder", "GroundOverlay", diff --git a/fastkml/containers.py b/fastkml/containers.py index 5041d219..40acd84f 100644 --- a/fastkml/containers.py +++ b/fastkml/containers.py @@ -47,9 +47,12 @@ from fastkml.styles import StyleMap from fastkml.styles import StyleUrl from fastkml.times import TimeSpan +from fastkml.times import KmlDateTime from fastkml.times import TimeStamp +from fastkml.update import Update from fastkml.utils import find_all from fastkml.views import Camera +from fastkml.views import _AbstractView from fastkml.views import LookAt from fastkml.views import Region @@ -323,6 +326,79 @@ def get_style_by_url(self, style_url: str) -> Optional[Union[Style, StyleMap]]: ) +class NetworkLinkControl(_Container): + + schemata: List[Schema] + + def __init__( + self, + ns: Optional[str] = None, + name_spaces: Optional[Dict[str, str]] = None, + id: Optional[str] = None, + target_id: Optional[str] = None, + min_refresh_period: Optional[float] = None, + max_session_length: Optional[float] = None, + cookie: Optional[str] = None, + message: Optional[str] = None, + link_name: Optional[str] = None, + link_description: Optional[str] = None, + link_snippet: Optional[str] = None, + expires: Optional[KmlDateTime] = None, + update:Optional[Update] = None, + view: Optional[Union[Camera, LookAt]] = None, + schemata: Optional[Iterable[Schema]] = None, + **kwargs: Any + ) -> None: + super().__init__( + ns=ns, + name_spaces=name_spaces, + id=id, + target_id=target_id, + min_refresh_period=min_refresh_period, + max_session_length=max_session_length, + cookie=cookie, + message=message, + link_name=link_name, + link_description=link_description, + link_snippet=link_snippet, + expires=expires, + update=update, + view=view, + **kwargs + ) + self.schemata = list(schemata) if schemata else [] + + def __repr__(self) -> str: + """ + Return a string representation of the NetworkLinkControl object. + + Returns + ------- + str: A string representation of the NetworkLinkControl object. + + """ + 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"min_refresh_period={self.min_refresh_period!r}, " + f"max_session_length={self.max_session_length!r}, " + f"cookie={self.cookie!r}, " + f"message={self.message!r}, " + f"link_name={self.link_name!r}, " + f"link_description={self.link_description!r}, " + f"link_snippet={self.link_snippet!r}, " + f"expires={self.expires!r}, " + f"update={self.update!r}, " + f"view={self.view!r}, " + f"schemata={self.schemata!r}, " + f"**{self._get_splat()!r}," + ")" + ) + + registry.register( _Container, RegistryItem( @@ -334,6 +410,17 @@ def get_style_by_url(self, style_url: str) -> Optional[Union[Style, StyleMap]]: set_element=xml_subelement_list, ), ) +registry.register( + NetworkLinkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="schemata", + node_name="Schema", + classes=(Schema,), + get_kwarg=xml_subelement_list_kwarg, + set_element=xml_subelement_list, + ), +) registry.register( Document, RegistryItem( diff --git a/fastkml/features.py b/fastkml/features.py index f8d091a5..83beeefa 100644 --- a/fastkml/features.py +++ b/fastkml/features.py @@ -43,6 +43,8 @@ from fastkml.geometry import Polygon from fastkml.geometry import create_kml_geometry from fastkml.helpers import attribute_int_kwarg +from fastkml.helpers import datetime_subelement +from fastkml.helpers import datetime_subelement_kwarg from fastkml.helpers import bool_subelement from fastkml.helpers import clean_string from fastkml.helpers import int_attribute @@ -63,8 +65,9 @@ from fastkml.styles import Style from fastkml.styles import StyleMap from fastkml.styles import StyleUrl -from fastkml.times import TimeSpan +from fastkml.times import KmlDateTime, TimeSpan from fastkml.times import TimeStamp +from fastkml.update import Update from fastkml.views import Camera from fastkml.views import LookAt from fastkml.views import Region @@ -222,6 +225,15 @@ class _Feature(TimeMixin, _BaseObject): view: Union[Camera, LookAt, None] region: Optional[Region] extended_data: Optional[ExtendedData] + min_refresh_period: Optional[float] = None, + max_session_length: Optional[float] = None, + cookie: Optional[str] = None, + message: Optional[str] = None, + link_name: Optional[str] = None, + link_description: Optional[str] = None, + link_snippet: Optional[str] = None, + expires: Optional[KmlDateTime] = None, + update:Optional[Update] = None, def __init__( self, @@ -244,6 +256,15 @@ def __init__( styles: Optional[Iterable[Union[Style, StyleMap]]] = None, region: Optional[Region] = None, extended_data: Optional[ExtendedData] = None, + min_refresh_period: Optional[float] = None, + max_session_length: Optional[float] = None, + cookie: Optional[str] = None, + message: Optional[str] = None, + link_name: Optional[str] = None, + link_description: Optional[str] = None, + link_snippet: Optional[str] = None, + expires: Optional[KmlDateTime] = None, + update:Optional[Update] = None, **kwargs: Any, ) -> None: """ @@ -304,6 +325,15 @@ def __init__( self.region = region self.extended_data = extended_data self.times = times + self.min_refresh_period = min_refresh_period + self.max_session_length = max_session_length + self.cookie = cookie + self.message = message + self.link_name = link_name + self.link_description = link_description + self.link_snippet = link_snippet + self.expires = expires + self.update = update registry.register( @@ -407,6 +437,95 @@ def __init__( set_element=text_subelement, ), ) +registry.register( + _Feature, + RegistryItem( + ns_ids=("kml",), + attr_name="min_refresh_period", + node_name="minRefreshPeriod", + classes=(float,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + _Feature, + RegistryItem( + ns_ids=("kml",), + attr_name="max_session_length", + node_name="maxSessionLength", + classes=(float,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + _Feature, + RegistryItem( + ns_ids=("kml",), + attr_name="cookie", + node_name="cookie", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + _Feature, + RegistryItem( + ns_ids=("kml",), + attr_name="message", + node_name="message", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + _Feature, + RegistryItem( + ns_ids=("kml",), + attr_name="link_name", + node_name="linkName", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + _Feature, + RegistryItem( + ns_ids=("kml",), + attr_name="link_description", + node_name="linkDescription", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + _Feature, + RegistryItem( + ns_ids=("kml",), + attr_name="link_snippet", + node_name="linkSnippet", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + _Feature, + item=RegistryItem( + ns_ids=("kml", ), + classes=(KmlDateTime,), + attr_name="expires", + node_name="expires", + get_kwarg=datetime_subelement_kwarg, + set_element=datetime_subelement, + ), +) + registry.register( _Feature, RegistryItem( diff --git a/fastkml/kml.py b/fastkml/kml.py index 0886c6ba..3edba7cb 100644 --- a/fastkml/kml.py +++ b/fastkml/kml.py @@ -45,6 +45,7 @@ from fastkml import validator from fastkml.base import _XMLObject from fastkml.containers import Document +from fastkml.containers import NetworkLinkControl from fastkml.containers import Folder from fastkml.enums import Verbosity from fastkml.features import NetworkLink @@ -59,7 +60,7 @@ logger = logging.getLogger(__name__) -kml_children = Union[Folder, Document, Placemark, GroundOverlay, PhotoOverlay] +kml_children = Union[Folder, Document, Placemark, GroundOverlay, PhotoOverlay, NetworkLinkControl] def lxml_parse_and_validate( @@ -286,8 +287,8 @@ def write( KML, RegistryItem( ns_ids=("kml",), - classes=(Document, Folder, Placemark, GroundOverlay, PhotoOverlay, NetworkLink), - node_name="Document,Folder,Placemark,GroundOverlay,PhotoOverlay,NetworkLink", + classes=(Document, Folder, Placemark, GroundOverlay, PhotoOverlay, NetworkLink, NetworkLinkControl), + node_name="Document,Folder,Placemark,GroundOverlay,PhotoOverlay,NetworkLink,NetworkLinkControl", attr_name="features", get_kwarg=xml_subelement_list_kwarg, set_element=xml_subelement_list, diff --git a/fastkml/times.py b/fastkml/times.py index a2f35b5f..1f3eeabd 100644 --- a/fastkml/times.py +++ b/fastkml/times.py @@ -189,6 +189,11 @@ def __str__(self) -> str: else self.dt.isoformat() ) return self.dt.isoformat() + + @classmethod + def get_tag_name(cls) -> str: + """Return the tag name.""" + return cls.__name__.lower() @classmethod def parse(cls, datestr: str) -> Optional["KmlDateTime"]: diff --git a/fastkml/update.py b/fastkml/update.py new file mode 100644 index 00000000..b73b873a --- /dev/null +++ b/fastkml/update.py @@ -0,0 +1,36 @@ +from typing import Any, Dict, List, Optional + +from fastkml.base import _XMLObject + +class Change(_XMLObject): + + def __int__( + self, + target_id: str, + elements: List[Dict[str, Any]] = None + ): + super.__init__( + target_id=target_id, + elements=elements + ) + + def parse_children(self): + pass + +class Create(_XMLObject): + def __init__(): + pass + +class Delete(_XMLObject): + def __init__(): + pass + +class Update: + + def __init__( + target_href: Optional[str] = None, + change: Optional[Change] = None, + create: Optional[Create] = None, + delete: Optional[Delete] = None + ): + pass From 64c24741c4fd5c234be6d3057653ef39863eb4a8 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sat, 23 Nov 2024 15:25:56 +0000 Subject: [PATCH 09/58] add ScreenOverlay class and related functionality for KML overlays --- fastkml/containers.py | 18 +- fastkml/overlays.py | 412 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 428 insertions(+), 2 deletions(-) diff --git a/fastkml/containers.py b/fastkml/containers.py index 5041d219..8d0f6cfe 100644 --- a/fastkml/containers.py +++ b/fastkml/containers.py @@ -41,6 +41,7 @@ from fastkml.helpers import xml_subelement_list_kwarg from fastkml.overlays import GroundOverlay from fastkml.overlays import PhotoOverlay +from fastkml.overlays import ScreenOverlay from fastkml.registry import RegistryItem from fastkml.registry import registry from fastkml.styles import Style @@ -55,6 +56,8 @@ logger = logging.getLogger(__name__) +__all__ = ["Document", "Folder"] + KmlGeometry = Union[ Point, LineString, @@ -328,8 +331,19 @@ def get_style_by_url(self, style_url: str) -> Optional[Union[Style, StyleMap]]: RegistryItem( ns_ids=("kml",), attr_name="features", - node_name="Folder,Placemark,Document,GroundOverlay,PhotoOverlay,NetworkLink", - classes=(Document, Folder, Placemark, GroundOverlay, PhotoOverlay, NetworkLink), + node_name=( + "Folder,Placemark,Document,GroundOverlay,PhotoOverlay,ScreenOverlay," + "NetworkLink" + ), + classes=( + Document, + Folder, + Placemark, + GroundOverlay, + PhotoOverlay, + ScreenOverlay, + NetworkLink, + ), get_kwarg=xml_subelement_list_kwarg, set_element=xml_subelement_list, ), diff --git a/fastkml/overlays.py b/fastkml/overlays.py index ab6a9551..acc5be4a 100644 --- a/fastkml/overlays.py +++ b/fastkml/overlays.py @@ -30,6 +30,7 @@ from fastkml.enums import AltitudeMode from fastkml.enums import GridOrigin from fastkml.enums import Shape +from fastkml.enums import Units from fastkml.features import Snippet from fastkml.features import _Feature from fastkml.geometry import LinearRing @@ -37,8 +38,12 @@ from fastkml.geometry import MultiGeometry from fastkml.geometry import Point from fastkml.geometry import Polygon +from fastkml.helpers import attribute_enum_kwarg +from fastkml.helpers import attribute_float_kwarg from fastkml.helpers import clean_string +from fastkml.helpers import enum_attribute from fastkml.helpers import enum_subelement +from fastkml.helpers import float_attribute from fastkml.helpers import float_subelement from fastkml.helpers import int_subelement from fastkml.helpers import subelement_enum_kwarg @@ -65,7 +70,12 @@ "ImagePyramid", "KmlGeometry", "LatLonBox", + "OverlayXY", "PhotoOverlay", + "RotationXY", + "ScreenOverlay", + "ScreenXY", + "Size", "ViewVolume", ] @@ -1264,3 +1274,405 @@ def __repr__(self) -> str: set_element=xml_subelement, ), ) + + +class _XY(_XMLObject): + """Specifies a point relative to the screen origin in pixels.""" + + _default_nsid = config.KML + + x: Optional[float] + y: Optional[float] + x_units: Optional[Units] + + y_units: Optional[Units] + + def __init__( + self, + ns: Optional[str] = None, + name_spaces: Optional[Dict[str, str]] = None, + x: Optional[float] = None, + y: Optional[float] = None, + x_units: Optional[Units] = None, + y_units: Optional[Units] = None, + **kwargs: Any, + ) -> None: + """ + Initialize a new _XY object. + + Args: + ---- + ns : Optional[str] + The namespace for the element. + name_spaces : Optional[Dict[str, str]] + A dictionary of namespace prefixes and URIs. + x : Optional[float] + The horizontal position of the point relative to the left edge. + y : Optional[float] + The vertical position of the point relative to the bottom edge. + x_units : Optional[Units] + The horizontal units of the point. + y_units : Optional[Units] + The vertical units of the point + kwargs : Any + Additional keyword arguments. + + """ + super().__init__(ns=ns, name_spaces=name_spaces, **kwargs) + self.x = x + self.y = y + self.x_units = x_units + self.y_units = y_units + + def __repr__(self) -> str: + """Create a string (c)representation for _XY.""" + return ( + f"{self.__class__.__module__}.{self.__class__.__name__}(" + f"ns={self.ns!r}, " + f"name_spaces={self.name_spaces!r}, " + f"x={self.x!r}, " + f"y={self.y!r}, " + f"x_units={self.x_units}, " + f"y_units={self.y_units}, " + f"**{self._get_splat()!r}," + ")" + ) + + def __bool__(self) -> bool: + """ + Check if all the attributes necessary are not None. + + Returns + ------- + bool: True if all attributes (x, y) are not None. + + """ + return all([self.x is not None, self.y is not None]) + + +registry.register( + _XY, + RegistryItem( + ns_ids=("", "kml"), + attr_name="x", + node_name="x", + classes=(float,), + get_kwarg=attribute_float_kwarg, + set_element=float_attribute, + ), +) +registry.register( + _XY, + RegistryItem( + ns_ids=("", "kml"), + attr_name="y", + node_name="y", + classes=(float,), + get_kwarg=attribute_float_kwarg, + set_element=float_attribute, + ), +) +registry.register( + _XY, + RegistryItem( + ns_ids=("", "kml"), + attr_name="x_units", + node_name="xunits", + classes=(Units,), + get_kwarg=attribute_enum_kwarg, + set_element=enum_attribute, + default=Units.fraction, + ), +) +registry.register( + _XY, + RegistryItem( + ns_ids=("", "kml"), + attr_name="y_units", + node_name="yunits", + classes=(Units,), + get_kwarg=attribute_enum_kwarg, + set_element=enum_attribute, + default=Units.fraction, + ), +) + + +class OverlayXY(_XY): + """Specifies the placement of the overlay on the screen.""" + + @classmethod + def get_tag_name(cls) -> str: + """Return the tag name.""" + return "overlayXY" + + +class ScreenXY(_XY): + """Specifies the placement of the overlay on the screen.""" + + @classmethod + def get_tag_name(cls) -> str: + """Return the tag name.""" + return "screenXY" + + +class RotationXY(_XY): + """Specifies the rotation of the overlay on the screen.""" + + @classmethod + def get_tag_name(cls) -> str: + """Return the tag name.""" + return "rotationXY" + + +class Size(_XY): + """Specifies the size of the overlay on the screen.""" + + @classmethod + def get_tag_name(cls) -> str: + """Return the tag name.""" + return "size" + + +class ScreenOverlay(_Overlay): + """ + A ScreenOverlay draws an image overlay fixed to the screen. + + This element draws an image overlay fixed to the screen. Sample uses include + watermarking the map with an image, such as a company logo, or adding a + heads-up display (HUD) to show real-time information. + + The child of specifies the image to be used as the overlay. + This file can be either on a local file system or on a web server. + + https://developers.google.com/kml/documentation/kmlreference#screenoverlay + """ + + def __init__( + self, + ns: Optional[str] = None, + name_spaces: Optional[Dict[str, str]] = None, + id: Optional[str] = None, + target_id: Optional[str] = None, + name: Optional[str] = None, + visibility: Optional[bool] = None, + isopen: Optional[bool] = None, + atom_link: Optional[atom.Link] = None, + atom_author: Optional[atom.Author] = None, + address: Optional[str] = None, + phone_number: Optional[str] = None, + snippet: Optional[Snippet] = None, + description: Optional[str] = None, + view: Optional[Union[Camera, LookAt]] = None, + times: Optional[Union[TimeSpan, TimeStamp]] = None, + style_url: Optional[StyleUrl] = None, + styles: Optional[Iterable[Union[Style, StyleMap]]] = None, + region: Optional[Region] = None, + extended_data: Optional[ExtendedData] = None, + color: Optional[str] = None, + draw_order: Optional[int] = None, + icon: Optional[Icon] = None, + # Screen Overlay specific + overlay_xy: Optional[OverlayXY] = None, + screen_xy: Optional[ScreenXY] = None, + rotation_xy: Optional[RotationXY] = None, + size: Optional[Size] = None, + rotation: Optional[float] = None, + **kwargs: Any, + ) -> None: + """ + Initialize a new ScreenOverlay object. + + Args: + ---- + ns : Optional[str] + The namespace of the element. + name_spaces : Optional[Dict[str, str]] + The dictionary of namespace prefixes and URIs. + id : Optional[str] + The ID of the element. + target_id : Optional[str] + The target ID of the element. + name : Optional[str] + The name of the element. + visibility : Optional[bool] + The visibility of the element. + isopen : Optional[bool] + The open state of the element. + atom_link : Optional[atom.Link] + The Atom link associated with the element. + atom_author : Optional[atom.Author] + The Atom author associated with the element. + address : Optional[str] + The address of the element. + phone_number : Optional[str] + The phone number of the element. + snippet : Optional[Snippet] + The snippet associated with the element. + description : Optional[str] + The description of the element. + view : Optional[Union[Camera, LookAt]] + The view associated with the element. + times : Optional[Union[TimeSpan, TimeStamp]] + The times associated with the element. + style_url : Optional[StyleUrl] + The style URL of the element. + styles : Optional[Iterable[Union[Style, StyleMap]]] + The styles associated with the element. + region : Optional[Region] + The region associated with the element. + extended_data : Optional[ExtendedData] + The extended data associated with the element. + color : Optional[str] + The color of the element. + draw_order : Optional[int] + The draw order of the element. + icon : Optional[Icon] + The icon associated with the element. + altitude : Optional[float] + The altitude of the element. + altitude_mode : Optional[AltitudeMode] + The altitude mode of the element. + lat_lon_box : Optional[LatLonBox] + The latitude-longitude box associated with the element. + overlay_xy : Optional[OverlayXY] + The overlay XY associated with the element. + screen_xy : Optional[ScreenXY] + The screen XY associated with the element. + rotation_xy : Optional[RotationXY] + The rotation XY associated with the element. + size : Optional[Size] + The size associated with the element. + rotation : Optional[float] + The rotation of the element. + kwargs : Any + Additional keyword arguments. + + Returns: + ------- + None + + """ + super().__init__( + ns=ns, + name_spaces=name_spaces, + id=id, + target_id=target_id, + name=name, + visibility=visibility, + isopen=isopen, + atom_link=atom_link, + atom_author=atom_author, + address=address, + phone_number=phone_number, + snippet=snippet, + description=description, + view=view, + times=times, + style_url=style_url, + styles=styles, + region=region, + extended_data=extended_data, + color=color, + draw_order=draw_order, + icon=icon, + **kwargs, + ) + self.overlay_xy = overlay_xy + self.screen_xy = screen_xy + self.rotation_xy = rotation_xy + self.size = size + self.rotation = rotation + + def __repr__(self) -> str: + """Create a string (c)representation for ScreenOverlay.""" + 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"name={self.name!r}, " + f"visibility={self.visibility!r}, " + f"isopen={self.isopen!r}, " + f"atom_link={self.atom_link!r}, " + f"atom_author={self.atom_author!r}, " + f"address={self.address!r}, " + f"phone_number={self.phone_number!r}, " + f"snippet={self.snippet!r}, " + f"description={self.description!r}, " + f"view={self.view!r}, " + f"times={self.times!r}, " + f"style_url={self.style_url!r}, " + f"styles={self.styles!r}, " + f"region={self.region!r}, " + f"extended_data={self.extended_data!r}, " + f"color={self.color!r}, " + f"draw_order={self.draw_order!r}, " + f"icon={self.icon!r}, " + f"overlay_xy={self.overlay_xy!r}, " + f"screen_xy={self.screen_xy!r}, " + f"rotation_xy={self.rotation_xy!r}, " + f"size={self.size!r}, " + f"rotation={self.rotation!r}, " + f"**{self._get_splat()!r}," + ")" + ) + + +registry.register( + ScreenOverlay, + RegistryItem( + ns_ids=("kml",), + attr_name="overlay_xy", + node_name="overlayXY", + classes=(OverlayXY,), + get_kwarg=xml_subelement_kwarg, + set_element=xml_subelement, + ), +) +registry.register( + ScreenOverlay, + RegistryItem( + ns_ids=("kml",), + attr_name="screen_xy", + node_name="screenXY", + classes=(ScreenXY,), + get_kwarg=xml_subelement_kwarg, + set_element=xml_subelement, + ), +) +registry.register( + ScreenOverlay, + RegistryItem( + ns_ids=("kml",), + attr_name="rotation_xy", + node_name="rotationXY", + classes=(RotationXY,), + get_kwarg=xml_subelement_kwarg, + set_element=xml_subelement, + ), +) +registry.register( + ScreenOverlay, + RegistryItem( + ns_ids=("kml",), + attr_name="size", + node_name="size", + classes=(Size,), + get_kwarg=xml_subelement_kwarg, + set_element=xml_subelement, + ), +) +registry.register( + ScreenOverlay, + RegistryItem( + ns_ids=("kml",), + attr_name="rotation", + node_name="rotation", + classes=(float,), + get_kwarg=subelement_float_kwarg, + set_element=float_subelement, + default=0.0, + ), +) From 3b691f7ea18220628e86baffbad5dd381781acb5 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sat, 23 Nov 2024 15:29:02 +0000 Subject: [PATCH 10/58] scaffold screen overlat test --- tests/overlays_test.py | 673 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 668 insertions(+), 5 deletions(-) diff --git a/tests/overlays_test.py b/tests/overlays_test.py index 3a7a71cf..86a7c635 100644 --- a/tests/overlays_test.py +++ b/tests/overlays_test.py @@ -24,15 +24,678 @@ from fastkml import overlays from fastkml import views from fastkml.enums import AltitudeMode +from fastkml.enums import Units from tests.base import Lxml from tests.base import StdLibrary -class TestGroundOverlay(StdLibrary): - pass +class TestScreenOverlay(StdLibrary): + def test_create_screen_overlay_with_all_optional_parameters(self) -> None: + """Create a ScreenOverlay object with all optional parameters.""" + overlays.ScreenOverlay( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + id="", + target_id="", + name="Simple crosshairs", + visibility=False, + isopen=None, + atom_link=None, + atom_author=None, + address=None, + phone_number=None, + snippet=None, + description=( + "This screen overlay uses fractional positioning to put the image" + "in the exact center of the screen" + ), + view=None, + times=None, + style_url=None, + styles=[], + region=None, + extended_data=None, + color=None, + draw_order=None, + icon=links.Icon( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + id="", + target_id="", + href="http://developers.google.com/kml/documentation/images/crosshairs.png", + refresh_mode=None, + refresh_interval=None, + view_refresh_mode=None, + view_refresh_time=None, + view_bound_scale=None, + view_format=None, + http_query=None, + ), + overlay_xy=overlays.OverlayXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.5, + y=0.5, + x_units=Units.fraction, + y_units=Units.fraction, + ), + screen_xy=overlays.ScreenXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.5, + y=0.5, + x_units=Units.fraction, + y_units=Units.fraction, + ), + rotation_xy=overlays.RotationXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.5, + y=0.5, + x_units=Units.fraction, + y_units=Units.fraction, + ), + size=overlays.Size( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=0.0, + x_units=Units.pixels, + y_units=Units.pixels, + ), + rotation=None, + ) + overlays.ScreenOverlay( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + id="", + target_id="", + name="Absolute Positioning: Top left", + visibility=False, + isopen=None, + atom_link=None, + atom_author=None, + address=None, + phone_number=None, + snippet=None, + description=None, + view=None, + times=None, + style_url=None, + styles=[], + region=None, + extended_data=None, + color=None, + draw_order=None, + icon=links.Icon( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + id="", + target_id="", + href="http://developers.google.com/kml/documentation/images/top_left.jpg", + refresh_mode=None, + refresh_interval=None, + view_refresh_mode=None, + view_refresh_time=None, + view_bound_scale=None, + view_format=None, + http_query=None, + ), + overlay_xy=overlays.OverlayXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=1.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + screen_xy=overlays.ScreenXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=1.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + rotation_xy=overlays.RotationXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=0.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + size=overlays.Size( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=0.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + rotation=None, + ) + overlays.ScreenOverlay( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + id="", + target_id="", + name="Absolute Positioning: Top right", + visibility=False, + isopen=None, + atom_link=None, + atom_author=None, + address=None, + phone_number=None, + snippet=None, + description=None, + view=None, + times=None, + style_url=None, + styles=[], + region=None, + extended_data=None, + color=None, + draw_order=None, + icon=links.Icon( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + id="", + target_id="", + href="http://developers.google.com/kml/documentation/images/top_right.jpg", + refresh_mode=None, + refresh_interval=None, + view_refresh_mode=None, + view_refresh_time=None, + view_bound_scale=None, + view_format=None, + http_query=None, + ), + overlay_xy=overlays.OverlayXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=1.0, + y=1.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + screen_xy=overlays.ScreenXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=1.0, + y=1.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + rotation_xy=overlays.RotationXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=0.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + size=overlays.Size( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=0.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + rotation=None, + ) + overlays.ScreenOverlay( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + id="", + target_id="", + name="Absolute Positioning: Bottom left", + visibility=False, + isopen=None, + atom_link=None, + atom_author=None, + address=None, + phone_number=None, + snippet=None, + description=None, + view=None, + times=None, + style_url=None, + styles=[], + region=None, + extended_data=None, + color=None, + draw_order=None, + icon=links.Icon( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + id="", + target_id="", + href="http://developers.google.com/kml/documentation/images/bottom_left.jpg", + refresh_mode=None, + refresh_interval=None, + view_refresh_mode=None, + view_refresh_time=None, + view_bound_scale=None, + view_format=None, + http_query=None, + ), + overlay_xy=overlays.OverlayXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=-1.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + screen_xy=overlays.ScreenXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=0.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + rotation_xy=overlays.RotationXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=0.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + size=overlays.Size( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=0.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + rotation=None, + ) + overlays.ScreenOverlay( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + id="", + target_id="", + name="Absolute Positioning: Bottom right", + visibility=False, + isopen=None, + atom_link=None, + atom_author=None, + address=None, + phone_number=None, + snippet=None, + description=None, + view=None, + times=None, + style_url=None, + styles=[], + region=None, + extended_data=None, + color=None, + draw_order=None, + icon=links.Icon( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + id="", + target_id="", + href="http://developers.google.com/kml/documentation/images/bottom_right.jpg", + refresh_mode=None, + refresh_interval=None, + view_refresh_mode=None, + view_refresh_time=None, + view_bound_scale=None, + view_format=None, + http_query=None, + ), + overlay_xy=overlays.OverlayXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=1.0, + y=-1.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + screen_xy=overlays.ScreenXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=1.0, + y=0.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + rotation_xy=overlays.RotationXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=0.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + size=overlays.Size( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=0.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + rotation=None, + ) + overlays.ScreenOverlay( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + id="", + target_id="", + name="Dynamic Positioning: Top of screen", + visibility=False, + isopen=None, + atom_link=None, + atom_author=None, + address=None, + phone_number=None, + snippet=None, + description=None, + view=None, + times=None, + style_url=None, + styles=[], + region=None, + extended_data=None, + color=None, + draw_order=None, + icon=links.Icon( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + id="", + target_id="", + href="http://developers.google.com/kml/documentation/images/dynamic_screenoverlay.jpg", + refresh_mode=None, + refresh_interval=None, + view_refresh_mode=None, + view_refresh_time=None, + view_bound_scale=None, + view_format=None, + http_query=None, + ), + overlay_xy=overlays.OverlayXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=1.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + screen_xy=overlays.ScreenXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=1.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + rotation_xy=overlays.RotationXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=0.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + size=overlays.Size( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=1.0, + y=0.2, + x_units=Units.fraction, + y_units=Units.fraction, + ), + rotation=None, + ) + overlays.ScreenOverlay( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + id="", + target_id="", + name="Dynamic Positioning: Right of screen", + visibility=False, + isopen=None, + atom_link=None, + atom_author=None, + address=None, + phone_number=None, + snippet=None, + description=None, + view=None, + times=None, + style_url=None, + styles=[], + region=None, + extended_data=None, + color=None, + draw_order=None, + icon=links.Icon( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + id="", + target_id="", + href="http://developers.google.com/kml/documentation/images/dynamic_right.jpg", + refresh_mode=None, + refresh_interval=None, + view_refresh_mode=None, + view_refresh_time=None, + view_bound_scale=None, + view_format=None, + http_query=None, + ), + overlay_xy=overlays.OverlayXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=1.0, + y=1.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + screen_xy=overlays.ScreenXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=1.0, + y=1.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + rotation_xy=overlays.RotationXY( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=0.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + size=overlays.Size( + ns="{http://www.opengis.net/kml/2.2}", + name_spaces={ + "kml": "{http://www.opengis.net/kml/2.2}", + "atom": "{http://www.w3.org/2005/Atom}", + "gx": "{http://www.google.com/kml/ext/2.2}", + }, + x=0.0, + y=1.0, + x_units=Units.fraction, + y_units=Units.fraction, + ), + rotation=None, + ) -class TestGroundOverlayString(StdLibrary): +class TestGroundOverlay(StdLibrary): def test_default_to_string(self) -> None: g = overlays.GroundOverlay() @@ -362,11 +1025,11 @@ def test_camera_initialization(self) -> None: assert po.view.roll == 60 -class TestGroundOverlayLxml(Lxml, TestGroundOverlay): +class TestScreenOverlayLxml(Lxml, TestScreenOverlay): """Test with lxml.""" -class TestGroundOverlayStringLxml(Lxml, TestGroundOverlay): +class TestGroundOverlayLxml(Lxml, TestGroundOverlay): """Test with lxml.""" From 35b6fa50162c120d3a32d371366cec2a7cafe43f Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Sun, 24 Nov 2024 04:03:50 -0500 Subject: [PATCH 11/58] Creating separate class for network control --- fastkml/containers.py | 24 +---- fastkml/features.py | 122 +----------------------- fastkml/network_control.py | 184 +++++++++++++++++++++++++++++++++++++ fastkml/update.py | 115 +++++++++++++++++++++-- 4 files changed, 293 insertions(+), 152 deletions(-) create mode 100644 fastkml/network_control.py diff --git a/fastkml/containers.py b/fastkml/containers.py index 40acd84f..b45b4f45 100644 --- a/fastkml/containers.py +++ b/fastkml/containers.py @@ -39,6 +39,7 @@ from fastkml.geometry import Polygon from fastkml.helpers import xml_subelement_list from fastkml.helpers import xml_subelement_list_kwarg +from fastkml.network_control import _NetworkControl from fastkml.overlays import GroundOverlay from fastkml.overlays import PhotoOverlay from fastkml.registry import RegistryItem @@ -52,7 +53,6 @@ from fastkml.update import Update from fastkml.utils import find_all from fastkml.views import Camera -from fastkml.views import _AbstractView from fastkml.views import LookAt from fastkml.views import Region @@ -326,7 +326,7 @@ def get_style_by_url(self, style_url: str) -> Optional[Union[Style, StyleMap]]: ) -class NetworkLinkControl(_Container): +class NetworkLinkControl(_NetworkControl): schemata: List[Schema] @@ -334,8 +334,6 @@ def __init__( self, ns: Optional[str] = None, name_spaces: Optional[Dict[str, str]] = None, - id: Optional[str] = None, - target_id: Optional[str] = None, min_refresh_period: Optional[float] = None, max_session_length: Optional[float] = None, cookie: Optional[str] = None, @@ -346,14 +344,11 @@ def __init__( expires: Optional[KmlDateTime] = None, update:Optional[Update] = None, view: Optional[Union[Camera, LookAt]] = None, - schemata: Optional[Iterable[Schema]] = None, **kwargs: Any ) -> None: super().__init__( ns=ns, name_spaces=name_spaces, - id=id, - target_id=target_id, min_refresh_period=min_refresh_period, max_session_length=max_session_length, cookie=cookie, @@ -366,7 +361,6 @@ def __init__( view=view, **kwargs ) - self.schemata = list(schemata) if schemata else [] def __repr__(self) -> str: """ @@ -381,8 +375,6 @@ def __repr__(self) -> str: 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"min_refresh_period={self.min_refresh_period!r}, " f"max_session_length={self.max_session_length!r}, " f"cookie={self.cookie!r}, " @@ -393,7 +385,6 @@ def __repr__(self) -> str: f"expires={self.expires!r}, " f"update={self.update!r}, " f"view={self.view!r}, " - f"schemata={self.schemata!r}, " f"**{self._get_splat()!r}," ")" ) @@ -410,17 +401,6 @@ def __repr__(self) -> str: set_element=xml_subelement_list, ), ) -registry.register( - NetworkLinkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="schemata", - node_name="Schema", - classes=(Schema,), - get_kwarg=xml_subelement_list_kwarg, - set_element=xml_subelement_list, - ), -) registry.register( Document, RegistryItem( diff --git a/fastkml/features.py b/fastkml/features.py index 83beeefa..07963b5b 100644 --- a/fastkml/features.py +++ b/fastkml/features.py @@ -43,8 +43,6 @@ from fastkml.geometry import Polygon from fastkml.geometry import create_kml_geometry from fastkml.helpers import attribute_int_kwarg -from fastkml.helpers import datetime_subelement -from fastkml.helpers import datetime_subelement_kwarg from fastkml.helpers import bool_subelement from fastkml.helpers import clean_string from fastkml.helpers import int_attribute @@ -65,9 +63,8 @@ from fastkml.styles import Style from fastkml.styles import StyleMap from fastkml.styles import StyleUrl -from fastkml.times import KmlDateTime, TimeSpan +from fastkml.times import TimeSpan from fastkml.times import TimeStamp -from fastkml.update import Update from fastkml.views import Camera from fastkml.views import LookAt from fastkml.views import Region @@ -225,15 +222,6 @@ class _Feature(TimeMixin, _BaseObject): view: Union[Camera, LookAt, None] region: Optional[Region] extended_data: Optional[ExtendedData] - min_refresh_period: Optional[float] = None, - max_session_length: Optional[float] = None, - cookie: Optional[str] = None, - message: Optional[str] = None, - link_name: Optional[str] = None, - link_description: Optional[str] = None, - link_snippet: Optional[str] = None, - expires: Optional[KmlDateTime] = None, - update:Optional[Update] = None, def __init__( self, @@ -256,15 +244,6 @@ def __init__( styles: Optional[Iterable[Union[Style, StyleMap]]] = None, region: Optional[Region] = None, extended_data: Optional[ExtendedData] = None, - min_refresh_period: Optional[float] = None, - max_session_length: Optional[float] = None, - cookie: Optional[str] = None, - message: Optional[str] = None, - link_name: Optional[str] = None, - link_description: Optional[str] = None, - link_snippet: Optional[str] = None, - expires: Optional[KmlDateTime] = None, - update:Optional[Update] = None, **kwargs: Any, ) -> None: """ @@ -325,16 +304,6 @@ def __init__( self.region = region self.extended_data = extended_data self.times = times - self.min_refresh_period = min_refresh_period - self.max_session_length = max_session_length - self.cookie = cookie - self.message = message - self.link_name = link_name - self.link_description = link_description - self.link_snippet = link_snippet - self.expires = expires - self.update = update - registry.register( _Feature, @@ -437,95 +406,6 @@ def __init__( set_element=text_subelement, ), ) -registry.register( - _Feature, - RegistryItem( - ns_ids=("kml",), - attr_name="min_refresh_period", - node_name="minRefreshPeriod", - classes=(float,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - _Feature, - RegistryItem( - ns_ids=("kml",), - attr_name="max_session_length", - node_name="maxSessionLength", - classes=(float,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - _Feature, - RegistryItem( - ns_ids=("kml",), - attr_name="cookie", - node_name="cookie", - classes=(str,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - _Feature, - RegistryItem( - ns_ids=("kml",), - attr_name="message", - node_name="message", - classes=(str,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - _Feature, - RegistryItem( - ns_ids=("kml",), - attr_name="link_name", - node_name="linkName", - classes=(str,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - _Feature, - RegistryItem( - ns_ids=("kml",), - attr_name="link_description", - node_name="linkDescription", - classes=(str,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - _Feature, - RegistryItem( - ns_ids=("kml",), - attr_name="link_snippet", - node_name="linkSnippet", - classes=(str,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - _Feature, - item=RegistryItem( - ns_ids=("kml", ), - classes=(KmlDateTime,), - attr_name="expires", - node_name="expires", - get_kwarg=datetime_subelement_kwarg, - set_element=datetime_subelement, - ), -) - registry.register( _Feature, RegistryItem( diff --git a/fastkml/network_control.py b/fastkml/network_control.py new file mode 100644 index 00000000..312ce5f4 --- /dev/null +++ b/fastkml/network_control.py @@ -0,0 +1,184 @@ +# Copyright (C) 2023 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 + +from typing import Any +from typing import Dict +from typing import Optional +from typing import Union +from fastkml.helpers import datetime_subelement +from fastkml.helpers import datetime_subelement_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.registry import registry +from fastkml.registry import RegistryItem +from fastkml.base import _XMLObject +from fastkml.times import KmlDateTime +from fastkml.update import Update +from fastkml.views import Camera, LookAt + + +class _NetworkControl(_XMLObject): + """ + Absrtact base class representing NetworkLinkControl in KML. + """ + min_refresh_period: Optional[float] + max_session_length: Optional[float] + cookie: Optional[str] + message: Optional[str] + link_name: Optional[str] + link_description: Optional[str] + link_snippet: Optional[str] + expires: Optional[KmlDateTime] + view: Union[Camera, LookAt, None] + update:Optional[Update] + + def __init__( + self, + ns: Optional[str] = None, + name_spaces: Optional[Dict[str, str]] = None, + min_refresh_period: Optional[float] = None, + max_session_length: Optional[float] = None, + cookie: Optional[str] = None, + message: Optional[str] = None, + link_name: Optional[str] = None, + link_description: Optional[str] = None, + link_snippet: Optional[str] = None, + expires: Optional[KmlDateTime] = None, + view: Optional[Union[Camera, LookAt]] = None, + update:Optional[Update] = None, + **kwargs: Any, + ): + super().__init__( + ns=ns, + name_spaces=name_spaces, + **kwargs, + ) + self.min_refresh_period = min_refresh_period + self.max_session_length = max_session_length + self.cookie = cookie + self.message = message + self.link_name = link_name + self.link_description = link_description + self.link_snippet = link_snippet + self.expires = expires + self.view = view + self.update = update + +registry.register( + _NetworkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="min_refresh_period", + node_name="minRefreshPeriod", + classes=(float,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + _NetworkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="max_session_length", + node_name="maxSessionLength", + classes=(float,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + _NetworkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="cookie", + node_name="cookie", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + _NetworkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="message", + node_name="message", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + _NetworkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="link_name", + node_name="linkName", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + _NetworkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="link_description", + node_name="linkDescription", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + _NetworkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="link_snippet", + node_name="linkSnippet", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + _NetworkControl, + item=RegistryItem( + ns_ids=("kml", ), + classes=(KmlDateTime,), + attr_name="expires", + node_name="expires", + get_kwarg=datetime_subelement_kwarg, + set_element=datetime_subelement, + ), +) + +registry.register( + _NetworkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="view", + node_name="Camera,LookAt", + classes=( + Camera, + LookAt, + ), + get_kwarg=xml_subelement_kwarg, + set_element=xml_subelement, + ), +) \ No newline at end of file diff --git a/fastkml/update.py b/fastkml/update.py index b73b873a..948e02a4 100644 --- a/fastkml/update.py +++ b/fastkml/update.py @@ -1,15 +1,41 @@ -from typing import Any, Dict, List, Optional +# Copyright (C) 2023 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 +from typing import Any +from typing import Union +from typing import Dict +from typing import Iterable +from typing import List +from typing import Optional +# from fastkml.containers import Folder +from fastkml.helpers import xml_subelement +from fastkml.helpers import xml_subelement_kwarg +from fastkml.registry import registry +from fastkml.registry import RegistryItem from fastkml.base import _XMLObject +from fastkml.times import TimeSpan class Change(_XMLObject): def __int__( self, target_id: str, - elements: List[Dict[str, Any]] = None + elements: Optional[Union[TimeSpan]] = None ): - super.__init__( + super().__init__( target_id=target_id, elements=elements ) @@ -18,19 +44,90 @@ def parse_children(self): pass class Create(_XMLObject): - def __init__(): + def __int__( + self, + target_id: str, + elements: Optional[Union[TimeSpan]] = None + ): + super().__init__( + target_id=target_id, + elements=elements + ) + + def parse_children(self): pass class Delete(_XMLObject): - def __init__(): + def __int__( + self, + target_id: str, + elements: Optional[Union[TimeSpan]] = None + ): + super().__init__( + target_id=target_id, + elements=elements + ) + + def parse_children(self): pass class Update: def __init__( target_href: Optional[str] = None, - change: Optional[Change] = None, - create: Optional[Create] = None, - delete: Optional[Delete] = None + change: Optional[Iterable[Change]] = None, + create: Optional[Iterable[Create]] = None, + delete: Optional[Iterable[Delete]] = None ): - pass + super().__init__( + target_href=target_href, + change=change, + create=create, + delete=delete + ) + +registry.register( + Update, + RegistryItem( + ns_ids=("kml",), + attr_name="change", + node_name="TimeSpan", + classes=( + # PhotoOverlay, + # Folder, + TimeSpan, + ), + get_kwarg=xml_subelement_kwarg, + set_element=xml_subelement, + ) +) +registry.register( + Update, + RegistryItem( + ns_ids=("kml",), + attr_name="create", + node_name="TimeSpan", + classes=( + # PhotoOverlay, + # Folder, + TimeSpan, + ), + get_kwarg=xml_subelement_kwarg, + set_element=xml_subelement, + ) +) +registry.register( + Update, + RegistryItem( + ns_ids=("kml",), + attr_name="delete", + node_name="TimeSpan", + classes=( + # PhotoOverlay, + # Folder, + TimeSpan, + ), + get_kwarg=xml_subelement_kwarg, + set_element=xml_subelement, + ) +) \ No newline at end of file From 48af036b4eb6b710c126af9e1bd72ead7c19bd66 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 24 Nov 2024 12:39:29 +0000 Subject: [PATCH 12/58] add hypothesis strategies and tests for OverlayXY and ScreenOverlay functionality --- tests/hypothesis/overlay_test.py | 67 ++++ tests/hypothesis/strategies.py | 8 + tests/overlays_test.py | 658 ++----------------------------- 3 files changed, 103 insertions(+), 630 deletions(-) diff --git a/tests/hypothesis/overlay_test.py b/tests/hypothesis/overlay_test.py index 5d7762ef..807e50bb 100644 --- a/tests/hypothesis/overlay_test.py +++ b/tests/hypothesis/overlay_test.py @@ -17,6 +17,7 @@ import typing +import pytest from hypothesis import given from hypothesis import strategies as st from pygeoif.hypothesis.strategies import epsg4326 @@ -31,6 +32,7 @@ from tests.hypothesis.common import assert_str_roundtrip from tests.hypothesis.common import assert_str_roundtrip_terse from tests.hypothesis.common import assert_str_roundtrip_verbose +from tests.hypothesis.strategies import xy class TestLxml(Lxml): @@ -242,3 +244,68 @@ def test_fuzz_ground_overlay( assert_str_roundtrip(ground_overlay) assert_str_roundtrip_terse(ground_overlay) assert_str_roundtrip_verbose(ground_overlay) + + @pytest.mark.parametrize( + "cls", + [ + fastkml.overlays.OverlayXY, + fastkml.overlays.RotationXY, + fastkml.overlays.ScreenXY, + fastkml.overlays.Size, + ], + ) + @given( + x=st.one_of(st.none(), st.floats(allow_nan=False, allow_infinity=False)), + y=st.one_of(st.none(), st.floats(allow_nan=False, allow_infinity=False)), + x_units=st.one_of(st.none(), st.sampled_from(fastkml.enums.Units)), + y_units=st.one_of(st.none(), st.sampled_from(fastkml.enums.Units)), + ) + def test_fuzz_xy( + self, + cls: typing.Union[ + typing.Type[fastkml.overlays.OverlayXY], + typing.Type[fastkml.overlays.RotationXY], + typing.Type[fastkml.overlays.ScreenXY], + typing.Type[fastkml.overlays.Size], + ], + x: typing.Optional[float], + y: typing.Optional[float], + x_units: typing.Optional[fastkml.enums.Units], + y_units: typing.Optional[fastkml.enums.Units], + ) -> None: + xy = cls(x=x, y=y, x_units=x_units, y_units=y_units) + + assert_repr_roundtrip(xy) + assert_str_roundtrip(xy) + assert_str_roundtrip_terse(xy) + assert_str_roundtrip_verbose(xy) + + @given( + overlay_xy=xy(fastkml.overlays.OverlayXY), + screen_xy=xy(fastkml.overlays.ScreenXY), + rotation_xy=xy(fastkml.overlays.RotationXY), + size=xy(fastkml.overlays.Size), + rotation=st.floats(min_value=-180, max_value=180).filter(lambda x: x != 0), + ) + def test_screen_overlay( + self, + overlay_xy: typing.Optional[fastkml.overlays.OverlayXY], + screen_xy: typing.Optional[fastkml.overlays.ScreenXY], + rotation_xy: typing.Optional[fastkml.overlays.RotationXY], + size: typing.Optional[fastkml.overlays.Size], + rotation: typing.Optional[float], + ) -> None: + screen_overlay = fastkml.overlays.ScreenOverlay( + id="screen_overlay1", + name="screen_overlay", + overlay_xy=overlay_xy, + screen_xy=screen_xy, + rotation_xy=rotation_xy, + size=size, + rotation=rotation, + ) + + assert_repr_roundtrip(screen_overlay) + assert_str_roundtrip(screen_overlay) + assert_str_roundtrip_terse(screen_overlay) + assert_str_roundtrip_verbose(screen_overlay) diff --git a/tests/hypothesis/strategies.py b/tests/hypothesis/strategies.py index 4390510f..e070c450 100644 --- a/tests/hypothesis/strategies.py +++ b/tests/hypothesis/strategies.py @@ -178,6 +178,14 @@ when=kml_datetimes(), ) +xy = partial( + st.builds, + x=st.floats(allow_nan=False, allow_infinity=False), + y=st.floats(allow_nan=False, allow_infinity=False), + x_units=st.sampled_from(fastkml.enums.Units), + y_units=st.sampled_from(fastkml.enums.Units), +) + @st.composite def query_strings(draw: st.DrawFn) -> str: diff --git a/tests/overlays_test.py b/tests/overlays_test.py index 86a7c635..9539acb0 100644 --- a/tests/overlays_test.py +++ b/tests/overlays_test.py @@ -16,6 +16,8 @@ """Test the kml classes.""" +import contextlib + from pygeoif import geometry as geo from fastkml import enums @@ -30,669 +32,65 @@ class TestScreenOverlay(StdLibrary): - def test_create_screen_overlay_with_all_optional_parameters(self) -> None: + def test_screen_overlay_from_string(self) -> None: """Create a ScreenOverlay object with all optional parameters.""" - overlays.ScreenOverlay( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - id="", - target_id="", + doc = ( + '' + "Simple crosshairs" + "0" + "This screen overlay uses fractional positioning to put the " + "image in the exact center of the screen" + "" + "http://developers.google.com/kml/images/crosshairs.png" + "" + '' + '' + '' + '' + "-45" + "" + ) + + screen_overlay = overlays.ScreenOverlay.from_string(doc) + + assert screen_overlay == overlays.ScreenOverlay( name="Simple crosshairs", visibility=False, - isopen=None, - atom_link=None, - atom_author=None, - address=None, - phone_number=None, - snippet=None, description=( - "This screen overlay uses fractional positioning to put the image" + "This screen overlay uses fractional positioning to put the image " "in the exact center of the screen" ), - view=None, - times=None, - style_url=None, - styles=[], - region=None, - extended_data=None, - color=None, - draw_order=None, icon=links.Icon( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - id="", - target_id="", - href="http://developers.google.com/kml/documentation/images/crosshairs.png", - refresh_mode=None, - refresh_interval=None, - view_refresh_mode=None, - view_refresh_time=None, - view_bound_scale=None, - view_format=None, - http_query=None, + href="http://developers.google.com/kml/images/crosshairs.png", ), overlay_xy=overlays.OverlayXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, x=0.5, y=0.5, x_units=Units.fraction, y_units=Units.fraction, ), screen_xy=overlays.ScreenXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, x=0.5, y=0.5, x_units=Units.fraction, y_units=Units.fraction, ), rotation_xy=overlays.RotationXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, x=0.5, y=0.5, x_units=Units.fraction, y_units=Units.fraction, ), size=overlays.Size( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, x=0.0, y=0.0, x_units=Units.pixels, y_units=Units.pixels, ), - rotation=None, - ) - overlays.ScreenOverlay( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - id="", - target_id="", - name="Absolute Positioning: Top left", - visibility=False, - isopen=None, - atom_link=None, - atom_author=None, - address=None, - phone_number=None, - snippet=None, - description=None, - view=None, - times=None, - style_url=None, - styles=[], - region=None, - extended_data=None, - color=None, - draw_order=None, - icon=links.Icon( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - id="", - target_id="", - href="http://developers.google.com/kml/documentation/images/top_left.jpg", - refresh_mode=None, - refresh_interval=None, - view_refresh_mode=None, - view_refresh_time=None, - view_bound_scale=None, - view_format=None, - http_query=None, - ), - overlay_xy=overlays.OverlayXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=1.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - screen_xy=overlays.ScreenXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=1.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - rotation_xy=overlays.RotationXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=0.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - size=overlays.Size( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=0.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - rotation=None, - ) - overlays.ScreenOverlay( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - id="", - target_id="", - name="Absolute Positioning: Top right", - visibility=False, - isopen=None, - atom_link=None, - atom_author=None, - address=None, - phone_number=None, - snippet=None, - description=None, - view=None, - times=None, - style_url=None, - styles=[], - region=None, - extended_data=None, - color=None, - draw_order=None, - icon=links.Icon( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - id="", - target_id="", - href="http://developers.google.com/kml/documentation/images/top_right.jpg", - refresh_mode=None, - refresh_interval=None, - view_refresh_mode=None, - view_refresh_time=None, - view_bound_scale=None, - view_format=None, - http_query=None, - ), - overlay_xy=overlays.OverlayXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=1.0, - y=1.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - screen_xy=overlays.ScreenXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=1.0, - y=1.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - rotation_xy=overlays.RotationXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=0.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - size=overlays.Size( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=0.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - rotation=None, - ) - overlays.ScreenOverlay( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - id="", - target_id="", - name="Absolute Positioning: Bottom left", - visibility=False, - isopen=None, - atom_link=None, - atom_author=None, - address=None, - phone_number=None, - snippet=None, - description=None, - view=None, - times=None, - style_url=None, - styles=[], - region=None, - extended_data=None, - color=None, - draw_order=None, - icon=links.Icon( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - id="", - target_id="", - href="http://developers.google.com/kml/documentation/images/bottom_left.jpg", - refresh_mode=None, - refresh_interval=None, - view_refresh_mode=None, - view_refresh_time=None, - view_bound_scale=None, - view_format=None, - http_query=None, - ), - overlay_xy=overlays.OverlayXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=-1.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - screen_xy=overlays.ScreenXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=0.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - rotation_xy=overlays.RotationXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=0.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - size=overlays.Size( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=0.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - rotation=None, - ) - overlays.ScreenOverlay( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - id="", - target_id="", - name="Absolute Positioning: Bottom right", - visibility=False, - isopen=None, - atom_link=None, - atom_author=None, - address=None, - phone_number=None, - snippet=None, - description=None, - view=None, - times=None, - style_url=None, - styles=[], - region=None, - extended_data=None, - color=None, - draw_order=None, - icon=links.Icon( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - id="", - target_id="", - href="http://developers.google.com/kml/documentation/images/bottom_right.jpg", - refresh_mode=None, - refresh_interval=None, - view_refresh_mode=None, - view_refresh_time=None, - view_bound_scale=None, - view_format=None, - http_query=None, - ), - overlay_xy=overlays.OverlayXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=1.0, - y=-1.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - screen_xy=overlays.ScreenXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=1.0, - y=0.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - rotation_xy=overlays.RotationXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=0.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - size=overlays.Size( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=0.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - rotation=None, - ) - overlays.ScreenOverlay( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - id="", - target_id="", - name="Dynamic Positioning: Top of screen", - visibility=False, - isopen=None, - atom_link=None, - atom_author=None, - address=None, - phone_number=None, - snippet=None, - description=None, - view=None, - times=None, - style_url=None, - styles=[], - region=None, - extended_data=None, - color=None, - draw_order=None, - icon=links.Icon( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - id="", - target_id="", - href="http://developers.google.com/kml/documentation/images/dynamic_screenoverlay.jpg", - refresh_mode=None, - refresh_interval=None, - view_refresh_mode=None, - view_refresh_time=None, - view_bound_scale=None, - view_format=None, - http_query=None, - ), - overlay_xy=overlays.OverlayXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=1.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - screen_xy=overlays.ScreenXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=1.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - rotation_xy=overlays.RotationXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=0.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - size=overlays.Size( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=1.0, - y=0.2, - x_units=Units.fraction, - y_units=Units.fraction, - ), - rotation=None, - ) - overlays.ScreenOverlay( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - id="", - target_id="", - name="Dynamic Positioning: Right of screen", - visibility=False, - isopen=None, - atom_link=None, - atom_author=None, - address=None, - phone_number=None, - snippet=None, - description=None, - view=None, - times=None, - style_url=None, - styles=[], - region=None, - extended_data=None, - color=None, - draw_order=None, - icon=links.Icon( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - id="", - target_id="", - href="http://developers.google.com/kml/documentation/images/dynamic_right.jpg", - refresh_mode=None, - refresh_interval=None, - view_refresh_mode=None, - view_refresh_time=None, - view_bound_scale=None, - view_format=None, - http_query=None, - ), - overlay_xy=overlays.OverlayXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=1.0, - y=1.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - screen_xy=overlays.ScreenXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=1.0, - y=1.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - rotation_xy=overlays.RotationXY( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=0.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - size=overlays.Size( - ns="{http://www.opengis.net/kml/2.2}", - name_spaces={ - "kml": "{http://www.opengis.net/kml/2.2}", - "atom": "{http://www.w3.org/2005/Atom}", - "gx": "{http://www.google.com/kml/ext/2.2}", - }, - x=0.0, - y=1.0, - x_units=Units.fraction, - y_units=Units.fraction, - ), - rotation=None, + rotation=-45, ) + with contextlib.suppress(TypeError): + screen_overlay.validate() class TestGroundOverlay(StdLibrary): From 0d641e11aeb1e62a56b550bf3d83e43ee69b3e87 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 24 Nov 2024 12:42:26 +0000 Subject: [PATCH 13/58] add support for ScreenOverlay in changelog --- docs/HISTORY.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/HISTORY.rst b/docs/HISTORY.rst index 01c2cb1d..23d423a7 100644 --- a/docs/HISTORY.rst +++ b/docs/HISTORY.rst @@ -4,6 +4,8 @@ Changelog 1.0.0dev0 (unreleased) ---------------------- +- Add support for ScreenOverlay + 1.0 (2024/11/19) ----------------- From d836ecfad424552bed2dadc6e590f6e24737e046 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 24 Nov 2024 13:12:12 +0000 Subject: [PATCH 14/58] import ScreenOverlay in __init__.py for overlay functionality --- fastkml/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fastkml/__init__.py b/fastkml/__init__.py index b607e08f..d17b4a43 100644 --- a/fastkml/__init__.py +++ b/fastkml/__init__.py @@ -46,6 +46,7 @@ from fastkml.links import Link from fastkml.overlays import GroundOverlay from fastkml.overlays import PhotoOverlay +from fastkml.overlays import ScreenOverlay from fastkml.styles import BalloonStyle from fastkml.styles import IconStyle from fastkml.styles import LabelStyle @@ -72,6 +73,7 @@ "PhotoOverlay", "Schema", "SchemaData", + "ScreenOverlay", "StyleUrl", "Style", "StyleMap", From a2c75fd3c6bdede34feec0e4b66c3deafb87e46b Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Mon, 25 Nov 2024 13:45:56 +0000 Subject: [PATCH 15/58] add upgrading documentation for FastKML version changes --- docs/index.rst | 1 + docs/upgrading.rst | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 docs/upgrading.rst diff --git a/docs/index.rst b/docs/index.rst index acdaf036..657e117b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -26,6 +26,7 @@ requirements, namely: create_kml_files working_with_kml configuration + upgrading fastkml contributing alternatives diff --git a/docs/upgrading.rst b/docs/upgrading.rst new file mode 100644 index 00000000..82a002ac --- /dev/null +++ b/docs/upgrading.rst @@ -0,0 +1,28 @@ +Upgrading from older versions of FastKML +======================================== + +Q: I updated from 0.12 to 1.0.0 and now getting the following errors when using +``parse()``:: + + File "src/lxml/etree.pyx", line 3701, in lxml.etree._Validator.assert_ + AssertionError: Element ... + +A: Your KML does not validate against the XML Schema. +You can read it without validations by passing ``validate=False`` or ``strict=False`` +to the parse method:: + + from fastkml.kml import KML + doc = KML.parse('path/to/your/file.kml', strict=False) + # or + doc = KML.parse('path/to/your/file.kml', validate=False) + +With version 1.0, ``.from_string()`` is a class method that returns a new object. + +In fastkml 0.x:: + + postcode_kml = kml.KML() + postcode_kml.from_string(kml_file.read()) + +Becomes in 1.0:: + + postcode_kml = kml.KML.from_string(kml_file.read()) From 1bee58b5397822eb41b4b5e6ad566a9d2c8ca07a Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Mon, 25 Nov 2024 14:24:50 +0000 Subject: [PATCH 16/58] Changelog: change from_string method to a class method returning a new instance --- docs/HISTORY.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/HISTORY.rst b/docs/HISTORY.rst index 23d423a7..f15ed935 100644 --- a/docs/HISTORY.rst +++ b/docs/HISTORY.rst @@ -17,6 +17,7 @@ Changelog - refactor - Use arrow instead of dateutil - Add an informative ``__repr__`` +- Change the ``from_string`` method to a class method which returns a new instance. 0.12 (2020/09/23) ----------------- From 66d9991b6ca264559b3899aeea0f0ad67edf7d6b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:38:08 +0000 Subject: [PATCH 17/58] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.4 → v0.8.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.4...v0.8.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d3629c79..d3b17031 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,7 +43,7 @@ repos: hooks: - id: absolufy-imports - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.7.4' + rev: 'v0.8.0' hooks: - id: ruff - id: ruff-format From d0f926f8d324c6dd9698b027fee4a07bf4d9addc Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Mon, 25 Nov 2024 17:07:49 +0000 Subject: [PATCH 18/58] ruff update fixes --- fastkml/__init__.py | 46 ++++++++++++++++++++++----------------------- fastkml/base.py | 4 ++-- fastkml/registry.py | 2 +- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/fastkml/__init__.py b/fastkml/__init__.py index d17b4a43..736054e2 100644 --- a/fastkml/__init__.py +++ b/fastkml/__init__.py @@ -62,36 +62,36 @@ __all__ = [ "KML", + "AtomAuthor", + "AtomContributor", + "AtomLink", + "BalloonStyle", + "Camera", + "Data", "Document", + "ExtendedData", "Folder", "GroundOverlay", - "Placemark", - "TimeSpan", - "TimeStamp", - "ExtendedData", - "Data", + "Icon", + "IconStyle", + "LabelStyle", + "LineString", + "LineStyle", + "LinearRing", + "Link", + "LookAt", + "MultiGeometry", "PhotoOverlay", + "Placemark", + "Point", + "PolyStyle", + "Polygon", "Schema", "SchemaData", "ScreenOverlay", - "StyleUrl", "Style", "StyleMap", - "IconStyle", - "LineStyle", - "PolyStyle", - "LabelStyle", - "BalloonStyle", - "AtomLink", - "Icon", - "Link", - "Point", - "LineString", - "LinearRing", - "Polygon", - "MultiGeometry", - "AtomAuthor", - "AtomContributor", - "Camera", - "LookAt", + "StyleUrl", + "TimeSpan", + "TimeStamp", ] diff --git a/fastkml/base.py b/fastkml/base.py index ca2c7c21..8fa9d875 100644 --- a/fastkml/base.py +++ b/fastkml/base.py @@ -83,8 +83,8 @@ def __init__( self.ns: str = ( self.name_spaces.get(self._default_nsid, "") if ns is None else ns ) - for arg in kwargs: - setattr(self, arg, kwargs[arg]) + for arg, val in kwargs.items(): + setattr(self, arg, val) self.__kwarg_keys = tuple(kwargs.keys()) def __repr__(self) -> str: diff --git a/fastkml/registry.py b/fastkml/registry.py index a2a5957e..76023093 100644 --- a/fastkml/registry.py +++ b/fastkml/registry.py @@ -189,4 +189,4 @@ def get(self, cls: Type["_XMLObject"]) -> List[RegistryItem]: registry = Registry() -__all__ = ["registry", "RegistryItem"] +__all__ = ["RegistryItem", "registry"] From 00c38ecfdd857195a0b7fadabfa751ee20c425ae Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Thu, 28 Nov 2024 11:46:55 +0000 Subject: [PATCH 19/58] Enhance module exports with new classes and methods --- fastkml/__init__.py | 49 ++++++++++++++++++++++++++++++++++++++++++++- fastkml/data.py | 9 +++++++++ fastkml/features.py | 2 +- fastkml/geometry.py | 7 ++++--- fastkml/links.py | 2 ++ fastkml/overlays.py | 1 - fastkml/styles.py | 25 ++++++++++++----------- fastkml/times.py | 6 +++++- 8 files changed, 82 insertions(+), 19 deletions(-) diff --git a/fastkml/__init__.py b/fastkml/__init__.py index 736054e2..ddc48af2 100644 --- a/fastkml/__init__.py +++ b/fastkml/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012 -2022 Christian Ledermann +# Copyright (C) 2012 -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 @@ -35,28 +35,50 @@ from fastkml.data import ExtendedData from fastkml.data import Schema from fastkml.data import SchemaData +from fastkml.data import SimpleData +from fastkml.data import SimpleField +from fastkml.features import NetworkLink from fastkml.features import Placemark +from fastkml.features import Snippet +from fastkml.geometry import Coordinates +from fastkml.geometry import InnerBoundaryIs from fastkml.geometry import LinearRing from fastkml.geometry import LineString from fastkml.geometry import MultiGeometry +from fastkml.geometry import OuterBoundaryIs from fastkml.geometry import Point from fastkml.geometry import Polygon +from fastkml.geometry import create_kml_geometry from fastkml.kml import KML from fastkml.links import Icon from fastkml.links import Link from fastkml.overlays import GroundOverlay +from fastkml.overlays import ImagePyramid +from fastkml.overlays import LatLonBox +from fastkml.overlays import OverlayXY from fastkml.overlays import PhotoOverlay +from fastkml.overlays import RotationXY from fastkml.overlays import ScreenOverlay +from fastkml.overlays import ScreenXY +from fastkml.overlays import Size +from fastkml.overlays import ViewVolume from fastkml.styles import BalloonStyle +from fastkml.styles import HotSpot from fastkml.styles import IconStyle from fastkml.styles import LabelStyle from fastkml.styles import LineStyle +from fastkml.styles import Pair from fastkml.styles import PolyStyle from fastkml.styles import Style from fastkml.styles import StyleMap from fastkml.styles import StyleUrl +from fastkml.times import KmlDateTime from fastkml.times import TimeSpan from fastkml.times import TimeStamp +from fastkml.utils import find +from fastkml.utils import find_all +from fastkml.validator import get_schema_parser +from fastkml.validator import validate from fastkml.views import Camera from fastkml.views import LookAt @@ -67,31 +89,56 @@ "AtomLink", "BalloonStyle", "Camera", + "Coordinates", "Data", "Document", "ExtendedData", "Folder", "GroundOverlay", + "HotSpot", "Icon", "IconStyle", + "ImagePyramid", + "InnerBoundaryIs", + "KmlDateTime", "LabelStyle", + "LatLonBox", "LineString", "LineStyle", "LinearRing", "Link", "LookAt", "MultiGeometry", + "NetworkLink", + "OuterBoundaryIs", + "OverlayXY", + "Pair", "PhotoOverlay", "Placemark", "Point", + "Point", "PolyStyle", "Polygon", + "Polygon", + "RotationXY", "Schema", "SchemaData", "ScreenOverlay", + "ScreenXY", + "SimpleData", + "SimpleField", + "Size", + "Snippet", + "Style", "Style", "StyleMap", "StyleUrl", "TimeSpan", "TimeStamp", + "ViewVolume", + "create_kml_geometry", + "find", + "find_all", + "get_schema_parser", + "validate", ] diff --git a/fastkml/data.py b/fastkml/data.py index baea3587..20e80575 100644 --- a/fastkml/data.py +++ b/fastkml/data.py @@ -50,6 +50,7 @@ "ExtendedData", "Schema", "SchemaData", + "SimpleData", "SimpleField", ] @@ -441,6 +442,14 @@ def __bool__(self) -> bool: class SimpleData(_XMLObject): + """ + A SimpleData element is a custom data field. + + This element assigns a value to the custom data field identified by the name + attribute. The type and name of this custom data field are declared in the + ```` element. + """ + _default_nsid = "kml" name: Optional[str] diff --git a/fastkml/features.py b/fastkml/features.py index f8d091a5..e8635bd2 100644 --- a/fastkml/features.py +++ b/fastkml/features.py @@ -69,7 +69,7 @@ from fastkml.views import LookAt from fastkml.views import Region -__all__ = ["KmlGeometry", "NetworkLink", "Placemark", "Snippet"] +__all__ = ["NetworkLink", "Placemark", "Snippet"] logger = logging.getLogger(__name__) diff --git a/fastkml/geometry.py b/fastkml/geometry.py index 3d920a90..ed20c6d9 100644 --- a/fastkml/geometry.py +++ b/fastkml/geometry.py @@ -69,18 +69,19 @@ from fastkml.types import Element __all__ = [ - "AnyGeometryType", "Coordinates", - "GeometryType", + "InnerBoundaryIs", "LineString", "LinearRing", "MultiGeometry", - "MultiGeometryType", + "OuterBoundaryIs", "Point", "Polygon", + "create_kml_geometry", "create_multigeometry", ] + logger = logging.getLogger(__name__) GeometryType = Union[geo.Polygon, geo.LineString, geo.LinearRing, geo.Point] diff --git a/fastkml/links.py b/fastkml/links.py index a0180bf7..3553cb07 100644 --- a/fastkml/links.py +++ b/fastkml/links.py @@ -32,6 +32,8 @@ from fastkml.registry import RegistryItem from fastkml.registry import registry +__all__ = ["Icon", "Link"] + class Link(_BaseObject): """ diff --git a/fastkml/overlays.py b/fastkml/overlays.py index acc5be4a..9b6bde8a 100644 --- a/fastkml/overlays.py +++ b/fastkml/overlays.py @@ -68,7 +68,6 @@ __all__ = [ "GroundOverlay", "ImagePyramid", - "KmlGeometry", "LatLonBox", "OverlayXY", "PhotoOverlay", diff --git a/fastkml/styles.py b/fastkml/styles.py index 671a7943..023517a8 100644 --- a/fastkml/styles.py +++ b/fastkml/styles.py @@ -62,6 +62,19 @@ logger = logging.getLogger(__name__) +__all__ = [ + "BalloonStyle", + "HotSpot", + "IconStyle", + "LabelStyle", + "LineStyle", + "Pair", + "PolyStyle", + "Style", + "StyleMap", + "StyleUrl", +] + class StyleUrl(_XMLObject): """ @@ -1399,15 +1412,3 @@ def highlight(self) -> Optional[Union[StyleUrl, Style]]: set_element=xml_subelement_list, ), ) - - -__all__ = [ - "BalloonStyle", - "IconStyle", - "LabelStyle", - "LineStyle", - "PolyStyle", - "Style", - "StyleMap", - "StyleUrl", -] diff --git a/fastkml/times.py b/fastkml/times.py index a2f35b5f..8f7a451e 100644 --- a/fastkml/times.py +++ b/fastkml/times.py @@ -41,7 +41,11 @@ from fastkml.registry import RegistryItem from fastkml.registry import registry -__all__ = ["KmlDateTime", "TimeSpan", "TimeStamp", "adjust_date_to_resolution"] +__all__ = [ + "KmlDateTime", + "TimeSpan", + "TimeStamp", +] # regular expression to parse a gYearMonth string # year and month may be separated by an optional dash From 9693880e85106247407c66c80b4f67a5e38e6dac Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Thu, 28 Nov 2024 13:42:53 +0000 Subject: [PATCH 20/58] Refactor Sphinx Makefile and quickstart documentation for improved clarity and simplicity --- docs/Makefile | 183 ++++---------------------------------------- docs/quickstart.rst | 15 ++-- 2 files changed, 19 insertions(+), 179 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index 38879a67..d4bb2cbb 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,177 +1,20 @@ -# Makefile for Sphinx documentation +# Minimal makefile for Sphinx documentation # -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . BUILDDIR = _build -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - +# Put it first so that "make" without argument is like "make help". help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/FastKML.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/FastKML.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/FastKML" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/FastKML" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." +.PHONY: help Makefile -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 7bf738f8..fb837a58 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -17,25 +17,24 @@ First we import the necessary modules: >>> from fastkml import kml >>> from pygeoif.geometry import Polygon -Create a KML object and set the namespace: +Create a KML object: .. code-block:: pycon >>> k = kml.KML() - >>> ns = "{http://www.opengis.net/kml/2.2}" Create a KML Document and add it to the KML root object: .. code-block:: pycon - >>> d = kml.Document(ns=ns, id="docid", name="doc name", description="doc description") + >>> d = kml.Document(id="docid", name="doc name", description="doc description") >>> k.append(d) Create a KML Folder and add it to the Document: .. code-block:: pycon - >>> f = kml.Folder(ns=ns, id="fid", name="f name", description="f description") + >>> f = kml.Folder(id="fid", name="f name", description="f description") >>> d.append(f) Create a KML Folder and nest it in the first Folder: @@ -43,7 +42,7 @@ Create a KML Folder and nest it in the first Folder: .. code-block:: pycon >>> nf = kml.Folder( - ... ns=ns, id="nested-fid", name="nested f name", description="nested f description" + ... id="nested-fid", name="nested f name", description="nested f description" ... ) >>> f.append(nf) @@ -51,7 +50,7 @@ Create a second KML Folder within the Document: .. code-block:: pycon - >>> f2 = kml.Folder(ns=ns, id="id2", name="name2", description="description2") + >>> f2 = kml.Folder(id="id2", name="name2", description="description2") >>> d.append(f2) Create a KML Placemark with a simple polygon geometry and add it to the second Folder: @@ -59,9 +58,7 @@ Create a KML Placemark with a simple polygon geometry and add it to the second F .. code-block:: pycon >>> polygon = Polygon([(0, 0, 0), (1, 1, 0), (1, 0, 1)]) - >>> p = kml.Placemark( - ... ns=ns, id="id", name="name", description="description", geometry=polygon - ... ) + >>> p = kml.Placemark(id="id", name="name", description="description", geometry=polygon) >>> f2.append(p) Finally, print out the KML object as a string: From 49ead8f436698efaf94b0da95eedf7c849c0842d Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Thu, 28 Nov 2024 15:02:38 +0000 Subject: [PATCH 21/58] update documentation --- docs/conf.py | 276 +++++--------------------------------- docs/create_kml_files.rst | 11 +- docs/fastkml.rst | 9 +- fastkml/registry.py | 21 +-- 4 files changed, 50 insertions(+), 267 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 53caa139..07251bbd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,33 +1,36 @@ +# noqa: INP001, D100 +# Configuration file for the Sphinx documentation builder. # -# FastKML documentation build configuration file, created by -# sphinx-quickstart on Mon Oct 13 22:24:07 2014. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html -import os +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +import pathlib import sys +DOC_ROOT = pathlib.Path(__file__).parent +PROJECT_ROOT = DOC_ROOT.parent + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath("..")) -from fastkml import about +sys.path.insert(0, str(PROJECT_ROOT)) +from fastkml import about # noqa: E402 -# -- General configuration ------------------------------------------------ +# General information about the project. +project = "FastKML" +copyright = "2014 -2024, Christian Ledermann & Ian Lee" # noqa: A001 +author = "Christian Ledermann" +# The short X.Y version. +version = ".".join(about.__version__.split(".")[:2]) +# The full version, including alpha/beta/rc tags. +release = about.__version__ -# If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. extensions = [ "sphinx.ext.autodoc", "sphinx.ext.doctest", @@ -38,75 +41,21 @@ ] autosummary_generate = True -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] - -# The suffix of source filenames. -source_suffix = ".rst" - -# The encoding of source files. -# source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = "index" -# General information about the project. -project = "FastKML" -copyright = "2014 -2024, Christian Ledermann & Ian Lee" - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = ".".join(about.__version__.split(".")[:2]) -# The full version, including alpha/beta/rc tags. -release = about.__version__ - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ["_build"] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -# default_role = None +templates_path = ["_templates"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True +root_doc = "index" -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# add_module_names = True -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# show_authors = False +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -# keep_warnings = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = "default" +html_theme = "alabaster" +html_static_path = ["_static"] try: import sphinx_rtd_theme @@ -115,67 +64,6 @@ except ImportError: pass -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -# html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True - html_context = { "display_github": True, # Integrate GitHub "github_user": "cleder", # Username @@ -183,107 +71,3 @@ "github_version": "main", # Version "conf_py_path": "/docs/", # Path in the checkout to the docs root } - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = "FastKMLdoc" - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). - # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. - # 'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ( - "index", - "FastKML.tex", - "FastKML Documentation", - r"Christian Ledermann \& Ian Lee", - "manual", - ), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# If true, show page references after internal links. -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ("index", "fastkml", "FastKML Documentation", ["Christian Ledermann & Ian Lee"], 1), -] - -# If true, show URL addresses after external links. -# man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ( - "index", - "FastKML", - "FastKML Documentation", - "Christian Ledermann & Ian Lee", - "FastKML", - "One line description of project.", - "Miscellaneous", - ), -] - -# Documents to append as an appendix to all manuals. -# texinfo_appendices = [] - -# If false, no module index is generated. -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -# texinfo_no_detailmenu = False diff --git a/docs/create_kml_files.rst b/docs/create_kml_files.rst index f722aa20..d608a0ee 100644 --- a/docs/create_kml_files.rst +++ b/docs/create_kml_files.rst @@ -109,10 +109,8 @@ Finally, we create the KML object and write it to a file: >>> document = fastkml.containers.Document(features=placemarks) >>> kml = fastkml.KML(features=[document]) >>> outfile = pathlib.Path("co2_per_capita_2020.kml") - >>> with outfile.open("w") as f: - ... f.write(kml.to_string(prettyprint=True, precision=3)) # doctest: +ELLIPSIS - ... - 4... + >>> kml.write(outfile, prettyprint=True, precision=3) # doctest: +ELLIPSIS + The resulting KML file can be opened in Google Earth or any other KML viewer. @@ -232,10 +230,7 @@ Finally, we create the KML object and write it to a file: >>> document = fastkml.containers.Document(features=folders, styles=styles) >>> kml = fastkml.KML(features=[document]) >>> outfile = pathlib.Path("co2_growth_1995_2022.kml") - >>> with outfile.open("w") as f: - ... f.write(kml.to_string(prettyprint=True, precision=3)) # doctest: +ELLIPSIS - ... - 1... + >>> kml.write(outfile, prettyprint=True, precision=3) You can open the resulting KML file in Google Earth Desktop and use the time slider to diff --git a/docs/fastkml.rst b/docs/fastkml.rst index 96553566..86e54762 100644 --- a/docs/fastkml.rst +++ b/docs/fastkml.rst @@ -30,14 +30,13 @@ fastkml.registry ----------------------- .. automodule:: fastkml.registry - :members: + :members: RegistryItem,Registry :undoc-members: :show-inheritance: -.. autoclass:: fastkml.registry::Registry - :members: register, get - :undoc-members: - :show-inheritance: + .. autodata:: registry + :no-value: + fastkml.kml\_base ------------------------ diff --git a/fastkml/registry.py b/fastkml/registry.py index 76023093..d603a80b 100644 --- a/fastkml/registry.py +++ b/fastkml/registry.py @@ -20,14 +20,6 @@ This approach allows for flexible, declarative mapping between XML and Python objects, with the registry acting as a central configuration for these mappings. -Direct ``Registry`` class use is typically only for library internals or advanced -customization. For normal usage, stick with the ``registry`` instance: - -- The library is designed around this global instance. -- Ensures all parts of the library use the same registry. -- Pre-populated with standard KML mappings. -- Singleton pattern: Avoids multiple conflicting registries. - """ from dataclasses import dataclass @@ -188,5 +180,18 @@ def get(self, cls: Type["_XMLObject"]) -> List[RegistryItem]: registry = Registry() +""" +Global Registry. + +You should use ``registry.registry`` instead of instantiating ``registry.Registry()`` +because ``registry.registry`` is a pre-instantiated global instance, ensuring a single, +shared registry across the entire application. +It is pre-populated with all the necessary KML element registrations. +If you need to add custom elements, you can extend the existing registry without +creating a new one. +Using the pre-defined ``registry.registry`` ensures you're working with the complete, +properly initialized registry for the fastkml library. + +""" __all__ = ["RegistryItem", "registry"] From 6807966f39ff82c1198bd8dba60c90d4f0f8cf93 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Thu, 28 Nov 2024 18:15:00 +0000 Subject: [PATCH 22/58] Refactor KML writing in examples to use the new write method and simplify file handling --- examples/shp2kml.py | 3 +-- examples/shp2kml_timed.py | 13 +++++++------ examples/simple_example.py | 3 +-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/examples/shp2kml.py b/examples/shp2kml.py index 1b0a1634..2535fbc6 100755 --- a/examples/shp2kml.py +++ b/examples/shp2kml.py @@ -68,5 +68,4 @@ kml = fastkml.KML(features=[document]) outfile = pathlib.Path("co2_per_capita_2020.kml") -with outfile.open("w") as f: - f.write(kml.to_string(prettyprint=True, precision=3)) +kml.write(outfile, prettyprint=True, precision=3) diff --git a/examples/shp2kml_timed.py b/examples/shp2kml_timed.py index 2f7dd343..e3f0dc8f 100755 --- a/examples/shp2kml_timed.py +++ b/examples/shp2kml_timed.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +from __future__ import annotations + import csv import datetime import pathlib @@ -23,7 +25,7 @@ co2_csv = pathlib.Path(examples_dir / "owid-co2-data.csv") -co2_pa = {str(i): {} for i in range(1995, 2023)} +co2_pa: dict[str, dict[str, float]] = {str(i): {} for i in range(1995, 2023)} with co2_csv.open() as csvfile: reader = csv.DictReader(csvfile) @@ -54,9 +56,9 @@ ) style_url = fastkml.styles.StyleUrl(url=f"#{iso3_code}") folder = fastkml.containers.Folder(name=feature["properties"]["NAME"]) - co2_growth = 0 + co2_growth = 0.0 for year in range(1995, 2023): - co2_year = co2_pa[str(year)].get(iso3_code, 0) + co2_year = co2_pa[str(year)].get(iso3_code, 0.0) co2_growth += co2_year kml_geometry = create_kml_geometry( @@ -87,6 +89,5 @@ document = fastkml.containers.Document(features=folders, styles=styles) kml = fastkml.KML(features=[document]) -outfile = pathlib.Path("co2_growth_1995_2022.kml") -with outfile.open("w") as f: - f.write(kml.to_string(prettyprint=True, precision=3)) +outfile = pathlib.Path("co2_growth_1995_2022.kmz") +kml.write(outfile, prettyprint=True, precision=3) diff --git a/examples/simple_example.py b/examples/simple_example.py index c5b9e139..183d856a 100755 --- a/examples/simple_example.py +++ b/examples/simple_example.py @@ -17,7 +17,6 @@ def print_child_features(element, depth=0): examples_dir = pathlib.Path(__file__).parent fname = pathlib.Path(examples_dir / "KML_Samples.kml") - with fname.open(encoding="utf-8") as kml_file: - k = kml.KML.from_string(kml_file.read().encode("utf-8")) + k = kml.KML.parse(fname) print_child_features(k) From 60645c93c5468c664a1f626ed732331fa753207e Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Thu, 28 Nov 2024 18:35:38 +0000 Subject: [PATCH 23/58] Fix ISO codes in CO2 data and KML generation scripts for consistency --- examples/owid-co2-data.csv | 56 +++++++++++++++++++------------------- examples/shp2kml.py | 6 ++-- examples/shp2kml_timed.py | 2 +- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/examples/owid-co2-data.csv b/examples/owid-co2-data.csv index f6f5e1a3..65c696f6 100644 --- a/examples/owid-co2-data.csv +++ b/examples/owid-co2-data.csv @@ -197,7 +197,7 @@ year,iso_code,co2_per_capita 1995,THA,2.586 1995,TGO,0.274 1995,TON,0.953 -1995,TO,11.265 +1995,TTO,11.265 1995,TUN,1.751 1995,TUR,3.058 1995,TKM,8.179 @@ -416,7 +416,7 @@ year,iso_code,co2_per_capita 1996,THA,2.883 1996,TGO,0.285 1996,TON,0.768 -1996,TO,14.053 +1996,TTO,14.053 1996,TUN,1.762 1996,TUR,3.311 1996,TKM,7.36 @@ -635,7 +635,7 @@ year,iso_code,co2_per_capita 1997,THA,2.971 1997,TGO,0.191 1997,TON,0.983 -1997,TO,14.346 +1997,TTO,14.346 1997,TUN,1.801 1997,TUR,3.461 1997,TKM,7.149 @@ -854,7 +854,7 @@ year,iso_code,co2_per_capita 1998,THA,2.587 1998,TGO,0.273 1998,TON,0.868 -1998,TO,15.05 +1998,TTO,15.05 1998,TUN,1.838 1998,TUR,3.408 1998,TKM,7.547 @@ -1073,7 +1073,7 @@ year,iso_code,co2_per_capita 1999,THA,2.678 1999,TGO,0.38 1999,TON,1.078 -1999,TO,17.026 +1999,TTO,17.026 1999,TUN,1.904 1999,TUR,3.291 1999,TKM,8.842 @@ -1292,7 +1292,7 @@ year,iso_code,co2_per_capita 2000,THA,2.654 2000,TGO,0.266 2000,TON,0.928 -2000,TO,18.29 +2000,TTO,18.29 2000,TUN,1.977 2000,TUR,3.586 2000,TKM,8.615 @@ -1511,7 +1511,7 @@ year,iso_code,co2_per_capita 2001,THA,2.706 2001,TGO,0.225 2001,TON,0.852 -2001,TO,19.989 +2001,TTO,19.989 2001,TUN,2.044 2001,TUR,3.282 2001,TKM,7.343 @@ -1730,7 +1730,7 @@ year,iso_code,co2_per_capita 2002,THA,2.875 2002,TGO,0.25 2002,TON,0.988 -2002,TO,21.324 +2002,TTO,21.324 2002,TUN,2.049 2002,TUR,3.352 2002,TKM,6.412 @@ -1949,7 +1949,7 @@ year,iso_code,co2_per_capita 2003,THA,2.95 2003,TGO,0.333 2003,TON,1.123 -2003,TO,23.931 +2003,TTO,23.931 2003,TUN,2.074 2003,TUR,3.541 2003,TKM,8.524 @@ -2168,7 +2168,7 @@ year,iso_code,co2_per_capita 2004,THA,3.175 2004,TGO,0.313 2004,TON,1.046 -2004,TO,24.032 +2004,TTO,24.032 2004,TUN,2.147 2004,TUR,3.611 2004,TKM,10.24 @@ -2387,7 +2387,7 @@ year,iso_code,co2_per_capita 2005,THA,3.256 2005,TGO,0.301 2005,TON,1.075 -2005,TO,27.922 +2005,TTO,27.922 2005,TUN,2.171 2005,TUR,3.855 2005,TKM,9.81 @@ -2606,7 +2606,7 @@ year,iso_code,co2_per_capita 2006,THA,3.254 2006,TGO,0.255 2006,TON,1.208 -2006,TO,31.031 +2006,TTO,31.031 2006,TUN,2.191 2006,TUR,4.057 2006,TKM,9.992 @@ -2825,7 +2825,7 @@ year,iso_code,co2_per_capita 2007,THA,3.353 2007,TGO,0.251 2007,TON,1.065 -2007,TO,32.915 +2007,TTO,32.915 2007,TUN,2.324 2007,TUR,4.452 2007,TKM,9.788 @@ -3044,7 +3044,7 @@ year,iso_code,co2_per_capita 2008,THA,3.331 2008,TGO,0.245 2008,TON,1.131 -2008,TO,31.924 +2008,TTO,31.924 2008,TUN,2.391 2008,TUR,4.355 2008,TKM,11.654 @@ -3263,7 +3263,7 @@ year,iso_code,co2_per_capita 2009,THA,3.374 2009,TGO,0.428 2009,TON,1.231 -2009,TO,31.717 +2009,TTO,31.717 2009,TUN,2.357 2009,TUR,4.381 2009,TKM,10.126 @@ -3482,7 +3482,7 @@ year,iso_code,co2_per_capita 2010,THA,3.53 2010,TGO,0.395 2010,TON,1.092 -2010,TO,33.406 +2010,TTO,33.406 2010,TUN,2.583 2010,TUR,4.32 2010,TKM,11.235 @@ -3701,7 +3701,7 @@ year,iso_code,co2_per_capita 2011,THA,3.564 2011,TGO,0.371 2011,TON,0.953 -2011,TO,33.146 +2011,TTO,33.146 2011,TUN,2.396 2011,TUR,4.612 2011,TKM,12.154 @@ -3920,7 +3920,7 @@ year,iso_code,co2_per_capita 2012,THA,3.794 2012,TGO,0.32 2012,TON,0.988 -2012,TO,32.282 +2012,TTO,32.282 2012,TUN,2.547 2012,TUR,4.731 2012,TKM,12.263 @@ -4139,7 +4139,7 @@ year,iso_code,co2_per_capita 2013,THA,3.791 2013,TGO,0.231 2013,TON,1.06 -2013,TO,31.806 +2013,TTO,31.806 2013,TUN,2.507 2013,TUR,4.536 2013,TKM,11.572 @@ -4358,7 +4358,7 @@ year,iso_code,co2_per_capita 2014,THA,3.895 2014,TGO,0.212 2014,TON,1.065 -2014,TO,32.324 +2014,TTO,32.324 2014,TUN,2.598 2014,TUR,4.66 2014,TKM,11.089 @@ -4577,7 +4577,7 @@ year,iso_code,co2_per_capita 2015,THA,3.942 2015,TGO,0.249 2015,TON,1.105 -2015,TO,31.201 +2015,TTO,31.201 2015,TUN,2.718 2015,TUR,4.833 2015,TKM,11.155 @@ -4796,7 +4796,7 @@ year,iso_code,co2_per_capita 2016,THA,4.023 2016,TGO,0.302 2016,TON,1.178 -2016,TO,27.15 +2016,TTO,27.15 2016,TUN,2.618 2016,TUR,5.011 2016,TKM,10.982 @@ -5015,7 +5015,7 @@ year,iso_code,co2_per_capita 2017,THA,3.997 2017,TGO,0.254 2017,TON,1.286 -2017,TO,27.267 +2017,TTO,27.267 2017,TUN,2.648 2017,TUR,5.249 2017,TKM,10.783 @@ -5234,7 +5234,7 @@ year,iso_code,co2_per_capita 2018,THA,4.054 2018,TGO,0.269 2018,TON,1.289 -2018,TO,26.801 +2018,TTO,26.801 2018,TUN,2.609 2018,TUR,5.097 2018,TKM,10.513 @@ -5453,7 +5453,7 @@ year,iso_code,co2_per_capita 2019,THA,3.953 2019,TGO,0.293 2019,TON,1.536 -2019,TO,26.832 +2019,TTO,26.832 2019,TUN,2.519 2019,TUR,4.824 2019,TKM,10.607 @@ -5672,7 +5672,7 @@ year,iso_code,co2_per_capita 2020,THA,3.803 2020,TGO,0.282 2020,TON,1.74 -2020,TO,23.074 +2020,TTO,23.074 2020,TUN,2.343 2020,TUR,4.908 2020,TKM,10.831 @@ -5891,7 +5891,7 @@ year,iso_code,co2_per_capita 2021,THA,3.732 2021,TGO,0.296 2021,TON,1.803 -2021,TO,23.29 +2021,TTO,23.29 2021,TUN,2.874 2021,TUR,5.34 2021,TKM,11.034 @@ -6110,7 +6110,7 @@ year,iso_code,co2_per_capita 2022,THA,3.776 2022,TGO,0.291 2022,TON,1.769 -2022,TO,22.424 +2022,TTO,22.424 2022,TUN,2.879 2022,TUR,5.105 2022,TKM,11.034 diff --git a/examples/shp2kml.py b/examples/shp2kml.py index 2535fbc6..f5edb6a7 100755 --- a/examples/shp2kml.py +++ b/examples/shp2kml.py @@ -35,7 +35,7 @@ for feature in shp.__geo_interface__["features"]: geometry = shape(feature["geometry"]) - co2_emission = co2_data.get(feature["properties"]["ADM0_A3"], 0) + co2_emission = co2_data.get(feature["properties"]["ADM0_ISO"], 0) geometry = force_3d(geometry, co2_emission * 100_000) kml_geometry = create_kml_geometry( geometry, @@ -44,7 +44,7 @@ ) color = random.randint(0, 0xFFFFFF) style = fastkml.styles.Style( - id=feature["properties"]["ADM0_A3"], + id=feature["properties"]["ADM0_ISO"], styles=[ fastkml.styles.LineStyle(color=f"55{color:06X}", width=2), fastkml.styles.PolyStyle( @@ -56,7 +56,7 @@ ], ) - style_url = fastkml.styles.StyleUrl(url=f"#{feature['properties']['ADM0_A3']}") + style_url = fastkml.styles.StyleUrl(url=f"#{feature['properties']['ADM0_ISO']}") placemark = fastkml.features.Placemark( name=feature["properties"]["NAME"], description=feature["properties"]["FORMAL_EN"], diff --git a/examples/shp2kml_timed.py b/examples/shp2kml_timed.py index e3f0dc8f..cc5c9cc3 100755 --- a/examples/shp2kml_timed.py +++ b/examples/shp2kml_timed.py @@ -38,7 +38,7 @@ styles = [] folders = [] for feature in shp.__geo_interface__["features"]: - iso3_code = feature["properties"]["ADM0_A3"] + iso3_code = feature["properties"]["ADM0_ISO"] geometry = shape(feature["geometry"]) color = random.randint(0, 0xFFFFFF) styles.append( From 6531a33f55d2aa007c4e3df4c3c2c10fc964884b Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Thu, 28 Nov 2024 18:36:47 +0000 Subject: [PATCH 24/58] Fix ISO codes in CO2 data and KML generation scripts for consistency --- examples/owid-co2-data.csv | 56 +++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/examples/owid-co2-data.csv b/examples/owid-co2-data.csv index 65c696f6..f6f5e1a3 100644 --- a/examples/owid-co2-data.csv +++ b/examples/owid-co2-data.csv @@ -197,7 +197,7 @@ year,iso_code,co2_per_capita 1995,THA,2.586 1995,TGO,0.274 1995,TON,0.953 -1995,TTO,11.265 +1995,TO,11.265 1995,TUN,1.751 1995,TUR,3.058 1995,TKM,8.179 @@ -416,7 +416,7 @@ year,iso_code,co2_per_capita 1996,THA,2.883 1996,TGO,0.285 1996,TON,0.768 -1996,TTO,14.053 +1996,TO,14.053 1996,TUN,1.762 1996,TUR,3.311 1996,TKM,7.36 @@ -635,7 +635,7 @@ year,iso_code,co2_per_capita 1997,THA,2.971 1997,TGO,0.191 1997,TON,0.983 -1997,TTO,14.346 +1997,TO,14.346 1997,TUN,1.801 1997,TUR,3.461 1997,TKM,7.149 @@ -854,7 +854,7 @@ year,iso_code,co2_per_capita 1998,THA,2.587 1998,TGO,0.273 1998,TON,0.868 -1998,TTO,15.05 +1998,TO,15.05 1998,TUN,1.838 1998,TUR,3.408 1998,TKM,7.547 @@ -1073,7 +1073,7 @@ year,iso_code,co2_per_capita 1999,THA,2.678 1999,TGO,0.38 1999,TON,1.078 -1999,TTO,17.026 +1999,TO,17.026 1999,TUN,1.904 1999,TUR,3.291 1999,TKM,8.842 @@ -1292,7 +1292,7 @@ year,iso_code,co2_per_capita 2000,THA,2.654 2000,TGO,0.266 2000,TON,0.928 -2000,TTO,18.29 +2000,TO,18.29 2000,TUN,1.977 2000,TUR,3.586 2000,TKM,8.615 @@ -1511,7 +1511,7 @@ year,iso_code,co2_per_capita 2001,THA,2.706 2001,TGO,0.225 2001,TON,0.852 -2001,TTO,19.989 +2001,TO,19.989 2001,TUN,2.044 2001,TUR,3.282 2001,TKM,7.343 @@ -1730,7 +1730,7 @@ year,iso_code,co2_per_capita 2002,THA,2.875 2002,TGO,0.25 2002,TON,0.988 -2002,TTO,21.324 +2002,TO,21.324 2002,TUN,2.049 2002,TUR,3.352 2002,TKM,6.412 @@ -1949,7 +1949,7 @@ year,iso_code,co2_per_capita 2003,THA,2.95 2003,TGO,0.333 2003,TON,1.123 -2003,TTO,23.931 +2003,TO,23.931 2003,TUN,2.074 2003,TUR,3.541 2003,TKM,8.524 @@ -2168,7 +2168,7 @@ year,iso_code,co2_per_capita 2004,THA,3.175 2004,TGO,0.313 2004,TON,1.046 -2004,TTO,24.032 +2004,TO,24.032 2004,TUN,2.147 2004,TUR,3.611 2004,TKM,10.24 @@ -2387,7 +2387,7 @@ year,iso_code,co2_per_capita 2005,THA,3.256 2005,TGO,0.301 2005,TON,1.075 -2005,TTO,27.922 +2005,TO,27.922 2005,TUN,2.171 2005,TUR,3.855 2005,TKM,9.81 @@ -2606,7 +2606,7 @@ year,iso_code,co2_per_capita 2006,THA,3.254 2006,TGO,0.255 2006,TON,1.208 -2006,TTO,31.031 +2006,TO,31.031 2006,TUN,2.191 2006,TUR,4.057 2006,TKM,9.992 @@ -2825,7 +2825,7 @@ year,iso_code,co2_per_capita 2007,THA,3.353 2007,TGO,0.251 2007,TON,1.065 -2007,TTO,32.915 +2007,TO,32.915 2007,TUN,2.324 2007,TUR,4.452 2007,TKM,9.788 @@ -3044,7 +3044,7 @@ year,iso_code,co2_per_capita 2008,THA,3.331 2008,TGO,0.245 2008,TON,1.131 -2008,TTO,31.924 +2008,TO,31.924 2008,TUN,2.391 2008,TUR,4.355 2008,TKM,11.654 @@ -3263,7 +3263,7 @@ year,iso_code,co2_per_capita 2009,THA,3.374 2009,TGO,0.428 2009,TON,1.231 -2009,TTO,31.717 +2009,TO,31.717 2009,TUN,2.357 2009,TUR,4.381 2009,TKM,10.126 @@ -3482,7 +3482,7 @@ year,iso_code,co2_per_capita 2010,THA,3.53 2010,TGO,0.395 2010,TON,1.092 -2010,TTO,33.406 +2010,TO,33.406 2010,TUN,2.583 2010,TUR,4.32 2010,TKM,11.235 @@ -3701,7 +3701,7 @@ year,iso_code,co2_per_capita 2011,THA,3.564 2011,TGO,0.371 2011,TON,0.953 -2011,TTO,33.146 +2011,TO,33.146 2011,TUN,2.396 2011,TUR,4.612 2011,TKM,12.154 @@ -3920,7 +3920,7 @@ year,iso_code,co2_per_capita 2012,THA,3.794 2012,TGO,0.32 2012,TON,0.988 -2012,TTO,32.282 +2012,TO,32.282 2012,TUN,2.547 2012,TUR,4.731 2012,TKM,12.263 @@ -4139,7 +4139,7 @@ year,iso_code,co2_per_capita 2013,THA,3.791 2013,TGO,0.231 2013,TON,1.06 -2013,TTO,31.806 +2013,TO,31.806 2013,TUN,2.507 2013,TUR,4.536 2013,TKM,11.572 @@ -4358,7 +4358,7 @@ year,iso_code,co2_per_capita 2014,THA,3.895 2014,TGO,0.212 2014,TON,1.065 -2014,TTO,32.324 +2014,TO,32.324 2014,TUN,2.598 2014,TUR,4.66 2014,TKM,11.089 @@ -4577,7 +4577,7 @@ year,iso_code,co2_per_capita 2015,THA,3.942 2015,TGO,0.249 2015,TON,1.105 -2015,TTO,31.201 +2015,TO,31.201 2015,TUN,2.718 2015,TUR,4.833 2015,TKM,11.155 @@ -4796,7 +4796,7 @@ year,iso_code,co2_per_capita 2016,THA,4.023 2016,TGO,0.302 2016,TON,1.178 -2016,TTO,27.15 +2016,TO,27.15 2016,TUN,2.618 2016,TUR,5.011 2016,TKM,10.982 @@ -5015,7 +5015,7 @@ year,iso_code,co2_per_capita 2017,THA,3.997 2017,TGO,0.254 2017,TON,1.286 -2017,TTO,27.267 +2017,TO,27.267 2017,TUN,2.648 2017,TUR,5.249 2017,TKM,10.783 @@ -5234,7 +5234,7 @@ year,iso_code,co2_per_capita 2018,THA,4.054 2018,TGO,0.269 2018,TON,1.289 -2018,TTO,26.801 +2018,TO,26.801 2018,TUN,2.609 2018,TUR,5.097 2018,TKM,10.513 @@ -5453,7 +5453,7 @@ year,iso_code,co2_per_capita 2019,THA,3.953 2019,TGO,0.293 2019,TON,1.536 -2019,TTO,26.832 +2019,TO,26.832 2019,TUN,2.519 2019,TUR,4.824 2019,TKM,10.607 @@ -5672,7 +5672,7 @@ year,iso_code,co2_per_capita 2020,THA,3.803 2020,TGO,0.282 2020,TON,1.74 -2020,TTO,23.074 +2020,TO,23.074 2020,TUN,2.343 2020,TUR,4.908 2020,TKM,10.831 @@ -5891,7 +5891,7 @@ year,iso_code,co2_per_capita 2021,THA,3.732 2021,TGO,0.296 2021,TON,1.803 -2021,TTO,23.29 +2021,TO,23.29 2021,TUN,2.874 2021,TUR,5.34 2021,TKM,11.034 @@ -6110,7 +6110,7 @@ year,iso_code,co2_per_capita 2022,THA,3.776 2022,TGO,0.291 2022,TON,1.769 -2022,TTO,22.424 +2022,TO,22.424 2022,TUN,2.879 2022,TUR,5.105 2022,TKM,11.034 From dd7deb30668efbc320e414c125c0f5580bc996f7 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Thu, 28 Nov 2024 18:39:47 +0000 Subject: [PATCH 25/58] Fix ISO codes in CO2 data and KML generation scripts for consistency --- examples/owid-co2-data.csv | 56 +++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/examples/owid-co2-data.csv b/examples/owid-co2-data.csv index f6f5e1a3..65c696f6 100644 --- a/examples/owid-co2-data.csv +++ b/examples/owid-co2-data.csv @@ -197,7 +197,7 @@ year,iso_code,co2_per_capita 1995,THA,2.586 1995,TGO,0.274 1995,TON,0.953 -1995,TO,11.265 +1995,TTO,11.265 1995,TUN,1.751 1995,TUR,3.058 1995,TKM,8.179 @@ -416,7 +416,7 @@ year,iso_code,co2_per_capita 1996,THA,2.883 1996,TGO,0.285 1996,TON,0.768 -1996,TO,14.053 +1996,TTO,14.053 1996,TUN,1.762 1996,TUR,3.311 1996,TKM,7.36 @@ -635,7 +635,7 @@ year,iso_code,co2_per_capita 1997,THA,2.971 1997,TGO,0.191 1997,TON,0.983 -1997,TO,14.346 +1997,TTO,14.346 1997,TUN,1.801 1997,TUR,3.461 1997,TKM,7.149 @@ -854,7 +854,7 @@ year,iso_code,co2_per_capita 1998,THA,2.587 1998,TGO,0.273 1998,TON,0.868 -1998,TO,15.05 +1998,TTO,15.05 1998,TUN,1.838 1998,TUR,3.408 1998,TKM,7.547 @@ -1073,7 +1073,7 @@ year,iso_code,co2_per_capita 1999,THA,2.678 1999,TGO,0.38 1999,TON,1.078 -1999,TO,17.026 +1999,TTO,17.026 1999,TUN,1.904 1999,TUR,3.291 1999,TKM,8.842 @@ -1292,7 +1292,7 @@ year,iso_code,co2_per_capita 2000,THA,2.654 2000,TGO,0.266 2000,TON,0.928 -2000,TO,18.29 +2000,TTO,18.29 2000,TUN,1.977 2000,TUR,3.586 2000,TKM,8.615 @@ -1511,7 +1511,7 @@ year,iso_code,co2_per_capita 2001,THA,2.706 2001,TGO,0.225 2001,TON,0.852 -2001,TO,19.989 +2001,TTO,19.989 2001,TUN,2.044 2001,TUR,3.282 2001,TKM,7.343 @@ -1730,7 +1730,7 @@ year,iso_code,co2_per_capita 2002,THA,2.875 2002,TGO,0.25 2002,TON,0.988 -2002,TO,21.324 +2002,TTO,21.324 2002,TUN,2.049 2002,TUR,3.352 2002,TKM,6.412 @@ -1949,7 +1949,7 @@ year,iso_code,co2_per_capita 2003,THA,2.95 2003,TGO,0.333 2003,TON,1.123 -2003,TO,23.931 +2003,TTO,23.931 2003,TUN,2.074 2003,TUR,3.541 2003,TKM,8.524 @@ -2168,7 +2168,7 @@ year,iso_code,co2_per_capita 2004,THA,3.175 2004,TGO,0.313 2004,TON,1.046 -2004,TO,24.032 +2004,TTO,24.032 2004,TUN,2.147 2004,TUR,3.611 2004,TKM,10.24 @@ -2387,7 +2387,7 @@ year,iso_code,co2_per_capita 2005,THA,3.256 2005,TGO,0.301 2005,TON,1.075 -2005,TO,27.922 +2005,TTO,27.922 2005,TUN,2.171 2005,TUR,3.855 2005,TKM,9.81 @@ -2606,7 +2606,7 @@ year,iso_code,co2_per_capita 2006,THA,3.254 2006,TGO,0.255 2006,TON,1.208 -2006,TO,31.031 +2006,TTO,31.031 2006,TUN,2.191 2006,TUR,4.057 2006,TKM,9.992 @@ -2825,7 +2825,7 @@ year,iso_code,co2_per_capita 2007,THA,3.353 2007,TGO,0.251 2007,TON,1.065 -2007,TO,32.915 +2007,TTO,32.915 2007,TUN,2.324 2007,TUR,4.452 2007,TKM,9.788 @@ -3044,7 +3044,7 @@ year,iso_code,co2_per_capita 2008,THA,3.331 2008,TGO,0.245 2008,TON,1.131 -2008,TO,31.924 +2008,TTO,31.924 2008,TUN,2.391 2008,TUR,4.355 2008,TKM,11.654 @@ -3263,7 +3263,7 @@ year,iso_code,co2_per_capita 2009,THA,3.374 2009,TGO,0.428 2009,TON,1.231 -2009,TO,31.717 +2009,TTO,31.717 2009,TUN,2.357 2009,TUR,4.381 2009,TKM,10.126 @@ -3482,7 +3482,7 @@ year,iso_code,co2_per_capita 2010,THA,3.53 2010,TGO,0.395 2010,TON,1.092 -2010,TO,33.406 +2010,TTO,33.406 2010,TUN,2.583 2010,TUR,4.32 2010,TKM,11.235 @@ -3701,7 +3701,7 @@ year,iso_code,co2_per_capita 2011,THA,3.564 2011,TGO,0.371 2011,TON,0.953 -2011,TO,33.146 +2011,TTO,33.146 2011,TUN,2.396 2011,TUR,4.612 2011,TKM,12.154 @@ -3920,7 +3920,7 @@ year,iso_code,co2_per_capita 2012,THA,3.794 2012,TGO,0.32 2012,TON,0.988 -2012,TO,32.282 +2012,TTO,32.282 2012,TUN,2.547 2012,TUR,4.731 2012,TKM,12.263 @@ -4139,7 +4139,7 @@ year,iso_code,co2_per_capita 2013,THA,3.791 2013,TGO,0.231 2013,TON,1.06 -2013,TO,31.806 +2013,TTO,31.806 2013,TUN,2.507 2013,TUR,4.536 2013,TKM,11.572 @@ -4358,7 +4358,7 @@ year,iso_code,co2_per_capita 2014,THA,3.895 2014,TGO,0.212 2014,TON,1.065 -2014,TO,32.324 +2014,TTO,32.324 2014,TUN,2.598 2014,TUR,4.66 2014,TKM,11.089 @@ -4577,7 +4577,7 @@ year,iso_code,co2_per_capita 2015,THA,3.942 2015,TGO,0.249 2015,TON,1.105 -2015,TO,31.201 +2015,TTO,31.201 2015,TUN,2.718 2015,TUR,4.833 2015,TKM,11.155 @@ -4796,7 +4796,7 @@ year,iso_code,co2_per_capita 2016,THA,4.023 2016,TGO,0.302 2016,TON,1.178 -2016,TO,27.15 +2016,TTO,27.15 2016,TUN,2.618 2016,TUR,5.011 2016,TKM,10.982 @@ -5015,7 +5015,7 @@ year,iso_code,co2_per_capita 2017,THA,3.997 2017,TGO,0.254 2017,TON,1.286 -2017,TO,27.267 +2017,TTO,27.267 2017,TUN,2.648 2017,TUR,5.249 2017,TKM,10.783 @@ -5234,7 +5234,7 @@ year,iso_code,co2_per_capita 2018,THA,4.054 2018,TGO,0.269 2018,TON,1.289 -2018,TO,26.801 +2018,TTO,26.801 2018,TUN,2.609 2018,TUR,5.097 2018,TKM,10.513 @@ -5453,7 +5453,7 @@ year,iso_code,co2_per_capita 2019,THA,3.953 2019,TGO,0.293 2019,TON,1.536 -2019,TO,26.832 +2019,TTO,26.832 2019,TUN,2.519 2019,TUR,4.824 2019,TKM,10.607 @@ -5672,7 +5672,7 @@ year,iso_code,co2_per_capita 2020,THA,3.803 2020,TGO,0.282 2020,TON,1.74 -2020,TO,23.074 +2020,TTO,23.074 2020,TUN,2.343 2020,TUR,4.908 2020,TKM,10.831 @@ -5891,7 +5891,7 @@ year,iso_code,co2_per_capita 2021,THA,3.732 2021,TGO,0.296 2021,TON,1.803 -2021,TO,23.29 +2021,TTO,23.29 2021,TUN,2.874 2021,TUR,5.34 2021,TKM,11.034 @@ -6110,7 +6110,7 @@ year,iso_code,co2_per_capita 2022,THA,3.776 2022,TGO,0.291 2022,TON,1.769 -2022,TO,22.424 +2022,TTO,22.424 2022,TUN,2.879 2022,TUR,5.105 2022,TKM,11.034 From 15516aedda0c1ff0a9d9d0c632e9ac55fd9d4b45 Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Thu, 28 Nov 2024 20:36:02 -0500 Subject: [PATCH 26/58] requested change --- fastkml/containers.py | 175 +++++++++++++++++++++++++++++------ fastkml/features.py | 1 + fastkml/network_control.py | 184 ------------------------------------- fastkml/times.py | 5 - 4 files changed, 149 insertions(+), 216 deletions(-) delete mode 100644 fastkml/network_control.py diff --git a/fastkml/containers.py b/fastkml/containers.py index 3a9b3984..3343bb29 100644 --- a/fastkml/containers.py +++ b/fastkml/containers.py @@ -25,7 +25,9 @@ from typing import Union from fastkml import atom +from fastkml import config from fastkml import gx +from fastkml.base import _XMLObject from fastkml.data import ExtendedData from fastkml.data import Schema from fastkml.features import NetworkLink @@ -37,6 +39,12 @@ from fastkml.geometry import MultiGeometry from fastkml.geometry import Point from fastkml.geometry import Polygon +from fastkml.helpers import datetime_subelement +from fastkml.helpers import datetime_subelement_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.network_control import _NetworkControl @@ -328,11 +336,55 @@ def get_style_by_url(self, style_url: str) -> Optional[Union[Style, StyleMap]]: None, ) +registry.register( + _Container, + RegistryItem( + ns_ids=("kml",), + attr_name="features", + node_name=( + "Folder,Placemark,Document,GroundOverlay,PhotoOverlay,ScreenOverlay," + "NetworkLink" + ), + classes=( + Document, + Folder, + Placemark, + GroundOverlay, + PhotoOverlay, + ScreenOverlay, + NetworkLink, + ), + get_kwarg=xml_subelement_list_kwarg, + set_element=xml_subelement_list, + ), +) +registry.register( + Document, + RegistryItem( + ns_ids=("kml",), + attr_name="schemata", + node_name="Schema", + classes=(Schema,), + get_kwarg=xml_subelement_list_kwarg, + set_element=xml_subelement_list, + ), +) -class NetworkLinkControl(_NetworkControl): +class NetworkLinkControl(_XMLObject): + + _default_nsid = config.KML + + min_refresh_period: Optional[float] + max_session_length: Optional[float] + cookie: Optional[str] + message: Optional[str] + link_name: Optional[str] + link_description: Optional[str] + link_snippet: Optional[str] + expires: Optional[KmlDateTime] + view: Union[Camera, LookAt, None] + update:Optional[Update] - schemata: List[Schema] - def __init__( self, ns: Optional[str] = None, @@ -392,37 +444,106 @@ def __repr__(self) -> str: ")" ) - registry.register( - _Container, + NetworkLinkControl, RegistryItem( ns_ids=("kml",), - attr_name="features", - node_name=( - "Folder,Placemark,Document,GroundOverlay,PhotoOverlay,ScreenOverlay," - "NetworkLink" - ), - classes=( - Document, - Folder, - Placemark, - GroundOverlay, - PhotoOverlay, - ScreenOverlay, - NetworkLink, - ), - get_kwarg=xml_subelement_list_kwarg, - set_element=xml_subelement_list, + attr_name="min_refresh_period", + node_name="minRefreshPeriod", + classes=(float,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, ), ) registry.register( - Document, + NetworkLinkControl, RegistryItem( ns_ids=("kml",), - attr_name="schemata", - node_name="Schema", - classes=(Schema,), - get_kwarg=xml_subelement_list_kwarg, - set_element=xml_subelement_list, + attr_name="max_session_length", + node_name="maxSessionLength", + classes=(float,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + NetworkLinkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="cookie", + node_name="cookie", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + NetworkLinkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="message", + node_name="message", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + NetworkLinkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="link_name", + node_name="linkName", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + NetworkLinkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="link_description", + node_name="linkDescription", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + NetworkLinkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="link_snippet", + node_name="linkSnippet", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + NetworkLinkControl, + item=RegistryItem( + ns_ids=("kml", ), + classes=(KmlDateTime,), + attr_name="expires", + node_name="expires", + get_kwarg=datetime_subelement_kwarg, + set_element=datetime_subelement, ), ) + +registry.register( + NetworkLinkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="view", + node_name="Camera,LookAt", + classes=( + Camera, + LookAt, + ), + get_kwarg=xml_subelement_kwarg, + set_element=xml_subelement, + ), +) \ No newline at end of file diff --git a/fastkml/features.py b/fastkml/features.py index f8c8e34d..e8635bd2 100644 --- a/fastkml/features.py +++ b/fastkml/features.py @@ -305,6 +305,7 @@ def __init__( self.extended_data = extended_data self.times = times + registry.register( _Feature, RegistryItem( diff --git a/fastkml/network_control.py b/fastkml/network_control.py deleted file mode 100644 index 312ce5f4..00000000 --- a/fastkml/network_control.py +++ /dev/null @@ -1,184 +0,0 @@ -# Copyright (C) 2023 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 - -from typing import Any -from typing import Dict -from typing import Optional -from typing import Union -from fastkml.helpers import datetime_subelement -from fastkml.helpers import datetime_subelement_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.registry import registry -from fastkml.registry import RegistryItem -from fastkml.base import _XMLObject -from fastkml.times import KmlDateTime -from fastkml.update import Update -from fastkml.views import Camera, LookAt - - -class _NetworkControl(_XMLObject): - """ - Absrtact base class representing NetworkLinkControl in KML. - """ - min_refresh_period: Optional[float] - max_session_length: Optional[float] - cookie: Optional[str] - message: Optional[str] - link_name: Optional[str] - link_description: Optional[str] - link_snippet: Optional[str] - expires: Optional[KmlDateTime] - view: Union[Camera, LookAt, None] - update:Optional[Update] - - def __init__( - self, - ns: Optional[str] = None, - name_spaces: Optional[Dict[str, str]] = None, - min_refresh_period: Optional[float] = None, - max_session_length: Optional[float] = None, - cookie: Optional[str] = None, - message: Optional[str] = None, - link_name: Optional[str] = None, - link_description: Optional[str] = None, - link_snippet: Optional[str] = None, - expires: Optional[KmlDateTime] = None, - view: Optional[Union[Camera, LookAt]] = None, - update:Optional[Update] = None, - **kwargs: Any, - ): - super().__init__( - ns=ns, - name_spaces=name_spaces, - **kwargs, - ) - self.min_refresh_period = min_refresh_period - self.max_session_length = max_session_length - self.cookie = cookie - self.message = message - self.link_name = link_name - self.link_description = link_description - self.link_snippet = link_snippet - self.expires = expires - self.view = view - self.update = update - -registry.register( - _NetworkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="min_refresh_period", - node_name="minRefreshPeriod", - classes=(float,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - _NetworkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="max_session_length", - node_name="maxSessionLength", - classes=(float,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - _NetworkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="cookie", - node_name="cookie", - classes=(str,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - _NetworkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="message", - node_name="message", - classes=(str,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - _NetworkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="link_name", - node_name="linkName", - classes=(str,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - _NetworkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="link_description", - node_name="linkDescription", - classes=(str,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - _NetworkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="link_snippet", - node_name="linkSnippet", - classes=(str,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - _NetworkControl, - item=RegistryItem( - ns_ids=("kml", ), - classes=(KmlDateTime,), - attr_name="expires", - node_name="expires", - get_kwarg=datetime_subelement_kwarg, - set_element=datetime_subelement, - ), -) - -registry.register( - _NetworkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="view", - node_name="Camera,LookAt", - classes=( - Camera, - LookAt, - ), - get_kwarg=xml_subelement_kwarg, - set_element=xml_subelement, - ), -) \ No newline at end of file diff --git a/fastkml/times.py b/fastkml/times.py index 58ca8987..8f7a451e 100644 --- a/fastkml/times.py +++ b/fastkml/times.py @@ -193,11 +193,6 @@ def __str__(self) -> str: else self.dt.isoformat() ) return self.dt.isoformat() - - @classmethod - def get_tag_name(cls) -> str: - """Return the tag name.""" - return cls.__name__.lower() @classmethod def parse(cls, datestr: str) -> Optional["KmlDateTime"]: From a0f5851fe89d52f11352b033f308e096dc445cdc Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Fri, 29 Nov 2024 19:17:16 +0000 Subject: [PATCH 27/58] Add KML resources and tutorials to documentation --- README.rst | 1 + docs/kml.rst | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 docs/kml.rst diff --git a/README.rst b/README.rst index 34ae1140..f4a6f4e4 100644 --- a/README.rst +++ b/README.rst @@ -10,6 +10,7 @@ it simple and fast (using lxml_ if available). Fast refers to the time you spend to write and read KML files as well as the time you spend to get acquainted to the library or to create KML objects. It aims to provide all of the functionality that KML clients such as `Marble `_, +`NASA WorldWind `_, `Cesium JS `_, `OpenLayers `_, `Google Maps `_, and `Google Earth `_ support. diff --git a/docs/kml.rst b/docs/kml.rst new file mode 100644 index 00000000..71985554 --- /dev/null +++ b/docs/kml.rst @@ -0,0 +1,9 @@ +KML Resources and Tutorials +=========================== + + +https://developers.google.com/kml + +https://en.wikipedia.org/wiki/Keyhole_Markup_Language + +https://support.safe.com/hc/en-us/sections/25407358419469-OGC-Google-KML From c58c8dad43cd7be222614e4ff421d09dc439a17c Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sat, 30 Nov 2024 17:10:41 +0000 Subject: [PATCH 28/58] enhance documentation --- docs/index.rst | 1 + docs/kml.rst | 10 +++++++--- docs/working_with_kml.rst | 10 +++++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 657e117b..d438380c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -29,5 +29,6 @@ requirements, namely: upgrading fastkml contributing + kml alternatives HISTORY diff --git a/docs/kml.rst b/docs/kml.rst index 71985554..29f8133b 100644 --- a/docs/kml.rst +++ b/docs/kml.rst @@ -1,9 +1,13 @@ KML Resources and Tutorials =========================== +Learning KML can be straightforward with the right resources. +Here are some of the best sources: -https://developers.google.com/kml -https://en.wikipedia.org/wiki/Keyhole_Markup_Language +`Google Developers - KML `_ provides a comprehensive +guide to KML, including tutorials, reference, and examples. -https://support.safe.com/hc/en-us/sections/25407358419469-OGC-Google-KML + +The `FME Support Center's section on OGC and Google KML `_ +provides detailed guidance for creating, styling, and optimizing KML files. diff --git a/docs/working_with_kml.rst b/docs/working_with_kml.rst index 3da5942e..9a0eba4e 100644 --- a/docs/working_with_kml.rst +++ b/docs/working_with_kml.rst @@ -50,8 +50,12 @@ We could also search for all Points, which will also return the Points inside th ``find_all`` can also search for arbitrary elements by their attributes, by passing the attribute name and value as keyword arguments. -``find`` is a shortcut for ``find_all`` that returns the first element found, which is -useful when we know there is only one element that matches the search criteria. + +.. note:: + + ``find`` is a shortcut for ``find_all`` that returns the first element found, + which is useful when we know there is only one element that matches the search + criteria. .. code-block:: pycon @@ -232,7 +236,7 @@ Now we can remove the CascadingStyle from the document and have a look at the re hide - + Ort1 10.06256752902339 From 9a653d2be12a94bc28a200a426286a5a06a8719e Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sat, 30 Nov 2024 17:31:03 +0000 Subject: [PATCH 29/58] Add KML Model element scaffold implementation with associated classes --- fastkml/model.py | 153 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 fastkml/model.py diff --git a/fastkml/model.py b/fastkml/model.py new file mode 100644 index 00000000..62bb8f64 --- /dev/null +++ b/fastkml/model.py @@ -0,0 +1,153 @@ +# Copyright (C) 2012-2023 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 typing import Dict +from typing import Iterable +from typing import Optional + +from fastkml.base import _XMLObject +from fastkml.enums import AltitudeMode +from fastkml.helpers import clean_string +from fastkml.kml_base import _BaseObject +from fastkml.links import Link + + +class Location(_XMLObject): + """Represents a location in KML.""" + + def __init__( + self, + ns: Optional[str] = None, + name_spaces: Optional[Dict[str, str]] = None, + altitude: Optional[float] = None, + latitude: Optional[float] = None, + longitude: Optional[float] = None, + ) -> None: + super().__init__(ns=ns, name_spaces=name_spaces) + self.altitude = altitude + self.latitude = latitude + self.longitude = longitude + + +class Orientation(_XMLObject): + """Represents an orientation in KML.""" + + def __init__( + self, + ns: Optional[str] = None, + name_spaces: Optional[Dict[str, str]] = None, + heading: Optional[float] = None, + tilt: Optional[float] = None, + roll: Optional[float] = None, + ) -> None: + super().__init__(ns=ns, name_spaces=name_spaces) + self.heading = heading + self.tilt = tilt + self.roll = roll + + +class Scale(_XMLObject): + """Represents a scale in KML.""" + + def __init__( + self, + ns: Optional[str] = None, + name_spaces: Optional[Dict[str, str]] = None, + x: Optional[float] = None, + y: Optional[float] = None, + z: Optional[float] = None, + ) -> None: + super().__init__(ns=ns, name_spaces=name_spaces) + self.x = x + self.y = y + self.z = z + + +class Alias(_XMLObject): + """Represents an alias in KML.""" + + def __init__( + self, + ns: Optional[str] = None, + name_spaces: Optional[Dict[str, str]] = None, + target_href: Optional[str] = None, + source_href: Optional[str] = None, + ) -> None: + super().__init__(ns=ns, name_spaces=name_spaces) + self.target_href = clean_string(target_href) + self.source_href = clean_string(source_href) + + +class ResourceMap(_XMLObject): + """Represents a resource map in KML.""" + + def __init__( + self, + ns: Optional[str] = None, + name_spaces: Optional[Dict[str, str]] = None, + aliases: Optional[Iterable[Alias]] = None, + ) -> None: + super().__init__(ns=ns, name_spaces=name_spaces) + self.aliases = list(aliases) if aliases is not None else [] + + +class Model(_BaseObject): + """Represents a model in KML.""" + + 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, + ) -> None: + super().__init__(ns=ns, name_spaces=name_spaces, id=id, target_id=target_id) + self.altitude_mode = altitude_mode + self.location = location + self.orientation = orientation + self.scale = scale + self.link = link + self.resource_map = resource_map + + def __repr__(self) -> str: + return ( + f"{self.__class__.__module__}.{self.__class__.__name__}(" + f"ns={self.ns!r}, " + f"name_spaces={self.name_spaces!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}," + ")" + ) From b4ba74fa0401f7c4ca11f74f4a1e9236ed3bb167 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sat, 30 Nov 2024 17:43:19 +0000 Subject: [PATCH 30/58] add __bool__ and __repr__ for model classes --- fastkml/model.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/fastkml/model.py b/fastkml/model.py index 62bb8f64..22843dbf 100644 --- a/fastkml/model.py +++ b/fastkml/model.py @@ -45,11 +45,27 @@ def __init__( latitude: Optional[float] = None, longitude: Optional[float] = None, ) -> None: + """Create a new Location.""" super().__init__(ns=ns, name_spaces=name_spaces) self.altitude = altitude self.latitude = latitude self.longitude = longitude + def __bool__(self) -> bool: + 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"altitude={self.altitude!r}, " + f"latitude={self.latitude!r}, " + f"longitude={self.longitude!r}, " + ")" + ) + class Orientation(_XMLObject): """Represents an orientation in KML.""" @@ -67,6 +83,23 @@ def __init__( self.tilt = tilt self.roll = roll + def __bool__(self) -> bool: + 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"heading={self.heading!r}, " + f"tilt={self.tilt!r}, " + f"roll={self.roll!r}, " + ")" + ) + class Scale(_XMLObject): """Represents a scale in KML.""" @@ -84,6 +117,21 @@ def __init__( self.y = y self.z = z + def __bool__(self) -> bool: + 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"x={self.x!r}, " + f"y={self.y!r}, " + f"z={self.z!r}, " + ")" + ) + class Alias(_XMLObject): """Represents an alias in KML.""" @@ -99,6 +147,20 @@ def __init__( self.target_href = clean_string(target_href) self.source_href = clean_string(source_href) + def __bool__(self) -> bool: + return all((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"target_href={self.target_href!r}, " + f"source_href={self.source_href!r}, " + ")" + ) + class ResourceMap(_XMLObject): """Represents a resource map in KML.""" @@ -112,6 +174,19 @@ def __init__( super().__init__(ns=ns, name_spaces=name_spaces) self.aliases = list(aliases) if aliases is not None else [] + def __bool__(self) -> bool: + 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"aliases={self.aliases!r}, " + ")" + ) + class Model(_BaseObject): """Represents a model in KML.""" @@ -137,6 +212,9 @@ def __init__( self.link = link self.resource_map = resource_map + def __bool__(self) -> bool: + return bool(self.link) + def __repr__(self) -> str: return ( f"{self.__class__.__module__}.{self.__class__.__name__}(" From 1d80099dbaaf2aaeba5bcfe85c55f251d0ce8978 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sat, 30 Nov 2024 17:46:06 +0000 Subject: [PATCH 31/58] Add documentation for fastkml.model module and its members --- docs/fastkml.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/fastkml.rst b/docs/fastkml.rst index 86e54762..d4a13808 100644 --- a/docs/fastkml.rst +++ b/docs/fastkml.rst @@ -157,6 +157,15 @@ fastkml.mixins :undoc-members: :show-inheritance: +fastkml.model +-------------------- + +.. automodule:: fastkml.model + :members: + :undoc-members: + :show-inheritance: + + fastkml.overlays ----------------------- From 6e3febb5e117657b12d34cf6c0c5f30da2253aed Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sat, 30 Nov 2024 18:17:55 +0000 Subject: [PATCH 32/58] add attributes and registry registrations for KML Model classes --- fastkml/model.py | 249 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 248 insertions(+), 1 deletion(-) diff --git a/fastkml/model.py b/fastkml/model.py index 22843dbf..06c395f5 100644 --- a/fastkml/model.py +++ b/fastkml/model.py @@ -25,18 +25,35 @@ from typing import Dict from typing import Iterable +from typing import List from typing import Optional from fastkml.base import _XMLObject 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 class Location(_XMLObject): """Represents a location in KML.""" + latitude: Optional[float] + longitude: Optional[float] + altitude: Optional[float] + def __init__( self, ns: Optional[str] = None, @@ -67,9 +84,48 @@ def __repr__(self) -> str: ) +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, + ), +) + + class Orientation(_XMLObject): """Represents an orientation in KML.""" + heading: Optional[float] + tilt: Optional[float] + roll: Optional[float] + def __init__( self, ns: Optional[str] = None, @@ -85,7 +141,7 @@ def __init__( def __bool__(self) -> bool: return any( - (self.heading is not None, self.tilt is not None, self.roll is not None) + (self.heading is not None, self.tilt is not None, self.roll is not None), ) def __repr__(self) -> str: @@ -101,9 +157,48 @@ def __repr__(self) -> str: ) +registry.register( + Orientation, + RegistryItem( + ns_ids=("kml", ""), + attr_name="heading", + node_name="heading", + classes=(float,), + get_kwarg=subelement_float_kwarg, + set_element=float_subelement, + ), +) +registry.register( + Orientation, + RegistryItem( + ns_ids=("kml", ""), + attr_name="tilt", + node_name="tilt", + classes=(float,), + get_kwarg=subelement_float_kwarg, + set_element=float_subelement, + ), +) +registry.register( + Orientation, + RegistryItem( + ns_ids=("kml", ""), + attr_name="roll", + node_name="roll", + classes=(float,), + get_kwarg=subelement_float_kwarg, + set_element=float_subelement, + ), +) + + class Scale(_XMLObject): """Represents a scale in KML.""" + x: Optional[float] + y: Optional[float] + z: Optional[float] + def __init__( self, ns: Optional[str] = None, @@ -133,9 +228,47 @@ def __repr__(self) -> str: ) +registry.register( + Scale, + RegistryItem( + ns_ids=("kml", ""), + attr_name="x", + node_name="x", + classes=(float,), + get_kwarg=subelement_float_kwarg, + set_element=float_subelement, + ), +) +registry.register( + Scale, + RegistryItem( + ns_ids=("kml", ""), + attr_name="y", + node_name="y", + classes=(float,), + get_kwarg=subelement_float_kwarg, + set_element=float_subelement, + ), +) +registry.register( + Scale, + RegistryItem( + ns_ids=("kml", ""), + attr_name="z", + node_name="z", + classes=(float,), + get_kwarg=subelement_float_kwarg, + set_element=float_subelement, + ), +) + + class Alias(_XMLObject): """Represents an alias in KML.""" + target_href: Optional[str] + source_href: Optional[str] + def __init__( self, ns: Optional[str] = None, @@ -162,9 +295,35 @@ def __repr__(self) -> str: ) +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, + ), +) + + class ResourceMap(_XMLObject): """Represents a resource map in KML.""" + aliases: List[Alias] + def __init__( self, ns: Optional[str] = None, @@ -188,9 +347,29 @@ def __repr__(self) -> str: ) +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, + ), +) + + 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, @@ -229,3 +408,71 @@ def __repr__(self) -> str: f"**{self._get_splat()!r}," ")" ) + + +registry.register( + Model, + RegistryItem( + ns_ids=("kml", ""), + attr_name="altitude_mode", + node_name="altitudeMode", + classes=(AltitudeMode,), + get_kwarg=subelement_enum_kwarg, + set_element=enum_subelement, + ), +) +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, + ), +) From aa307b5c751a3c85e79fb6d903c5379ef972f2d4 Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Sat, 30 Nov 2024 23:53:59 -0500 Subject: [PATCH 33/58] removing update --- docs/network.kml | 16 +++ fastkml/__init__.py | 3 +- fastkml/containers.py | 190 +------------------------------- fastkml/kml.py | 2 +- fastkml/network_link_control.py | 190 ++++++++++++++++++++++++++++++++ fastkml/update.py | 133 ---------------------- 6 files changed, 210 insertions(+), 324 deletions(-) create mode 100644 docs/network.kml create mode 100644 fastkml/network_link_control.py delete mode 100644 fastkml/update.py diff --git a/docs/network.kml b/docs/network.kml new file mode 100644 index 00000000..48f6004d --- /dev/null +++ b/docs/network.kml @@ -0,0 +1,16 @@ + + + + 43200 + -1 + + A snippet of XHTML

+ ]]> +
+ 2008-05-30 +
+
\ No newline at end of file diff --git a/fastkml/__init__.py b/fastkml/__init__.py index b9936355..e364876b 100644 --- a/fastkml/__init__.py +++ b/fastkml/__init__.py @@ -31,6 +31,7 @@ from fastkml.atom import Link as AtomLink from fastkml.containers import Document from fastkml.containers import Folder +from fastkml.network_link_control import NetworkLinkControl from fastkml.data import Data from fastkml.data import ExtendedData from fastkml.data import Schema @@ -84,7 +85,7 @@ __all__ = [ "KML", - "NetworkLinkControl" + "NetworkLinkControl", "AtomAuthor", "AtomContributor", "AtomLink", diff --git a/fastkml/containers.py b/fastkml/containers.py index 3343bb29..8d0f6cfe 100644 --- a/fastkml/containers.py +++ b/fastkml/containers.py @@ -25,9 +25,7 @@ from typing import Union from fastkml import atom -from fastkml import config from fastkml import gx -from fastkml.base import _XMLObject from fastkml.data import ExtendedData from fastkml.data import Schema from fastkml.features import NetworkLink @@ -39,15 +37,8 @@ from fastkml.geometry import MultiGeometry from fastkml.geometry import Point from fastkml.geometry import Polygon -from fastkml.helpers import datetime_subelement -from fastkml.helpers import datetime_subelement_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.network_control import _NetworkControl from fastkml.overlays import GroundOverlay from fastkml.overlays import PhotoOverlay from fastkml.overlays import ScreenOverlay @@ -57,9 +48,7 @@ from fastkml.styles import StyleMap from fastkml.styles import StyleUrl from fastkml.times import TimeSpan -from fastkml.times import KmlDateTime from fastkml.times import TimeStamp -from fastkml.update import Update from fastkml.utils import find_all from fastkml.views import Camera from fastkml.views import LookAt @@ -336,6 +325,7 @@ def get_style_by_url(self, style_url: str) -> Optional[Union[Style, StyleMap]]: None, ) + registry.register( _Container, RegistryItem( @@ -369,181 +359,3 @@ def get_style_by_url(self, style_url: str) -> Optional[Union[Style, StyleMap]]: set_element=xml_subelement_list, ), ) - -class NetworkLinkControl(_XMLObject): - - _default_nsid = config.KML - - min_refresh_period: Optional[float] - max_session_length: Optional[float] - cookie: Optional[str] - message: Optional[str] - link_name: Optional[str] - link_description: Optional[str] - link_snippet: Optional[str] - expires: Optional[KmlDateTime] - view: Union[Camera, LookAt, None] - update:Optional[Update] - - def __init__( - self, - ns: Optional[str] = None, - name_spaces: Optional[Dict[str, str]] = None, - min_refresh_period: Optional[float] = None, - max_session_length: Optional[float] = None, - cookie: Optional[str] = None, - message: Optional[str] = None, - link_name: Optional[str] = None, - link_description: Optional[str] = None, - link_snippet: Optional[str] = None, - expires: Optional[KmlDateTime] = None, - update:Optional[Update] = None, - view: Optional[Union[Camera, LookAt]] = None, - **kwargs: Any - ) -> None: - super().__init__( - ns=ns, - name_spaces=name_spaces, - min_refresh_period=min_refresh_period, - max_session_length=max_session_length, - cookie=cookie, - message=message, - link_name=link_name, - link_description=link_description, - link_snippet=link_snippet, - expires=expires, - update=update, - view=view, - **kwargs - ) - - def __repr__(self) -> str: - """ - Return a string representation of the NetworkLinkControl object. - - Returns - ------- - str: A string representation of the NetworkLinkControl object. - - """ - return ( - f"{self.__class__.__module__}.{self.__class__.__name__}(" - f"ns={self.ns!r}, " - f"name_spaces={self.name_spaces!r}, " - f"min_refresh_period={self.min_refresh_period!r}, " - f"max_session_length={self.max_session_length!r}, " - f"cookie={self.cookie!r}, " - f"message={self.message!r}, " - f"link_name={self.link_name!r}, " - f"link_description={self.link_description!r}, " - f"link_snippet={self.link_snippet!r}, " - f"expires={self.expires!r}, " - f"update={self.update!r}, " - f"view={self.view!r}, " - f"**{self._get_splat()!r}," - ")" - ) - -registry.register( - NetworkLinkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="min_refresh_period", - node_name="minRefreshPeriod", - classes=(float,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - NetworkLinkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="max_session_length", - node_name="maxSessionLength", - classes=(float,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - NetworkLinkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="cookie", - node_name="cookie", - classes=(str,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - NetworkLinkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="message", - node_name="message", - classes=(str,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - NetworkLinkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="link_name", - node_name="linkName", - classes=(str,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - NetworkLinkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="link_description", - node_name="linkDescription", - classes=(str,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - NetworkLinkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="link_snippet", - node_name="linkSnippet", - classes=(str,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, - ), -) -registry.register( - NetworkLinkControl, - item=RegistryItem( - ns_ids=("kml", ), - classes=(KmlDateTime,), - attr_name="expires", - node_name="expires", - get_kwarg=datetime_subelement_kwarg, - set_element=datetime_subelement, - ), -) - -registry.register( - NetworkLinkControl, - RegistryItem( - ns_ids=("kml",), - attr_name="view", - node_name="Camera,LookAt", - classes=( - Camera, - LookAt, - ), - get_kwarg=xml_subelement_kwarg, - set_element=xml_subelement, - ), -) \ No newline at end of file diff --git a/fastkml/kml.py b/fastkml/kml.py index 3edba7cb..121044f5 100644 --- a/fastkml/kml.py +++ b/fastkml/kml.py @@ -45,7 +45,7 @@ from fastkml import validator from fastkml.base import _XMLObject from fastkml.containers import Document -from fastkml.containers import NetworkLinkControl +from fastkml.network_link_control import NetworkLinkControl from fastkml.containers import Folder from fastkml.enums import Verbosity from fastkml.features import NetworkLink diff --git a/fastkml/network_link_control.py b/fastkml/network_link_control.py new file mode 100644 index 00000000..42a599a0 --- /dev/null +++ b/fastkml/network_link_control.py @@ -0,0 +1,190 @@ +from typing import Any +from typing import Dict +from typing import Optional +from typing import Union +from fastkml import config +from fastkml.base import _XMLObject +from fastkml.helpers import datetime_subelement +from fastkml.helpers import datetime_subelement_kwarg +from fastkml.helpers import subelement_text_kwarg +from fastkml.helpers import text_subelement +from fastkml.helpers import xml_subelement_list +from fastkml.helpers import xml_subelement_list_kwarg +from fastkml.registry import RegistryItem +from fastkml.registry import registry +from fastkml.times import KmlDateTime +from fastkml.views import Camera, LookAt + + +class NetworkLinkControl(_XMLObject): + + _default_nsid = config.KML + + min_refresh_period: Optional[float] + max_session_length: Optional[float] + cookie: Optional[str] + message: Optional[str] + link_name: Optional[str] + link_description: Optional[str] + link_snippet: Optional[str] + expires: Optional[KmlDateTime] + view: Union[Camera, LookAt, None] # TODO: Add Update field to the parameters + + def __init__( + self, + ns: Optional[str] = None, + name_spaces: Optional[Dict[str, str]] = None, + min_refresh_period: Optional[float] = None, + max_session_length: Optional[float] = None, + cookie: Optional[str] = None, + message: Optional[str] = None, + link_name: Optional[str] = None, + link_description: Optional[str] = None, + link_snippet: Optional[str] = None, + expires: Optional[KmlDateTime] = None, + view: Optional[Union[Camera, LookAt]] = None, + **kwargs: Any + ) -> None: + super().__init__( + ns=ns, + name_spaces=name_spaces, + min_refresh_period=min_refresh_period, + max_session_length=max_session_length, + cookie=cookie, + message=message, + link_name=link_name, + link_description=link_description, + link_snippet=link_snippet, + expires=expires, + view=view, + **kwargs + ) + + def __repr__(self) -> str: + """ + Return a string representation of the NetworkLinkControl object. + + Returns + ------- + str: A string representation of the NetworkLinkControl object. + + """ + return ( + f"{self.__class__.__module__}.{self.__class__.__name__}(" + f"ns={self.ns!r}, " + f"name_spaces={self.name_spaces!r}, " + f"min_refresh_period={self.min_refresh_period!r}, " + f"max_session_length={self.max_session_length!r}, " + f"cookie={self.cookie!r}, " + f"message={self.message!r}, " + f"link_name={self.link_name!r}, " + f"link_description={self.link_description!r}, " + f"link_snippet={self.link_snippet!r}, " + f"expires={self.expires!r}, " + f"view={self.view!r}, " + f"**{self._get_splat()!r}," + ")" + ) + +registry.register( + NetworkLinkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="min_refresh_period", + node_name="minRefreshPeriod", + classes=(float,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + NetworkLinkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="max_session_length", + node_name="maxSessionLength", + classes=(float,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + NetworkLinkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="cookie", + node_name="cookie", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + NetworkLinkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="message", + node_name="message", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + NetworkLinkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="link_name", + node_name="linkName", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + NetworkLinkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="link_description", + node_name="linkDescription", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + NetworkLinkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="link_snippet", + node_name="linkSnippet", + classes=(str,), + get_kwarg=subelement_text_kwarg, + set_element=text_subelement, + ), +) +registry.register( + NetworkLinkControl, + item=RegistryItem( + ns_ids=("kml", ), + classes=(KmlDateTime,), + attr_name="expires", + node_name="expires", + get_kwarg=datetime_subelement_kwarg, + set_element=datetime_subelement, + ), +) +registry.register( + NetworkLinkControl, + RegistryItem( + ns_ids=("kml",), + attr_name="view", + node_name="Camera,LookAt", + classes=( + Camera, + LookAt, + ), + get_kwarg=xml_subelement_list_kwarg, + set_element=xml_subelement_list, + ), +) diff --git a/fastkml/update.py b/fastkml/update.py deleted file mode 100644 index 948e02a4..00000000 --- a/fastkml/update.py +++ /dev/null @@ -1,133 +0,0 @@ -# Copyright (C) 2023 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 - -from typing import Any -from typing import Union -from typing import Dict -from typing import Iterable -from typing import List -from typing import Optional -# from fastkml.containers import Folder -from fastkml.helpers import xml_subelement -from fastkml.helpers import xml_subelement_kwarg -from fastkml.registry import registry -from fastkml.registry import RegistryItem -from fastkml.base import _XMLObject -from fastkml.times import TimeSpan - -class Change(_XMLObject): - - def __int__( - self, - target_id: str, - elements: Optional[Union[TimeSpan]] = None - ): - super().__init__( - target_id=target_id, - elements=elements - ) - - def parse_children(self): - pass - -class Create(_XMLObject): - def __int__( - self, - target_id: str, - elements: Optional[Union[TimeSpan]] = None - ): - super().__init__( - target_id=target_id, - elements=elements - ) - - def parse_children(self): - pass - -class Delete(_XMLObject): - def __int__( - self, - target_id: str, - elements: Optional[Union[TimeSpan]] = None - ): - super().__init__( - target_id=target_id, - elements=elements - ) - - def parse_children(self): - pass - -class Update: - - def __init__( - target_href: Optional[str] = None, - change: Optional[Iterable[Change]] = None, - create: Optional[Iterable[Create]] = None, - delete: Optional[Iterable[Delete]] = None - ): - super().__init__( - target_href=target_href, - change=change, - create=create, - delete=delete - ) - -registry.register( - Update, - RegistryItem( - ns_ids=("kml",), - attr_name="change", - node_name="TimeSpan", - classes=( - # PhotoOverlay, - # Folder, - TimeSpan, - ), - get_kwarg=xml_subelement_kwarg, - set_element=xml_subelement, - ) -) -registry.register( - Update, - RegistryItem( - ns_ids=("kml",), - attr_name="create", - node_name="TimeSpan", - classes=( - # PhotoOverlay, - # Folder, - TimeSpan, - ), - get_kwarg=xml_subelement_kwarg, - set_element=xml_subelement, - ) -) -registry.register( - Update, - RegistryItem( - ns_ids=("kml",), - attr_name="delete", - node_name="TimeSpan", - classes=( - # PhotoOverlay, - # Folder, - TimeSpan, - ), - get_kwarg=xml_subelement_kwarg, - set_element=xml_subelement, - ) -) \ No newline at end of file From 4f4a3f0ac184081702cf7ee72d920d5d33e43fd5 Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Sun, 1 Dec 2024 01:24:49 -0500 Subject: [PATCH 34/58] test cases --- fastkml/network_link_control.py | 24 +++++++++ tests/network_link_control_test.py | 85 ++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 tests/network_link_control_test.py diff --git a/fastkml/network_link_control.py b/fastkml/network_link_control.py index 42a599a0..d65569f2 100644 --- a/fastkml/network_link_control.py +++ b/fastkml/network_link_control.py @@ -1,3 +1,20 @@ +# Copyright (C) 2012-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 +import logging + from typing import Any from typing import Dict from typing import Optional @@ -16,6 +33,13 @@ from fastkml.views import Camera, LookAt +__all__ = [ + "NetworkLinkControl", +] + +logger = logging.getLogger(__name__) + + class NetworkLinkControl(_XMLObject): _default_nsid = config.KML diff --git a/tests/network_link_control_test.py b/tests/network_link_control_test.py new file mode 100644 index 00000000..4a5ffe98 --- /dev/null +++ b/tests/network_link_control_test.py @@ -0,0 +1,85 @@ +# Copyright (C) 2021 - 2022 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 + +"""Test the Network Link Control classes.""" + +import datetime + +import pytest + +from fastkml.kml import KML +from fastkml.network_link_control import NetworkLinkControl +from fastkml.times import KmlDateTime +from tests.base import StdLibrary +from fastkml import views + +class TestStdLibrary(StdLibrary): + """Test with the standard library.""" + + def test_network_link_control_obj(self) -> None: + dt = datetime.datetime.now() + kml_datetime = KmlDateTime(dt=dt) + view = views.Camera() + + network_control_obj = NetworkLinkControl( + min_refresh_period=1.1, + max_session_length=100.1, + cookie="cookie", + message="message", + link_name="link_name", + link_description="link_description", + link_snippet="link_snippet", + expires=kml_datetime, + view=view + ) + + assert network_control_obj.min_refresh_period == 1.1 + assert network_control_obj.max_session_length == 100.1 + assert network_control_obj.cookie == "cookie" + assert network_control_obj.message == "message" + assert network_control_obj.link_name == "link_name" + assert network_control_obj.link_description == "link_description" + assert network_control_obj.link_snippet == "link_snippet" + assert network_control_obj.expires == kml_datetime + + assert network_control_obj.view == view + + def test_network_link_control_kml(self) -> None: + doc = ( + '' + "43200" + "-1" + "A Snippet" + "2008-05-30" + "" + ) + + network_control = NetworkLinkControl.from_string(doc) + + dt = datetime.date(2008, 5, 30) + kml_datetime = KmlDateTime(dt=dt) + + network_control_obj = NetworkLinkControl( + min_refresh_period=43200, + max_session_length=-1, + link_snippet="A Snippet", + expires=kml_datetime, + ) + + assert network_control.min_refresh_period == str(network_control_obj.min_refresh_period) + assert network_control.max_session_length == str(network_control_obj.max_session_length) + assert network_control.link_snippet == network_control_obj.link_snippet + assert network_control.expires == network_control_obj.expires \ No newline at end of file From 5f37b544daa49c3c3d253e9a3031b903a6f05b64 Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Sun, 1 Dec 2024 02:06:51 -0500 Subject: [PATCH 35/58] mypt fixes --- fastkml/kml.py | 24 +++++++++++++++++++++--- fastkml/network_link_control.py | 16 ++++++++++------ tests/network_link_control_test.py | 20 +++++++++----------- 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/fastkml/kml.py b/fastkml/kml.py index 121044f5..a5712482 100644 --- a/fastkml/kml.py +++ b/fastkml/kml.py @@ -60,7 +60,14 @@ logger = logging.getLogger(__name__) -kml_children = Union[Folder, Document, Placemark, GroundOverlay, PhotoOverlay, NetworkLinkControl] +kml_children = Union[ + Folder, + Document, + Placemark, + GroundOverlay, + PhotoOverlay, + NetworkLinkControl + ] def lxml_parse_and_validate( @@ -287,8 +294,19 @@ def write( KML, RegistryItem( ns_ids=("kml",), - classes=(Document, Folder, Placemark, GroundOverlay, PhotoOverlay, NetworkLink, NetworkLinkControl), - node_name="Document,Folder,Placemark,GroundOverlay,PhotoOverlay,NetworkLink,NetworkLinkControl", + classes=( + Document, + Folder, + Placemark, + GroundOverlay, + PhotoOverlay, + NetworkLink, + NetworkLinkControl + ), + node_name=( + "Document,Folder,Placemark,GroundOverlay,PhotoOverlay,NetworkLink," + "NetworkLinkControl" + ), attr_name="features", get_kwarg=xml_subelement_list_kwarg, set_element=xml_subelement_list, diff --git a/fastkml/network_link_control.py b/fastkml/network_link_control.py index d65569f2..ab812fbe 100644 --- a/fastkml/network_link_control.py +++ b/fastkml/network_link_control.py @@ -22,6 +22,8 @@ from fastkml import config from fastkml.base import _XMLObject from fastkml.helpers import datetime_subelement +from fastkml.helpers import float_subelement +from fastkml.helpers import subelement_float_kwarg from fastkml.helpers import datetime_subelement_kwarg from fastkml.helpers import subelement_text_kwarg from fastkml.helpers import text_subelement @@ -41,7 +43,7 @@ class NetworkLinkControl(_XMLObject): - + _default_nsid = config.KML min_refresh_period: Optional[float] @@ -54,6 +56,7 @@ class NetworkLinkControl(_XMLObject): expires: Optional[KmlDateTime] view: Union[Camera, LookAt, None] # TODO: Add Update field to the parameters + def __init__( self, ns: Optional[str] = None, @@ -83,7 +86,7 @@ def __init__( view=view, **kwargs ) - + def __repr__(self) -> str: """ Return a string representation of the NetworkLinkControl object. @@ -110,6 +113,7 @@ def __repr__(self) -> str: ")" ) + registry.register( NetworkLinkControl, RegistryItem( @@ -117,8 +121,8 @@ def __repr__(self) -> str: attr_name="min_refresh_period", node_name="minRefreshPeriod", classes=(float,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, + get_kwarg=subelement_float_kwarg, + set_element=float_subelement, ), ) registry.register( @@ -128,8 +132,8 @@ def __repr__(self) -> str: attr_name="max_session_length", node_name="maxSessionLength", classes=(float,), - get_kwarg=subelement_text_kwarg, - set_element=text_subelement, + get_kwarg=subelement_float_kwarg, + set_element=float_subelement, ), ) registry.register( diff --git a/tests/network_link_control_test.py b/tests/network_link_control_test.py index 4a5ffe98..5fc9720e 100644 --- a/tests/network_link_control_test.py +++ b/tests/network_link_control_test.py @@ -18,14 +18,12 @@ import datetime -import pytest - -from fastkml.kml import KML from fastkml.network_link_control import NetworkLinkControl from fastkml.times import KmlDateTime from tests.base import StdLibrary from fastkml import views + class TestStdLibrary(StdLibrary): """Test with the standard library.""" @@ -60,26 +58,26 @@ def test_network_link_control_obj(self) -> None: def test_network_link_control_kml(self) -> None: doc = ( '' - "43200" + "432000" "-1" "A Snippet" "2008-05-30" "" ) - network_control = NetworkLinkControl.from_string(doc) + nc = NetworkLinkControl.from_string(doc) dt = datetime.date(2008, 5, 30) kml_datetime = KmlDateTime(dt=dt) - network_control_obj = NetworkLinkControl( - min_refresh_period=43200, + nc_obj = NetworkLinkControl( + min_refresh_period=432000, max_session_length=-1, link_snippet="A Snippet", expires=kml_datetime, ) - assert network_control.min_refresh_period == str(network_control_obj.min_refresh_period) - assert network_control.max_session_length == str(network_control_obj.max_session_length) - assert network_control.link_snippet == network_control_obj.link_snippet - assert network_control.expires == network_control_obj.expires \ No newline at end of file + assert nc.min_refresh_period == nc_obj.min_refresh_period + assert nc.max_session_length == nc_obj.max_session_length + assert nc.link_snippet == nc_obj.link_snippet + assert nc.expires == nc_obj.expires From 9ece33b45951e3ebc7b5aee482db30bce178afbd Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Sun, 1 Dec 2024 02:08:34 -0500 Subject: [PATCH 36/58] typo --- fastkml/network_link_control.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fastkml/network_link_control.py b/fastkml/network_link_control.py index ab812fbe..1e9d05bd 100644 --- a/fastkml/network_link_control.py +++ b/fastkml/network_link_control.py @@ -54,8 +54,7 @@ class NetworkLinkControl(_XMLObject): link_description: Optional[str] link_snippet: Optional[str] expires: Optional[KmlDateTime] - view: Union[Camera, LookAt, None] # TODO: Add Update field to the parameters - + view: Union[Camera, LookAt, None] # TODO: Add Update field to the parameters def __init__( self, From cd1b7233e99eef8df3d0aea4c57d34c71bd68c2f Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 16:21:16 +0000 Subject: [PATCH 37/58] refactor to allow for empty kml namespace --- _typos.toml | 1 + docs/working_with_kml.rst | 2 +- fastkml/containers.py | 4 +-- fastkml/data.py | 6 ++-- fastkml/features.py | 32 ++++++++++----------- fastkml/geometry.py | 24 ++++++++-------- fastkml/kml.py | 2 +- fastkml/links.py | 16 +++++------ fastkml/model.py | 4 ++- fastkml/overlays.py | 58 +++++++++++++++++++-------------------- fastkml/styles.py | 38 ++++++++++++------------- fastkml/views.py | 38 ++++++++++++------------- 12 files changed, 114 insertions(+), 111 deletions(-) diff --git a/_typos.toml b/_typos.toml index a1cb930e..598e4d1f 100644 --- a/_typos.toml +++ b/_typos.toml @@ -1,6 +1,7 @@ [default.extend-words] lod = "lod" Lod = "Lod" +id ="04AFE6060F147CE66FBD" [files] extend-exclude = ["tests/ogc_conformance/data/kml/*.kml"] diff --git a/docs/working_with_kml.rst b/docs/working_with_kml.rst index 3da5942e..f2f0639d 100644 --- a/docs/working_with_kml.rst +++ b/docs/working_with_kml.rst @@ -123,7 +123,7 @@ We need to register the attributes of the KML object to be able to parse it: >>> registry.register( ... CascadingStyle, ... RegistryItem( - ... ns_ids=("kml",), + ... ns_ids=("kml", ""), ... attr_name="style", ... node_name="Style", ... classes=(Style,), diff --git a/fastkml/containers.py b/fastkml/containers.py index 8d0f6cfe..1a3e7737 100644 --- a/fastkml/containers.py +++ b/fastkml/containers.py @@ -329,7 +329,7 @@ def get_style_by_url(self, style_url: str) -> Optional[Union[Style, StyleMap]]: registry.register( _Container, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="features", node_name=( "Folder,Placemark,Document,GroundOverlay,PhotoOverlay,ScreenOverlay," @@ -351,7 +351,7 @@ def get_style_by_url(self, style_url: str) -> Optional[Union[Style, StyleMap]]: registry.register( Document, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="schemata", node_name="Schema", classes=(Schema,), diff --git a/fastkml/data.py b/fastkml/data.py index 20e80575..573ca074 100644 --- a/fastkml/data.py +++ b/fastkml/data.py @@ -311,7 +311,7 @@ def append(self, field: SimpleField) -> None: registry.register( Schema, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="fields", node_name="SimpleField", classes=(SimpleField,), @@ -644,7 +644,7 @@ def append_data(self, data: SimpleData) -> None: registry.register( SchemaData, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="data", node_name="SimpleData", classes=(SimpleData,), @@ -725,7 +725,7 @@ def __bool__(self) -> bool: registry.register( ExtendedData, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="elements", node_name="Data,SchemaData", classes=( diff --git a/fastkml/features.py b/fastkml/features.py index e8635bd2..9b19997a 100644 --- a/fastkml/features.py +++ b/fastkml/features.py @@ -309,7 +309,7 @@ def __init__( registry.register( _Feature, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="name", node_name="name", classes=(str,), @@ -320,7 +320,7 @@ def __init__( registry.register( _Feature, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="visibility", node_name="visibility", classes=(bool,), @@ -332,7 +332,7 @@ def __init__( registry.register( _Feature, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="isopen", node_name="open", classes=(bool,), @@ -366,7 +366,7 @@ def __init__( registry.register( _Feature, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="address", node_name="address", classes=(str,), @@ -377,7 +377,7 @@ def __init__( registry.register( _Feature, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="phone_number", node_name="phoneNumber", classes=(str,), @@ -388,7 +388,7 @@ def __init__( registry.register( _Feature, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="snippet", node_name="Snippet", classes=(Snippet,), @@ -399,7 +399,7 @@ def __init__( registry.register( _Feature, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="description", node_name="description", classes=(str,), @@ -410,7 +410,7 @@ def __init__( registry.register( _Feature, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="view", node_name="Camera,LookAt", classes=( @@ -424,7 +424,7 @@ def __init__( registry.register( _Feature, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="times", node_name="TimeSpan,TimeStamp", classes=( @@ -438,7 +438,7 @@ def __init__( registry.register( _Feature, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="style_url", node_name="styleUrl", classes=(StyleUrl,), @@ -449,7 +449,7 @@ def __init__( registry.register( _Feature, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="styles", node_name="Style,StyleMap", classes=( @@ -463,7 +463,7 @@ def __init__( registry.register( _Feature, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="region", node_name="region", classes=(Region,), @@ -474,7 +474,7 @@ def __init__( registry.register( _Feature, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="extended_data", node_name="ExtendedData", classes=(ExtendedData,), @@ -891,7 +891,7 @@ def __bool__(self) -> bool: registry.register( NetworkLink, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="refresh_visibility", node_name="refreshVisibility", classes=(bool,), @@ -903,7 +903,7 @@ def __bool__(self) -> bool: registry.register( NetworkLink, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="fly_to_view", node_name="flyToView", classes=(bool,), @@ -915,7 +915,7 @@ def __bool__(self) -> bool: registry.register( NetworkLink, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="link", node_name="Link", classes=(Link,), diff --git a/fastkml/geometry.py b/fastkml/geometry.py index ed20c6d9..1b62b3ab 100644 --- a/fastkml/geometry.py +++ b/fastkml/geometry.py @@ -300,7 +300,7 @@ def get_tag_name(cls) -> str: registry.register( Coordinates, item=RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), classes=(LineType,), # type: ignore[arg-type] attr_name="coords", node_name="coordinates", @@ -501,7 +501,7 @@ def geometry(self) -> Optional[geo.Point]: registry.register( Point, item=RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), classes=(bool,), attr_name="extrude", node_name="extrude", @@ -525,7 +525,7 @@ def geometry(self) -> Optional[geo.Point]: registry.register( Point, item=RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), classes=(Coordinates,), attr_name="kml_coordinates", node_name="coordinates", @@ -668,7 +668,7 @@ def geometry(self) -> Optional[geo.LineString]: registry.register( LineString, item=RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), classes=(bool,), attr_name="extrude", node_name="extrude", @@ -680,7 +680,7 @@ def geometry(self) -> Optional[geo.LineString]: registry.register( LineString, item=RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), classes=(bool,), attr_name="tessellate", node_name="tessellate", @@ -704,7 +704,7 @@ def geometry(self) -> Optional[geo.LineString]: registry.register( LineString, item=RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), classes=(Coordinates,), attr_name="kml_coordinates", node_name="coordinates", @@ -936,7 +936,7 @@ def get_tag_name(cls) -> str: registry.register( BoundaryIs, item=RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), classes=(LinearRing,), attr_name="kml_geometry", node_name="LinearRing", @@ -1134,7 +1134,7 @@ def __eq__(self, other: object) -> bool: registry.register( Polygon, item=RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), classes=(bool,), attr_name="extrude", node_name="extrude", @@ -1146,7 +1146,7 @@ def __eq__(self, other: object) -> bool: registry.register( Polygon, item=RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), classes=(bool,), attr_name="tessellate", node_name="tessellate", @@ -1170,7 +1170,7 @@ def __eq__(self, other: object) -> bool: registry.register( Polygon, item=RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), classes=(OuterBoundaryIs,), attr_name="outer_boundary", node_name="outerBoundaryIs", @@ -1181,7 +1181,7 @@ def __eq__(self, other: object) -> bool: registry.register( Polygon, item=RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), classes=(InnerBoundaryIs,), attr_name="inner_boundaries", node_name="innerBoundaryIs", @@ -1344,7 +1344,7 @@ def geometry(self) -> Optional[MultiGeometryType]: registry.register( MultiGeometry, item=RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), classes=(Point, LineString, Polygon, LinearRing, MultiGeometry), attr_name="kml_geometries", node_name="(Point|LineString|Polygon|LinearRing|MultiGeometry)", diff --git a/fastkml/kml.py b/fastkml/kml.py index 0886c6ba..aa9c4db1 100644 --- a/fastkml/kml.py +++ b/fastkml/kml.py @@ -285,7 +285,7 @@ def write( registry.register( KML, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), classes=(Document, Folder, Placemark, GroundOverlay, PhotoOverlay, NetworkLink), node_name="Document,Folder,Placemark,GroundOverlay,PhotoOverlay,NetworkLink", attr_name="features", diff --git a/fastkml/links.py b/fastkml/links.py index 3553cb07..8347f069 100644 --- a/fastkml/links.py +++ b/fastkml/links.py @@ -126,7 +126,7 @@ def __bool__(self) -> bool: registry.register( Link, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="href", node_name="href", classes=(str,), @@ -137,7 +137,7 @@ def __bool__(self) -> bool: registry.register( Link, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="refresh_mode", node_name="refreshMode", classes=(RefreshMode,), @@ -149,7 +149,7 @@ def __bool__(self) -> bool: registry.register( Link, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="refresh_interval", node_name="refreshInterval", classes=(float,), @@ -161,7 +161,7 @@ def __bool__(self) -> bool: registry.register( Link, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="view_refresh_mode", node_name="viewRefreshMode", classes=(ViewRefreshMode,), @@ -173,7 +173,7 @@ def __bool__(self) -> bool: registry.register( Link, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="view_refresh_time", node_name="viewRefreshTime", classes=(float,), @@ -185,7 +185,7 @@ def __bool__(self) -> bool: registry.register( Link, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="view_bound_scale", node_name="viewBoundScale", classes=(float,), @@ -197,7 +197,7 @@ def __bool__(self) -> bool: registry.register( Link, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="view_format", node_name="viewFormat", classes=(str,), @@ -209,7 +209,7 @@ def __bool__(self) -> bool: registry.register( Link, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="http_query", node_name="httpQuery", classes=(str,), diff --git a/fastkml/model.py b/fastkml/model.py index 06c395f5..fe16902a 100644 --- a/fastkml/model.py +++ b/fastkml/model.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2023 Christian Ledermann +# 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 @@ -46,6 +46,8 @@ from fastkml.registry import RegistryItem from fastkml.registry import registry +__all__ = ["Alias", "Location", "Model", "Orientation", "ResourceMap", "Scale"] + class Location(_XMLObject): """Represents a location in KML.""" diff --git a/fastkml/overlays.py b/fastkml/overlays.py index 9b6bde8a..48bfcdd9 100644 --- a/fastkml/overlays.py +++ b/fastkml/overlays.py @@ -229,7 +229,7 @@ def __init__( registry.register( _Overlay, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="color", node_name="color", classes=(str,), @@ -241,7 +241,7 @@ def __init__( registry.register( _Overlay, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="draw_order", node_name="drawOrder", classes=(int,), @@ -253,7 +253,7 @@ def __init__( registry.register( _Overlay, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="icon", node_name="Icon", classes=(Icon,), @@ -380,7 +380,7 @@ def __bool__(self) -> bool: registry.register( ViewVolume, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="left_fov", node_name="leftFov", classes=(float,), @@ -392,7 +392,7 @@ def __bool__(self) -> bool: registry.register( ViewVolume, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="right_fov", node_name="rightFov", classes=(float,), @@ -404,7 +404,7 @@ def __bool__(self) -> bool: registry.register( ViewVolume, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="bottom_fov", node_name="bottomFov", classes=(float,), @@ -416,7 +416,7 @@ def __bool__(self) -> bool: registry.register( ViewVolume, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="top_fov", node_name="topFov", classes=(float,), @@ -428,7 +428,7 @@ def __bool__(self) -> bool: registry.register( ViewVolume, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="near", node_name="near", classes=(float,), @@ -550,7 +550,7 @@ def __bool__(self) -> bool: registry.register( ImagePyramid, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="tile_size", node_name="tileSize", classes=(int,), @@ -562,7 +562,7 @@ def __bool__(self) -> bool: registry.register( ImagePyramid, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="max_width", node_name="maxWidth", classes=(int,), @@ -573,7 +573,7 @@ def __bool__(self) -> bool: registry.register( ImagePyramid, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="max_height", node_name="maxHeight", classes=(int,), @@ -584,7 +584,7 @@ def __bool__(self) -> bool: registry.register( ImagePyramid, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="grid_origin", node_name="gridOrigin", classes=(GridOrigin,), @@ -821,7 +821,7 @@ def __repr__(self) -> str: registry.register( PhotoOverlay, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="rotation", node_name="rotation", classes=(float,), @@ -833,7 +833,7 @@ def __repr__(self) -> str: registry.register( PhotoOverlay, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="view_volume", node_name="ViewVolume", classes=(ViewVolume,), @@ -844,7 +844,7 @@ def __repr__(self) -> str: registry.register( PhotoOverlay, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="image_pyramid", node_name="ImagePyramid", classes=(ImagePyramid,), @@ -855,7 +855,7 @@ def __repr__(self) -> str: registry.register( PhotoOverlay, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="point", node_name="Point", classes=(Point,), @@ -866,7 +866,7 @@ def __repr__(self) -> str: registry.register( PhotoOverlay, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="shape", node_name="shape", classes=(Shape,), @@ -992,7 +992,7 @@ def __bool__(self) -> bool: registry.register( LatLonBox, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="north", node_name="north", classes=(float,), @@ -1003,7 +1003,7 @@ def __bool__(self) -> bool: registry.register( LatLonBox, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="south", node_name="south", classes=(float,), @@ -1014,7 +1014,7 @@ def __bool__(self) -> bool: registry.register( LatLonBox, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="east", node_name="east", classes=(float,), @@ -1025,7 +1025,7 @@ def __bool__(self) -> bool: registry.register( LatLonBox, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="west", node_name="west", classes=(float,), @@ -1036,7 +1036,7 @@ def __bool__(self) -> bool: registry.register( LatLonBox, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="rotation", node_name="rotation", classes=(float,), @@ -1241,7 +1241,7 @@ def __repr__(self) -> str: registry.register( GroundOverlay, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="altitude", node_name="altitude", classes=(float,), @@ -1265,7 +1265,7 @@ def __repr__(self) -> str: registry.register( GroundOverlay, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="lat_lon_box", node_name="LatLonBox", classes=(LatLonBox,), @@ -1622,7 +1622,7 @@ def __repr__(self) -> str: registry.register( ScreenOverlay, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="overlay_xy", node_name="overlayXY", classes=(OverlayXY,), @@ -1633,7 +1633,7 @@ def __repr__(self) -> str: registry.register( ScreenOverlay, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="screen_xy", node_name="screenXY", classes=(ScreenXY,), @@ -1644,7 +1644,7 @@ def __repr__(self) -> str: registry.register( ScreenOverlay, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="rotation_xy", node_name="rotationXY", classes=(RotationXY,), @@ -1655,7 +1655,7 @@ def __repr__(self) -> str: registry.register( ScreenOverlay, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="size", node_name="size", classes=(Size,), @@ -1666,7 +1666,7 @@ def __repr__(self) -> str: registry.register( ScreenOverlay, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="rotation", node_name="rotation", classes=(float,), diff --git a/fastkml/styles.py b/fastkml/styles.py index 023517a8..b5b9292a 100644 --- a/fastkml/styles.py +++ b/fastkml/styles.py @@ -156,7 +156,7 @@ def get_tag_name(cls) -> str: registry.register( StyleUrl, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="url", node_name="styleUrl", classes=(str,), @@ -242,7 +242,7 @@ def __init__( registry.register( _ColorStyle, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="color", node_name="color", classes=(str,), @@ -254,7 +254,7 @@ def __init__( registry.register( _ColorStyle, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="color_mode", node_name="colorMode", classes=(ColorMode,), @@ -526,7 +526,7 @@ def icon_href(self) -> Optional[str]: registry.register( IconStyle, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="scale", node_name="scale", classes=(float,), @@ -538,7 +538,7 @@ def icon_href(self) -> Optional[str]: registry.register( IconStyle, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="heading", node_name="heading", classes=(float,), @@ -550,7 +550,7 @@ def icon_href(self) -> Optional[str]: registry.register( IconStyle, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="icon", node_name="Icon", classes=(Icon,), @@ -561,7 +561,7 @@ def icon_href(self) -> Optional[str]: registry.register( IconStyle, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="hot_spot", node_name="hotSpot", classes=(HotSpot,), @@ -659,7 +659,7 @@ def __bool__(self) -> bool: registry.register( LineStyle, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="width", node_name="width", classes=(float,), @@ -768,7 +768,7 @@ def __bool__(self) -> bool: registry.register( PolyStyle, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="fill", node_name="fill", classes=(bool,), @@ -780,7 +780,7 @@ def __bool__(self) -> bool: registry.register( PolyStyle, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="outline", node_name="outline", classes=(bool,), @@ -882,7 +882,7 @@ def __bool__(self) -> bool: registry.register( LabelStyle, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="scale", node_name="scale", classes=(float,), @@ -1034,7 +1034,7 @@ def __bool__(self) -> bool: registry.register( BalloonStyle, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="bg_color", node_name="bgColor", classes=(str,), @@ -1046,7 +1046,7 @@ def __bool__(self) -> bool: registry.register( BalloonStyle, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="text_color", node_name="textColor", classes=(str,), @@ -1058,7 +1058,7 @@ def __bool__(self) -> bool: registry.register( BalloonStyle, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="text", node_name="text", classes=(str,), @@ -1069,7 +1069,7 @@ def __bool__(self) -> bool: registry.register( BalloonStyle, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="display_mode", node_name="displayMode", classes=(DisplayMode,), @@ -1161,7 +1161,7 @@ def __bool__(self) -> bool: registry.register( Style, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="styles", node_name="Style", classes=( @@ -1271,7 +1271,7 @@ def __bool__(self) -> bool: registry.register( Pair, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="key", node_name="key", classes=(PairKey,), @@ -1282,7 +1282,7 @@ def __bool__(self) -> bool: registry.register( Pair, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="style", node_name="Style", classes=( @@ -1404,7 +1404,7 @@ def highlight(self) -> Optional[Union[StyleUrl, Style]]: registry.register( StyleMap, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="pairs", node_name="Pair", classes=(Pair,), diff --git a/fastkml/views.py b/fastkml/views.py index 16ac27fe..57b30481 100644 --- a/fastkml/views.py +++ b/fastkml/views.py @@ -153,7 +153,7 @@ def __init__( registry.register( _AbstractView, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="longitude", node_name="longitude", classes=(float,), @@ -165,7 +165,7 @@ def __init__( registry.register( _AbstractView, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="latitude", node_name="latitude", classes=(float,), @@ -177,7 +177,7 @@ def __init__( registry.register( _AbstractView, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="altitude", node_name="altitude", classes=(float,), @@ -189,7 +189,7 @@ def __init__( registry.register( _AbstractView, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="heading", node_name="heading", classes=(float,), @@ -201,7 +201,7 @@ def __init__( registry.register( _AbstractView, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="tilt", node_name="tilt", classes=(float,), @@ -317,7 +317,7 @@ def __repr__(self) -> str: registry.register( Camera, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="roll", node_name="roll", classes=(float,), @@ -432,7 +432,7 @@ def __repr__(self) -> str: registry.register( LookAt, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="range", node_name="range", classes=(float,), @@ -554,7 +554,7 @@ def __bool__(self) -> bool: registry.register( LatLonAltBox, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="north", node_name="north", classes=(float,), @@ -565,7 +565,7 @@ def __bool__(self) -> bool: registry.register( LatLonAltBox, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="south", node_name="south", classes=(float,), @@ -576,7 +576,7 @@ def __bool__(self) -> bool: registry.register( LatLonAltBox, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="east", node_name="east", classes=(float,), @@ -587,7 +587,7 @@ def __bool__(self) -> bool: registry.register( LatLonAltBox, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="west", node_name="west", classes=(float,), @@ -598,7 +598,7 @@ def __bool__(self) -> bool: registry.register( LatLonAltBox, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="min_altitude", node_name="minAltitude", classes=(float,), @@ -610,7 +610,7 @@ def __bool__(self) -> bool: registry.register( LatLonAltBox, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="max_altitude", node_name="maxAltitude", classes=(float,), @@ -716,7 +716,7 @@ def __bool__(self) -> bool: registry.register( Lod, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="min_lod_pixels", node_name="minLodPixels", classes=(float,), @@ -728,7 +728,7 @@ def __bool__(self) -> bool: registry.register( Lod, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="max_lod_pixels", node_name="maxLodPixels", classes=(float,), @@ -740,7 +740,7 @@ def __bool__(self) -> bool: registry.register( Lod, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="min_fade_extent", node_name="minFadeExtent", classes=(float,), @@ -752,7 +752,7 @@ def __bool__(self) -> bool: registry.register( Lod, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="max_fade_extent", node_name="maxFadeExtent", classes=(float,), @@ -852,7 +852,7 @@ def __bool__(self) -> bool: registry.register( Region, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="lat_lon_alt_box", node_name="LatLonAltBox", classes=(LatLonAltBox,), @@ -863,7 +863,7 @@ def __bool__(self) -> bool: registry.register( Region, RegistryItem( - ns_ids=("kml",), + ns_ids=("kml", ""), attr_name="lod", node_name="Lod", classes=(Lod,), From df56d564ccdd4421134f0bb35c4598c5e1f6c5a2 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 16:39:01 +0000 Subject: [PATCH 38/58] add ignore identifiers for typos in KML namespace --- _typos.toml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/_typos.toml b/_typos.toml index 598e4d1f..269e7f28 100644 --- a/_typos.toml +++ b/_typos.toml @@ -1,7 +1,15 @@ +[default] +extend-ignore-identifiers-re = [ + "04AFE6060F147CE66FBD", + "Lod", + "lod", +] + + + [default.extend-words] lod = "lod" Lod = "Lod" -id ="04AFE6060F147CE66FBD" [files] extend-exclude = ["tests/ogc_conformance/data/kml/*.kml"] From 79e500f52fe6330183e13efa9016cafdf307b884 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 16:41:51 +0000 Subject: [PATCH 39/58] fix typo --- docs/working_with_kml.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/working_with_kml.rst b/docs/working_with_kml.rst index eee1dd55..ebb7ef6b 100644 --- a/docs/working_with_kml.rst +++ b/docs/working_with_kml.rst @@ -236,7 +236,7 @@ Now we can remove the CascadingStyle from the document and have a look at the re hide - + Ort1 10.06256752902339 From e8ccb0495a0bd704e9a9cb0cf88271ec39afe1e1 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 18:04:07 +0000 Subject: [PATCH 40/58] add default KML namespace identifier to model classes and implement hypothesis tests for KML model --- fastkml/model.py | 59 ++++++- tests/hypothesis/model_test.py | 288 +++++++++++++++++++++++++++++++++ 2 files changed, 339 insertions(+), 8 deletions(-) create mode 100644 tests/hypothesis/model_test.py diff --git a/fastkml/model.py b/fastkml/model.py index fe16902a..0cbd0a21 100644 --- a/fastkml/model.py +++ b/fastkml/model.py @@ -23,11 +23,13 @@ """ +from typing import Any from typing import Dict from typing import Iterable from typing import List from typing import Optional +from fastkml import config from fastkml.base import _XMLObject from fastkml.enums import AltitudeMode from fastkml.helpers import clean_string @@ -52,6 +54,8 @@ class Location(_XMLObject): """Represents a location in KML.""" + _default_nsid = config.KML + latitude: Optional[float] longitude: Optional[float] altitude: Optional[float] @@ -63,14 +67,16 @@ def __init__( 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) + super().__init__(ns=ns, name_spaces=name_spaces, **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: @@ -82,6 +88,7 @@ def __repr__(self) -> str: f"altitude={self.altitude!r}, " f"latitude={self.latitude!r}, " f"longitude={self.longitude!r}, " + f"**{self._get_splat()!r}," ")" ) @@ -124,6 +131,8 @@ def __repr__(self) -> str: class Orientation(_XMLObject): """Represents an orientation in KML.""" + _default_nsid = config.KML + heading: Optional[float] tilt: Optional[float] roll: Optional[float] @@ -135,13 +144,16 @@ def __init__( heading: Optional[float] = None, tilt: Optional[float] = None, roll: Optional[float] = None, + **kwargs: Any, ) -> None: - super().__init__(ns=ns, name_spaces=name_spaces) + """Create a new Orientation.""" + super().__init__(ns=ns, name_spaces=name_spaces, **kwargs) self.heading = heading self.tilt = tilt self.roll = roll def __bool__(self) -> bool: + """Return True if heading, tilt, and roll are set.""" return any( (self.heading is not None, self.tilt is not None, self.roll is not None), ) @@ -155,6 +167,7 @@ def __repr__(self) -> str: f"heading={self.heading!r}, " f"tilt={self.tilt!r}, " f"roll={self.roll!r}, " + f"**{self._get_splat()!r}," ")" ) @@ -197,6 +210,8 @@ def __repr__(self) -> str: class Scale(_XMLObject): """Represents a scale in KML.""" + _default_nsid = config.KML + x: Optional[float] y: Optional[float] z: Optional[float] @@ -208,13 +223,16 @@ def __init__( x: Optional[float] = None, y: Optional[float] = None, z: Optional[float] = None, + **kwargs: Any, ) -> None: - super().__init__(ns=ns, name_spaces=name_spaces) + """Create a new Scale.""" + super().__init__(ns=ns, name_spaces=name_spaces, **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: @@ -226,6 +244,7 @@ def __repr__(self) -> str: f"x={self.x!r}, " f"y={self.y!r}, " f"z={self.z!r}, " + f"**{self._get_splat()!r}," ")" ) @@ -268,6 +287,8 @@ def __repr__(self) -> str: class Alias(_XMLObject): """Represents an alias in KML.""" + _default_nsid = config.KML + target_href: Optional[str] source_href: Optional[str] @@ -277,13 +298,16 @@ def __init__( name_spaces: Optional[Dict[str, str]] = None, target_href: Optional[str] = None, source_href: Optional[str] = None, + **kwargs: Any, ) -> None: - super().__init__(ns=ns, name_spaces=name_spaces) + """Create a new Alias.""" + super().__init__(ns=ns, name_spaces=name_spaces, **kwargs) self.target_href = clean_string(target_href) self.source_href = clean_string(source_href) def __bool__(self) -> bool: - return all((self.target_href is not None, self.source_href is not None)) + """Return True if target_href and 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.""" @@ -293,6 +317,7 @@ def __repr__(self) -> str: f"name_spaces={self.name_spaces!r}, " f"target_href={self.target_href!r}, " f"source_href={self.source_href!r}, " + f"**{self._get_splat()!r}," ")" ) @@ -324,6 +349,8 @@ def __repr__(self) -> str: class ResourceMap(_XMLObject): """Represents a resource map in KML.""" + _default_nsid = config.KML + aliases: List[Alias] def __init__( @@ -331,11 +358,14 @@ def __init__( ns: Optional[str] = None, name_spaces: Optional[Dict[str, str]] = None, aliases: Optional[Iterable[Alias]] = None, + **kwargs: Any, ) -> None: - super().__init__(ns=ns, name_spaces=name_spaces) + """Create a new ResourceMap.""" + super().__init__(ns=ns, name_spaces=name_spaces, **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: @@ -345,6 +375,7 @@ def __repr__(self) -> str: f"ns={self.ns!r}, " f"name_spaces={self.name_spaces!r}, " f"aliases={self.aliases!r}, " + f"**{self._get_splat()!r}," ")" ) @@ -384,8 +415,16 @@ def __init__( scale: Optional[Scale] = None, link: Optional[Link] = None, resource_map: Optional[ResourceMap] = None, + **kwargs: Any, ) -> None: - super().__init__(ns=ns, name_spaces=name_spaces, id=id, target_id=target_id) + """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 @@ -394,13 +433,17 @@ def __init__( self.resource_map = resource_map def __bool__(self) -> bool: + """Return True if link is set.""" return bool(self.link) 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}, " @@ -415,7 +458,7 @@ def __repr__(self) -> str: registry.register( Model, RegistryItem( - ns_ids=("kml", ""), + ns_ids=("kml", "gx", ""), attr_name="altitude_mode", node_name="altitudeMode", classes=(AltitudeMode,), diff --git a/tests/hypothesis/model_test.py b/tests/hypothesis/model_test.py new file mode 100644 index 00000000..ee318107 --- /dev/null +++ b/tests/hypothesis/model_test.py @@ -0,0 +1,288 @@ +# 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 +"""Hypothesis tests for the fastkml.model module.""" + +import typing + +from hypothesis import given +from hypothesis import strategies as st +from hypothesis.provisional import urls + +import fastkml +import fastkml.enums +import fastkml.links +import fastkml.model +from tests.base import Lxml +from tests.hypothesis.common import assert_repr_roundtrip +from tests.hypothesis.common import assert_str_roundtrip +from tests.hypothesis.common import assert_str_roundtrip_terse +from tests.hypothesis.common import assert_str_roundtrip_verbose +from tests.hypothesis.strategies import nc_name + + +class TestLxml(Lxml): + @given( + altitude=st.one_of( + st.none(), + st.just(0.0), + st.floats(allow_nan=False, allow_infinity=False).filter(lambda x: x != 0), + ), + latitude=st.one_of( + st.none(), + st.just(0.0), + st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-90, + max_value=90, + ), + ), + longitude=st.one_of( + st.none(), + st.just(0.0), + st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-180, + max_value=180, + ), + ), + ) + def test_fuzz_location( + self, + altitude: typing.Optional[float], + latitude: typing.Optional[float], + longitude: typing.Optional[float], + ) -> None: + location = fastkml.model.Location( + altitude=altitude, + latitude=latitude, + longitude=longitude, + ) + + assert_repr_roundtrip(location) + assert_str_roundtrip(location) + assert_str_roundtrip_terse(location) + assert_str_roundtrip_verbose(location) + + @given( + heading=st.one_of( + st.none(), + st.just(0.0), + st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-360, + max_value=360, + ).filter(lambda x: x != 0), + ), + tilt=st.one_of( + st.none(), + st.just(0.0), + st.floats( + allow_nan=False, + allow_infinity=False, + min_value=0, + max_value=180, + ).filter(lambda x: x != 0), + ), + roll=st.one_of( + st.none(), + st.just(0.0), + st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-180, + max_value=180, + ).filter(lambda x: x != 0), + ), + ) + def test_fuzz_orientation( + self, + heading: typing.Optional[float], + tilt: typing.Optional[float], + roll: typing.Optional[float], + ) -> None: + orientation = fastkml.model.Orientation(heading=heading, tilt=tilt, roll=roll) + + assert_repr_roundtrip(orientation) + assert_str_roundtrip(orientation) + assert_str_roundtrip_terse(orientation) + assert_str_roundtrip_verbose(orientation) + + @given( + x=st.one_of(st.none(), st.floats(allow_nan=False, allow_infinity=False)), + y=st.one_of(st.none(), st.floats(allow_nan=False, allow_infinity=False)), + z=st.one_of(st.none(), st.floats(allow_nan=False, allow_infinity=False)), + ) + def test_fuzz_scale( + self, + x: typing.Optional[float], + y: typing.Optional[float], + z: typing.Optional[float], + ) -> None: + scale = fastkml.model.Scale(x=x, y=y, z=z) + + assert_repr_roundtrip(scale) + assert_str_roundtrip(scale) + assert_str_roundtrip_terse(scale) + assert_str_roundtrip_verbose(scale) + + @given( + target_href=st.one_of(st.none(), urls()), + source_href=st.one_of(st.none(), urls()), + ) + def test_fuzz_alias( + self, + target_href: typing.Optional[str], + source_href: typing.Optional[str], + ) -> None: + alias = fastkml.model.Alias(target_href=target_href, source_href=source_href) + + assert_repr_roundtrip(alias) + assert_str_roundtrip(alias) + assert_str_roundtrip_terse(alias) + assert_str_roundtrip_verbose(alias) + + @given( + aliases=st.one_of( + st.none(), + st.lists( + st.builds( + fastkml.model.Alias, + source_href=urls(), + target_href=urls(), + ), + ), + ), + ) + def test_fuzz_resource_map( + self, + aliases: typing.Optional[typing.Iterable[fastkml.model.Alias]], + ) -> None: + resource_map = fastkml.model.ResourceMap(aliases=aliases) + + assert_repr_roundtrip(resource_map) + assert_str_roundtrip(resource_map) + assert_str_roundtrip_terse(resource_map) + assert_str_roundtrip_verbose(resource_map) + + @given( + id=st.one_of(st.none(), nc_name()), + target_id=st.one_of(st.none(), nc_name()), + altitude_mode=st.one_of(st.none(), st.sampled_from(fastkml.enums.AltitudeMode)), + location=st.one_of( + st.none(), + st.builds( + fastkml.model.Location, + altitude=st.floats(allow_nan=False, allow_infinity=False).filter( + lambda x: x != 0, + ), + latitude=st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-90, + max_value=90, + ), + longitude=st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-180, + max_value=180, + ), + ), + ), + orientation=st.one_of( + st.none(), + st.builds( + fastkml.model.Orientation, + heading=st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-360, + max_value=360, + ).filter(lambda x: x != 0), + tilt=st.floats( + allow_nan=False, + allow_infinity=False, + min_value=0, + max_value=180, + ).filter(lambda x: x != 0), + roll=st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-180, + max_value=180, + ).filter(lambda x: x != 0), + ), + ), + scale=st.one_of( + st.none(), + st.builds( + fastkml.model.Scale, + x=st.floats(allow_nan=False, allow_infinity=False).filter( + lambda x: x != 0, + ), + y=st.floats(allow_nan=False, allow_infinity=False).filter( + lambda x: x != 0, + ), + z=st.floats(allow_nan=False, allow_infinity=False).filter( + lambda x: x != 0, + ), + ), + ), + link=st.one_of(st.none(), st.builds(fastkml.Link, href=urls())), + resource_map=st.one_of( + st.none(), + st.builds( + fastkml.model.ResourceMap, + aliases=st.lists( + st.builds( + fastkml.model.Alias, + source_href=urls(), + target_href=urls(), + ), + min_size=1, + ), + ), + ), + ) + def test_fuzz_model( + self, + id: typing.Optional[str], + target_id: typing.Optional[str], + altitude_mode: typing.Optional[fastkml.enums.AltitudeMode], + location: typing.Optional[fastkml.model.Location], + orientation: typing.Optional[fastkml.model.Orientation], + scale: typing.Optional[fastkml.model.Scale], + link: typing.Optional[fastkml.Link], + resource_map: typing.Optional[fastkml.model.ResourceMap], + ) -> None: + model = fastkml.model.Model( + id=id, + target_id=target_id, + altitude_mode=altitude_mode, + location=location, + orientation=orientation, + scale=scale, + link=link, + resource_map=resource_map, + ) + + assert_repr_roundtrip(model) + assert_str_roundtrip(model) + assert_str_roundtrip_terse(model) + assert_str_roundtrip_verbose(model) From aabc0622dacff5f7b5c3d9211d3acc38d0b2e2f3 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 18:34:28 +0000 Subject: [PATCH 41/58] add geometry property to Model and Location classes; update registry to include Model in KML geometry types --- fastkml/features.py | 5 +++- fastkml/model.py | 18 ++++++++++++++- tests/hypothesis/feature_test.py | 39 ++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/fastkml/features.py b/fastkml/features.py index 9b19997a..2c557ed2 100644 --- a/fastkml/features.py +++ b/fastkml/features.py @@ -58,6 +58,7 @@ from fastkml.kml_base import _BaseObject from fastkml.links import Link from fastkml.mixins import TimeMixin +from fastkml.model import Model from fastkml.registry import RegistryItem from fastkml.registry import registry from fastkml.styles import Style @@ -78,6 +79,7 @@ LineString, LinearRing, Polygon, + Model, MultiGeometry, gx.MultiTrack, gx.Track, @@ -665,7 +667,7 @@ def geometry(self) -> Optional[AnyGeometryType]: ns_ids=("kml", "gx"), attr_name="kml_geometry", node_name=( - "Point,LineString,LinearRing,Polygon,MultiGeometry," + "Point,LineString,LinearRing,Polygon,MultiGeometry,Model," "gx:MultiTrack,gx:Track" ), classes=( @@ -674,6 +676,7 @@ def geometry(self) -> Optional[AnyGeometryType]: LinearRing, Polygon, MultiGeometry, + Model, gx.MultiTrack, gx.Track, ), diff --git a/fastkml/model.py b/fastkml/model.py index 0cbd0a21..2fe2cee7 100644 --- a/fastkml/model.py +++ b/fastkml/model.py @@ -29,6 +29,8 @@ from typing import List from typing import Optional +from pygeoif.geometry import Point + from fastkml import config from fastkml.base import _XMLObject from fastkml.enums import AltitudeMode @@ -92,6 +94,15 @@ def __repr__(self) -> str: ")" ) + @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, @@ -434,7 +445,7 @@ def __init__( def __bool__(self) -> bool: """Return True if link is set.""" - return bool(self.link) + return all((self.link, self.location)) def __repr__(self) -> str: """Create a string representation for Model.""" @@ -454,6 +465,11 @@ def __repr__(self) -> str: ")" ) + @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, diff --git a/tests/hypothesis/feature_test.py b/tests/hypothesis/feature_test.py index 6f289c45..2751cc99 100644 --- a/tests/hypothesis/feature_test.py +++ b/tests/hypothesis/feature_test.py @@ -29,6 +29,7 @@ import fastkml.features import fastkml.gx import fastkml.links +import fastkml.model import fastkml.styles import fastkml.views from tests.base import Lxml @@ -419,6 +420,44 @@ def test_fuzz_placemark_styles( assert_str_roundtrip_terse(placemark) assert_str_roundtrip_verbose(placemark) + @given( + model=st.builds( + fastkml.model.Model, + altitude_mode=st.sampled_from(fastkml.enums.AltitudeMode), + location=st.builds( + fastkml.model.Location, + latitude=st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-90, + max_value=90, + ).filter(lambda x: x != 0), + longitude=st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-180, + max_value=180, + ).filter(lambda x: x != 0), + altitude=st.floats(allow_nan=False, allow_infinity=False).filter( + lambda x: x != 0, + ), + ), + link=st.builds(fastkml.Link, href=urls()), + ), + ) + def fuzz_placemark_model( + self, + model: fastkml.model.Model, + ) -> None: + placemark = fastkml.Placemark( + kml_geometry=model, + ) + + assert_repr_roundtrip(placemark) + assert_str_roundtrip(placemark) + assert_str_roundtrip_terse(placemark) + assert_str_roundtrip_verbose(placemark) + @given( refresh_visibility=st.one_of(st.none(), st.booleans()), fly_to_view=st.one_of(st.none(), st.booleans()), From 15f113fbeb85f60aee3b960cc590afa7ef5c6c0c Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 18:40:56 +0000 Subject: [PATCH 42/58] add empty string to KML namespace identifiers to enable parsing kml files without namespace declaration --- fastkml/features.py | 2 +- fastkml/geometry.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fastkml/features.py b/fastkml/features.py index 2c557ed2..af535ec2 100644 --- a/fastkml/features.py +++ b/fastkml/features.py @@ -664,7 +664,7 @@ def geometry(self) -> Optional[AnyGeometryType]: registry.register( Placemark, RegistryItem( - ns_ids=("kml", "gx"), + ns_ids=("kml", "gx", ""), attr_name="kml_geometry", node_name=( "Point,LineString,LinearRing,Polygon,MultiGeometry,Model," diff --git a/fastkml/geometry.py b/fastkml/geometry.py index 1b62b3ab..bb14f78c 100644 --- a/fastkml/geometry.py +++ b/fastkml/geometry.py @@ -513,7 +513,7 @@ def geometry(self) -> Optional[geo.Point]: registry.register( Point, item=RegistryItem( - ns_ids=("kml", "gx"), + ns_ids=("kml", "gx", ""), classes=(AltitudeMode,), attr_name="altitude_mode", node_name="altitudeMode", @@ -692,7 +692,7 @@ def geometry(self) -> Optional[geo.LineString]: registry.register( LineString, item=RegistryItem( - ns_ids=("kml", "gx"), + ns_ids=("kml", "gx", ""), classes=(AltitudeMode,), attr_name="altitude_mode", node_name="altitudeMode", @@ -1158,7 +1158,7 @@ def __eq__(self, other: object) -> bool: registry.register( Polygon, item=RegistryItem( - ns_ids=("kml", "gx"), + ns_ids=("kml", "gx", ""), classes=(AltitudeMode,), attr_name="altitude_mode", node_name="altitudeMode", From e7e781e46d498fd353886b267d30f03ada5e5e68 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 18:42:07 +0000 Subject: [PATCH 43/58] update changelog for version 1.1.0; add support for ScreenOverlay and Model, and allow parsing KML files without namespace declarations --- docs/HISTORY.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/HISTORY.rst b/docs/HISTORY.rst index f15ed935..360ef1f5 100644 --- a/docs/HISTORY.rst +++ b/docs/HISTORY.rst @@ -1,10 +1,11 @@ Changelog ========= -1.0.0dev0 (unreleased) +1.1.0 (unreleased) ---------------------- -- Add support for ScreenOverlay +- Add support for ScreenOverlay and Model. +- allow parsing kml files without namespace declarations. 1.0 (2024/11/19) From 31aa4dc5864279f71452d4901566f4660fce8c88 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 19:18:34 +0000 Subject: [PATCH 44/58] add default values for float properties in model registry and rename fuzz test method --- fastkml/model.py | 8 ++++++++ tests/hypothesis/feature_test.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/fastkml/model.py b/fastkml/model.py index 2fe2cee7..9b29c11b 100644 --- a/fastkml/model.py +++ b/fastkml/model.py @@ -135,6 +135,7 @@ def geometry(self) -> Optional[Point]: classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, + default=0.0, ), ) @@ -192,6 +193,7 @@ def __repr__(self) -> str: classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, + default=0.0, ), ) registry.register( @@ -203,6 +205,7 @@ def __repr__(self) -> str: classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, + default=0.0, ), ) registry.register( @@ -214,6 +217,7 @@ def __repr__(self) -> str: classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, + default=0.0, ), ) @@ -269,6 +273,7 @@ def __repr__(self) -> str: classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, + default=1.0, ), ) registry.register( @@ -280,6 +285,7 @@ def __repr__(self) -> str: classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, + default=1.0, ), ) registry.register( @@ -291,6 +297,7 @@ def __repr__(self) -> str: classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, + default=1.0, ), ) @@ -480,6 +487,7 @@ def geometry(self) -> Optional[Point]: classes=(AltitudeMode,), get_kwarg=subelement_enum_kwarg, set_element=enum_subelement, + default=AltitudeMode.clamp_to_ground, ), ) registry.register( diff --git a/tests/hypothesis/feature_test.py b/tests/hypothesis/feature_test.py index 2751cc99..f04bc8e8 100644 --- a/tests/hypothesis/feature_test.py +++ b/tests/hypothesis/feature_test.py @@ -445,7 +445,7 @@ def test_fuzz_placemark_styles( link=st.builds(fastkml.Link, href=urls()), ), ) - def fuzz_placemark_model( + def test_fuzz_placemark_model( self, model: fastkml.model.Model, ) -> None: From 8b5da4a9ba85e4acbad2226532a98644e6f2192e Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 19:25:45 +0000 Subject: [PATCH 45/58] refactor boolean methods in model classes to clarify conditions for truthiness --- fastkml/model.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fastkml/model.py b/fastkml/model.py index 9b29c11b..9387b98f 100644 --- a/fastkml/model.py +++ b/fastkml/model.py @@ -165,7 +165,7 @@ def __init__( self.roll = roll def __bool__(self) -> bool: - """Return True if heading, tilt, and roll are set.""" + """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), ) @@ -324,7 +324,7 @@ def __init__( self.source_href = clean_string(source_href) def __bool__(self) -> bool: - """Return True if target_href and source_href are set.""" + """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: @@ -451,7 +451,7 @@ def __init__( self.resource_map = resource_map def __bool__(self) -> bool: - """Return True if link is set.""" + """Return True if link and location are set.""" return all((self.link, self.location)) def __repr__(self) -> str: From a85ea4c2758e2e3c353536bda97379548e122612 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 19:55:06 +0000 Subject: [PATCH 46/58] add unit tests for Model class and update overlay test description --- tests/model_test.py | 84 ++++++++++++++++++++++++++++++++++++++++++ tests/overlays_test.py | 2 +- 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 tests/model_test.py diff --git a/tests/model_test.py b/tests/model_test.py new file mode 100644 index 00000000..5ba56339 --- /dev/null +++ b/tests/model_test.py @@ -0,0 +1,84 @@ +# Copyright (C) 2021 - 2023 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 + +"""Test the kml overlay classes.""" + +from pygeoif.geometry import Point + +import fastkml.links +import fastkml.model +from fastkml.enums import AltitudeMode +from tests.base import Lxml +from tests.base import StdLibrary + + +class TestModel(StdLibrary): + def test_from_string(self) -> None: + doc = ( + '' + "absolute" + "" + "-123.115776547816" + "49.279804095564" + "21.614010375743" + "" + "111" + "http://barcelona.galdos.local/files/PublicLibrary.dae" + '' + "" + "http://barcelona.galdos.local/images/Concrete2.jpg" + "../images/Concrete.jpg" + "" + "" + "" + ) + + model = fastkml.model.Model.from_string(doc) + assert model.altitude_mode == AltitudeMode.absolute + assert model.geometry == Point( + -123.115776547816, + 49.279804095564, + 21.614010375743, + ) + assert model == fastkml.model.Model( + altitude_mode=AltitudeMode.absolute, + location=fastkml.model.Location( + altitude=21.614010375743, + latitude=49.279804095564, + longitude=-123.115776547816, + ), + orientation=None, + scale=fastkml.model.Scale( + x=1.0, + y=1.0, + z=1.0, + ), + link=fastkml.links.Link( + href="http://barcelona.galdos.local/files/PublicLibrary.dae", + ), + resource_map=fastkml.model.ResourceMap( + aliases=[ + fastkml.model.Alias( + target_href="http://barcelona.galdos.local/images/Concrete2.jpg", + source_href="../images/Concrete.jpg", + ), + ], + ), + ) + + +class TestModelLxml(TestModel, Lxml): + pass diff --git a/tests/overlays_test.py b/tests/overlays_test.py index 9539acb0..0209e6ed 100644 --- a/tests/overlays_test.py +++ b/tests/overlays_test.py @@ -14,7 +14,7 @@ # along with this library; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -"""Test the kml classes.""" +"""Test the kml overlay classes.""" import contextlib From 7286d0948d2344f802692f8e1ec7449c139e70b3 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 20:00:59 +0000 Subject: [PATCH 47/58] add note why validate=False when parsing undocumented elements --- docs/working_with_kml.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/working_with_kml.rst b/docs/working_with_kml.rst index ebb7ef6b..b810f79f 100644 --- a/docs/working_with_kml.rst +++ b/docs/working_with_kml.rst @@ -156,8 +156,10 @@ And register the new element with the KML Document object: The CascadingStyle object is now part of the KML document and can be accessed like any other element. -When parsing the document we have to skip the validation as the ``gx:CascadingStyle`` is -not in the XSD Schema. + +.. note:: + When parsing the document we have to skip the validation by passing ``validate=False`` + to ``KML.parse`` as the ``gx:CascadingStyle`` is not in the XSD Schema. Create a new KML object and confirm that the new element is parsed correctly: From cfd9994752b1d21607ab0ed412d481a25963efc1 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 20:11:13 +0000 Subject: [PATCH 48/58] add test model with missing location --- tests/model_test.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/model_test.py b/tests/model_test.py index 5ba56339..6e1b325f 100644 --- a/tests/model_test.py +++ b/tests/model_test.py @@ -79,6 +79,26 @@ def test_from_string(self) -> None: ), ) + def test_from_string_no_location(self) -> None: + doc = ( + '' + "absolute" + "111" + "http://barcelona.galdos.local/files/PublicLibrary.dae" + '' + "" + "http://barcelona.galdos.local/images/Concrete2.jpg" + "../images/Concrete.jpg" + "" + "" + "" + ) + + model = fastkml.model.Model.from_string(doc) + assert model.altitude_mode == AltitudeMode.absolute + assert model.geometry is None + assert not model + class TestModelLxml(TestModel, Lxml): pass From d38b0968739f94a236a380701d746edc8364454c Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 20:22:21 +0000 Subject: [PATCH 49/58] improve readme --- README.rst | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index f4a6f4e4..55ecf1bc 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,8 @@ Introduction .. inclusion-marker-do-not-remove -KML is an XML geospatial data format and an OGC_ standard that deserves a canonical python implementation. +KML is an XML geospatial data format and an OGC_ standard that deserves a canonical +python implementation. Fastkml is a library to read, write and manipulate KML files. It aims to keep it simple and fast (using lxml_ if available). Fast refers to the time you @@ -19,9 +20,12 @@ For more details about the KML Specification, check out the `KML Reference `_ on the Google developers site. -Geometries are handled as pygeoif_ objects. +Geometries are handled as pygeoif_ objects, which are compatible with any geometry that +implements the ``__geo_interface__`` protocol, such as shapely_. -Fastkml is continually tested +Fastkml is tested on `CPython `_ and +`PyPy `_, but it should work on alternative +Python implementations (that implement the language specification *>=3.8*) as well. |test| |hypothesis| |cov| |black| |mypy| |commit| @@ -49,7 +53,7 @@ Fastkml is continually tested :target: https://github.com/pre-commit/pre-commit :alt: pre-commit -Is Maintained and documented: +Is maintained and documented: |pypi| |conda-forge| |status| |license| |doc| |stats| |pyversion| |pyimpl| |dependencies| |downloads| @@ -134,3 +138,4 @@ Please submit a PR with the features you'd like to see implemented. .. _lxml: https://pypi.python.org/pypi/lxml .. _arrow: https://pypi.python.org/pypi/arrow .. _OGC: https://www.ogc.org/standard/kml/ +.. _shapely: https://shapely.readthedocs.io/ From 917719149a89101b0c9822c65f71959867ea5f8f Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 20:33:27 +0000 Subject: [PATCH 50/58] add test when location is invalid --- tests/model_test.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/model_test.py b/tests/model_test.py index 6e1b325f..93a4ff53 100644 --- a/tests/model_test.py +++ b/tests/model_test.py @@ -99,6 +99,31 @@ def test_from_string_no_location(self) -> None: assert model.geometry is None assert not model + def test_from_string_invalid_location(self) -> None: + doc = ( + '' + "absolute" + "" + "" + "49.279804095564" + "21.614010375743" + "" + "111" + "http://barcelona.galdos.local/files/PublicLibrary.dae" + '' + "" + "http://barcelona.galdos.local/images/Concrete2.jpg" + "../images/Concrete.jpg" + "" + "" + "" + ) + + model = fastkml.model.Model.from_string(doc) + assert model.altitude_mode == AltitudeMode.absolute + assert model.geometry is None + assert not model + class TestModelLxml(TestModel, Lxml): pass From abcc0d9d96d4367515febd1b99cb4f8dd5003bb6 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 20:38:29 +0000 Subject: [PATCH 51/58] add test for handling invalid location in model --- tests/model_test.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/model_test.py b/tests/model_test.py index 93a4ff53..fb10c150 100644 --- a/tests/model_test.py +++ b/tests/model_test.py @@ -47,6 +47,7 @@ def test_from_string(self) -> None: ) model = fastkml.model.Model.from_string(doc) + assert model.altitude_mode == AltitudeMode.absolute assert model.geometry == Point( -123.115776547816, @@ -95,6 +96,7 @@ def test_from_string_no_location(self) -> None: ) model = fastkml.model.Model.from_string(doc) + assert model.altitude_mode == AltitudeMode.absolute assert model.geometry is None assert not model @@ -120,10 +122,27 @@ def test_from_string_invalid_location(self) -> None: ) model = fastkml.model.Model.from_string(doc) + assert model.altitude_mode == AltitudeMode.absolute assert model.geometry is None assert not model + def test_location_from_string_invalid_location(self) -> None: + doc = ( + '' + "" + "49.279804095564" + "21.614010375743" + "" + ) + + location = fastkml.model.Location.from_string(doc) + + assert location.altitude == 21.614010375743 + assert location.latitude == 49.279804095564 + assert location.geometry is None + assert not location + class TestModelLxml(TestModel, Lxml): pass From 7da60f0e3ca8ce89adfd8eb2086d581a38042a8e Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sun, 1 Dec 2024 21:19:49 +0000 Subject: [PATCH 52/58] import model to provide shortcuts --- fastkml/__init__.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fastkml/__init__.py b/fastkml/__init__.py index ddc48af2..bf8afff5 100644 --- a/fastkml/__init__.py +++ b/fastkml/__init__.py @@ -52,6 +52,12 @@ from fastkml.kml import KML from fastkml.links import Icon from fastkml.links import Link +from fastkml.model import Alias +from fastkml.model import Location +from fastkml.model import Model +from fastkml.model import Orientation +from fastkml.model import ResourceMap +from fastkml.model import Scale from fastkml.overlays import GroundOverlay from fastkml.overlays import ImagePyramid from fastkml.overlays import LatLonBox @@ -84,6 +90,7 @@ __all__ = [ "KML", + "Alias", "AtomAuthor", "AtomContributor", "AtomLink", @@ -107,9 +114,12 @@ "LineStyle", "LinearRing", "Link", + "Location", "LookAt", + "Model", "MultiGeometry", "NetworkLink", + "Orientation", "OuterBoundaryIs", "OverlayXY", "Pair", @@ -120,7 +130,9 @@ "PolyStyle", "Polygon", "Polygon", + "ResourceMap", "RotationXY", + "Scale", "Schema", "SchemaData", "ScreenOverlay", From 10ebe81d2cb1ff7a1142ae1aaefe801e8fe896a0 Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Sun, 1 Dec 2024 17:35:14 -0500 Subject: [PATCH 53/58] requested changes --- fastkml/__init__.py | 4 ++-- fastkml/kml.py | 2 +- fastkml/network_link_control.py | 22 ++++++++++++---------- tests/network_link_control_test.py | 10 +++------- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/fastkml/__init__.py b/fastkml/__init__.py index 0c9fb66f..e94dae15 100644 --- a/fastkml/__init__.py +++ b/fastkml/__init__.py @@ -31,7 +31,6 @@ from fastkml.atom import Link as AtomLink from fastkml.containers import Document from fastkml.containers import Folder -from fastkml.network_link_control import NetworkLinkControl from fastkml.data import Data from fastkml.data import ExtendedData from fastkml.data import Schema @@ -59,6 +58,7 @@ from fastkml.model import Orientation from fastkml.model import ResourceMap from fastkml.model import Scale +from fastkml.network_link_control import NetworkLinkControl from fastkml.overlays import GroundOverlay from fastkml.overlays import ImagePyramid from fastkml.overlays import LatLonBox @@ -91,7 +91,6 @@ __all__ = [ "KML", - "NetworkLinkControl", "Alias", "AtomAuthor", "AtomContributor", @@ -121,6 +120,7 @@ "Model", "MultiGeometry", "NetworkLink", + "NetworkLinkControl", "Orientation", "OuterBoundaryIs", "OverlayXY", diff --git a/fastkml/kml.py b/fastkml/kml.py index b858e40f..70d1b8ec 100644 --- a/fastkml/kml.py +++ b/fastkml/kml.py @@ -66,7 +66,7 @@ Placemark, GroundOverlay, PhotoOverlay, - NetworkLinkControl + NetworkLinkControl, ] diff --git a/fastkml/network_link_control.py b/fastkml/network_link_control.py index 1e9d05bd..feeec67d 100644 --- a/fastkml/network_link_control.py +++ b/fastkml/network_link_control.py @@ -14,26 +14,26 @@ # along with this library; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import logging - from typing import Any from typing import Dict from typing import Optional from typing import Union + from fastkml import config from fastkml.base import _XMLObject from fastkml.helpers import datetime_subelement +from fastkml.helpers import datetime_subelement_kwarg from fastkml.helpers import float_subelement from fastkml.helpers import subelement_float_kwarg -from fastkml.helpers import datetime_subelement_kwarg from fastkml.helpers import subelement_text_kwarg from fastkml.helpers import text_subelement -from fastkml.helpers import xml_subelement_list -from fastkml.helpers import xml_subelement_list_kwarg +from fastkml.helpers import xml_subelement +from fastkml.helpers import xml_subelement_kwarg from fastkml.registry import RegistryItem from fastkml.registry import registry from fastkml.times import KmlDateTime -from fastkml.views import Camera, LookAt - +from fastkml.views import Camera +from fastkml.views import LookAt __all__ = [ "NetworkLinkControl", @@ -69,7 +69,7 @@ def __init__( link_snippet: Optional[str] = None, expires: Optional[KmlDateTime] = None, view: Optional[Union[Camera, LookAt]] = None, - **kwargs: Any + **kwargs: Any, ) -> None: super().__init__( ns=ns, @@ -83,7 +83,7 @@ def __init__( link_snippet=link_snippet, expires=expires, view=view, - **kwargs + **kwargs, ) def __repr__(self) -> str: @@ -122,6 +122,7 @@ def __repr__(self) -> str: classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, + default=0, ), ) registry.register( @@ -133,6 +134,7 @@ def __repr__(self) -> str: classes=(float,), get_kwarg=subelement_float_kwarg, set_element=float_subelement, + default=-1, ), ) registry.register( @@ -211,7 +213,7 @@ def __repr__(self) -> str: Camera, LookAt, ), - get_kwarg=xml_subelement_list_kwarg, - set_element=xml_subelement_list, + get_kwarg=xml_subelement_kwarg, + set_element=xml_subelement, ), ) diff --git a/tests/network_link_control_test.py b/tests/network_link_control_test.py index 5fc9720e..b348ee52 100644 --- a/tests/network_link_control_test.py +++ b/tests/network_link_control_test.py @@ -51,9 +51,8 @@ def test_network_link_control_obj(self) -> None: assert network_control_obj.link_name == "link_name" assert network_control_obj.link_description == "link_description" assert network_control_obj.link_snippet == "link_snippet" - assert network_control_obj.expires == kml_datetime - - assert network_control_obj.view == view + assert str(network_control_obj.expires) == str(kml_datetime) + assert str(network_control_obj.view) == str(view) def test_network_link_control_kml(self) -> None: doc = ( @@ -77,7 +76,4 @@ def test_network_link_control_kml(self) -> None: expires=kml_datetime, ) - assert nc.min_refresh_period == nc_obj.min_refresh_period - assert nc.max_session_length == nc_obj.max_session_length - assert nc.link_snippet == nc_obj.link_snippet - assert nc.expires == nc_obj.expires + assert nc == nc_obj From bb7c364cfd59a9124b57e7a3cfbe636d94aad855 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Mon, 2 Dec 2024 00:04:15 +0000 Subject: [PATCH 54/58] Refactor NetworkLinkControl to use clean_string for input parameters and add Hypothesis tests for validation --- fastkml/network_link_control.py | 66 ++++++++-- tests/hypothesis/network_link_control_test.py | 122 ++++++++++++++++++ tests/network_link_control_test.py | 8 +- 3 files changed, 181 insertions(+), 15 deletions(-) create mode 100644 tests/hypothesis/network_link_control_test.py diff --git a/fastkml/network_link_control.py b/fastkml/network_link_control.py index feeec67d..01d2d066 100644 --- a/fastkml/network_link_control.py +++ b/fastkml/network_link_control.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2024 Christian Ledermann +# 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 @@ -13,6 +13,14 @@ # 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 +""" +NetworkLinkControl class. + +Controls the behavior of files fetched by a . + +https://developers.google.com/kml/documentation/kmlreference#networklinkcontrol +""" + import logging from typing import Any from typing import Dict @@ -21,6 +29,7 @@ from fastkml import config from fastkml.base import _XMLObject +from fastkml.helpers import clean_string from fastkml.helpers import datetime_subelement from fastkml.helpers import datetime_subelement_kwarg from fastkml.helpers import float_subelement @@ -43,6 +52,7 @@ class NetworkLinkControl(_XMLObject): + """Controls the behavior of files fetched by a .""" _default_nsid = config.KML @@ -54,7 +64,7 @@ class NetworkLinkControl(_XMLObject): link_description: Optional[str] link_snippet: Optional[str] expires: Optional[KmlDateTime] - view: Union[Camera, LookAt, None] # TODO: Add Update field to the parameters + view: Union[Camera, LookAt, None] def __init__( self, @@ -71,20 +81,52 @@ def __init__( view: Optional[Union[Camera, LookAt]] = None, **kwargs: Any, ) -> None: + """ + Create a NetworkLinkControl object. + + Parameters + ---------- + ns : str, optional + The namespace to use for the NetworkLinkControl object. + name_spaces : dict, optional + A dictionary of namespaces to use for the NetworkLinkControl object. + min_refresh_period : float, optional + The minimum number of seconds between fetches. A value of -1 indicates that + the NetworkLinkControl object should be fetched only once. + max_session_length : float, optional + The maximum number of seconds that the link should be followed. + cookie : str, optional + A string value that can be used to identify the client request. + message : str, optional + A message to be displayed to the user in case of a failure. + link_name : str, optional + The name of the link. + link_description : str, optional + A description of the link. + link_snippet : str, optional + A snippet of text to be displayed in the link. + expires : KmlDateTime, optional + The time at which the link should expire. + view : Camera or LookAt, optional + The view to be used when the link is followed. + **kwargs : Any, optional + Additional keyword arguments. + + """ super().__init__( ns=ns, name_spaces=name_spaces, - min_refresh_period=min_refresh_period, - max_session_length=max_session_length, - cookie=cookie, - message=message, - link_name=link_name, - link_description=link_description, - link_snippet=link_snippet, - expires=expires, - view=view, **kwargs, ) + self.min_refresh_period = min_refresh_period + self.max_session_length = max_session_length + self.cookie = clean_string(cookie) + self.message = clean_string(message) + self.link_name = clean_string(link_name) + self.link_description = clean_string(link_description) + self.link_snippet = clean_string(link_snippet) + self.expires = expires + self.view = view def __repr__(self) -> str: """ @@ -195,7 +237,7 @@ def __repr__(self) -> str: registry.register( NetworkLinkControl, item=RegistryItem( - ns_ids=("kml", ), + ns_ids=("kml",), classes=(KmlDateTime,), attr_name="expires", node_name="expires", diff --git a/tests/hypothesis/network_link_control_test.py b/tests/hypothesis/network_link_control_test.py new file mode 100644 index 00000000..3ff1d963 --- /dev/null +++ b/tests/hypothesis/network_link_control_test.py @@ -0,0 +1,122 @@ +# 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 +"""Hypothesis tests for the fastkml.network_link_control module.""" + +import typing + +from hypothesis import given +from hypothesis import strategies as st + +import fastkml +import fastkml.enums +import fastkml.model +import fastkml.views +from tests.base import Lxml +from tests.hypothesis.common import assert_repr_roundtrip +from tests.hypothesis.common import assert_str_roundtrip +from tests.hypothesis.common import assert_str_roundtrip_terse +from tests.hypothesis.common import assert_str_roundtrip_verbose +from tests.hypothesis.strategies import kml_datetimes +from tests.hypothesis.strategies import xml_text + + +class TestLxml(Lxml): + @given( + min_refresh_period=st.one_of( + st.none(), + st.floats(allow_nan=False, allow_infinity=False).filter(lambda x: x != 0), + ), + max_session_length=st.one_of( + st.none(), + st.floats(allow_nan=False, allow_infinity=False).filter(lambda x: x != -1), + ), + cookie=st.one_of(st.none(), xml_text()), + message=st.one_of(st.none(), xml_text()), + link_name=st.one_of(st.none(), xml_text()), + link_description=st.one_of(st.none(), xml_text()), + link_snippet=st.one_of(st.none(), xml_text()), + expires=st.one_of( + st.none(), + kml_datetimes(), + ), + view=st.one_of( + st.none(), + st.builds( + fastkml.views.Camera, + longitude=st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-180, + max_value=180, + ).filter(lambda x: x != 0), + latitude=st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-90, + max_value=90, + ).filter(lambda x: x != 0), + altitude=st.floats(allow_nan=False, allow_infinity=False).filter( + lambda x: x != 0, + ), + ), + st.builds( + fastkml.views.LookAt, + longitude=st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-180, + max_value=180, + ).filter(lambda x: x != 0), + latitude=st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-90, + max_value=90, + ).filter(lambda x: x != 0), + altitude=st.floats(allow_nan=False, allow_infinity=False).filter( + lambda x: x != 0, + ), + ), + ), + ) + def test_fuzz_network_link_control( + self, + min_refresh_period: typing.Optional[float], + max_session_length: typing.Optional[float], + cookie: typing.Optional[str], + message: typing.Optional[str], + link_name: typing.Optional[str], + link_description: typing.Optional[str], + link_snippet: typing.Optional[str], + expires: typing.Optional[fastkml.KmlDateTime], + view: typing.Union[fastkml.Camera, fastkml.LookAt, None], + ) -> None: + nlc = fastkml.NetworkLinkControl( + min_refresh_period=min_refresh_period, + max_session_length=max_session_length, + cookie=cookie, + message=message, + link_name=link_name, + link_description=link_description, + link_snippet=link_snippet, + expires=expires, + view=view, + ) + + assert_repr_roundtrip(nlc) + assert_str_roundtrip(nlc) + assert_str_roundtrip_terse(nlc) + assert_str_roundtrip_verbose(nlc) diff --git a/tests/network_link_control_test.py b/tests/network_link_control_test.py index b348ee52..5cfa0ac4 100644 --- a/tests/network_link_control_test.py +++ b/tests/network_link_control_test.py @@ -18,17 +18,19 @@ import datetime +from dateutil.tz import tzutc + +from fastkml import views from fastkml.network_link_control import NetworkLinkControl from fastkml.times import KmlDateTime from tests.base import StdLibrary -from fastkml import views class TestStdLibrary(StdLibrary): """Test with the standard library.""" def test_network_link_control_obj(self) -> None: - dt = datetime.datetime.now() + dt = datetime.datetime.now(tz=tzutc()) kml_datetime = KmlDateTime(dt=dt) view = views.Camera() @@ -41,7 +43,7 @@ def test_network_link_control_obj(self) -> None: link_description="link_description", link_snippet="link_snippet", expires=kml_datetime, - view=view + view=view, ) assert network_control_obj.min_refresh_period == 1.1 From 82605bc71418bcbdc1b26fa1546d6697e6cb9127 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Mon, 2 Dec 2024 00:08:07 +0000 Subject: [PATCH 55/58] fix black --- fastkml/kml.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fastkml/kml.py b/fastkml/kml.py index 70d1b8ec..aee6f9cd 100644 --- a/fastkml/kml.py +++ b/fastkml/kml.py @@ -45,13 +45,13 @@ from fastkml import validator from fastkml.base import _XMLObject from fastkml.containers import Document -from fastkml.network_link_control import NetworkLinkControl from fastkml.containers import Folder from fastkml.enums import Verbosity from fastkml.features import NetworkLink from fastkml.features import Placemark from fastkml.helpers import xml_subelement_list from fastkml.helpers import xml_subelement_list_kwarg +from fastkml.network_link_control import NetworkLinkControl from fastkml.overlays import GroundOverlay from fastkml.overlays import PhotoOverlay from fastkml.registry import RegistryItem @@ -61,13 +61,13 @@ logger = logging.getLogger(__name__) kml_children = Union[ - Folder, - Document, - Placemark, - GroundOverlay, - PhotoOverlay, - NetworkLinkControl, - ] + Folder, + Document, + Placemark, + GroundOverlay, + PhotoOverlay, + NetworkLinkControl, +] def lxml_parse_and_validate( From 453ccfe17410ac90fef2a7177736c52900282351 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Mon, 2 Dec 2024 00:08:25 +0000 Subject: [PATCH 56/58] Add support for NetworkLinkControl in the changelog --- docs/HISTORY.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/HISTORY.rst b/docs/HISTORY.rst index 360ef1f5..4e171fa8 100644 --- a/docs/HISTORY.rst +++ b/docs/HISTORY.rst @@ -6,6 +6,7 @@ Changelog - Add support for ScreenOverlay and Model. - allow parsing kml files without namespace declarations. +- Add support for NetworkLinkControl. [Apurva Banka] 1.0 (2024/11/19) From 19a165dc0f386c5b9c9fe286f0ce7cec5e8c9dff Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Mon, 2 Dec 2024 00:35:30 +0000 Subject: [PATCH 57/58] update version to 1.1.0 --- fastkml/about.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastkml/about.py b/fastkml/about.py index aab34882..aa006175 100644 --- a/fastkml/about.py +++ b/fastkml/about.py @@ -19,7 +19,7 @@ The only purpose of this module is to provide a version number for the package. """ -__version__ = "1.0.0dev0" +__version__ = "1.1.0" """Fastkml version number.""" __all__ = ["__version__"] From 1c346feb377d1cc7f45ef139ddf36778f2d26022 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Mon, 2 Dec 2024 09:21:09 +0000 Subject: [PATCH 58/58] Remove duplicate entries from __all__ in fastkml/__init__.py --- fastkml/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/fastkml/__init__.py b/fastkml/__init__.py index e94dae15..769799dd 100644 --- a/fastkml/__init__.py +++ b/fastkml/__init__.py @@ -128,10 +128,8 @@ "PhotoOverlay", "Placemark", "Point", - "Point", "PolyStyle", "Polygon", - "Polygon", "ResourceMap", "RotationXY", "Scale", @@ -144,7 +142,6 @@ "Size", "Snippet", "Style", - "Style", "StyleMap", "StyleUrl", "TimeSpan",