From 9e452f6c535b2e982ffd2b03795018a53fbbe7c9 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Sat, 26 Oct 2024 17:01:03 +0100 Subject: [PATCH] Refactor code to use the new `find_all` utility function --- docs/Document-clean.kml | 1 + .../{usage_guide.rst => create_kml_files.rst} | 8 ++- docs/index.rst | 3 +- docs/working_with_kml.rst | 53 +++++++++++++++++++ fastkml/__init__.py | 24 +++++++-- fastkml/utils.py | 44 ++++++++------- tests/utils_test.py | 10 ++++ 7 files changed, 109 insertions(+), 34 deletions(-) create mode 120000 docs/Document-clean.kml rename docs/{usage_guide.rst => create_kml_files.rst} (98%) create mode 100644 docs/working_with_kml.rst diff --git a/docs/Document-clean.kml b/docs/Document-clean.kml new file mode 120000 index 00000000..e39077a5 --- /dev/null +++ b/docs/Document-clean.kml @@ -0,0 +1 @@ +../tests/ogc_conformance/data/kml/Document-clean.kml \ No newline at end of file diff --git a/docs/usage_guide.rst b/docs/create_kml_files.rst similarity index 98% rename from docs/usage_guide.rst rename to docs/create_kml_files.rst index cf44c1c2..2e24903a 100644 --- a/docs/usage_guide.rst +++ b/docs/create_kml_files.rst @@ -1,14 +1,12 @@ -Usage guide -=========== +Creating KML files +================== Read a shapefile and build a 3D KML visualization. -------------------------------------------------- This example shows how to read a shapefile and build a 3D KML visualization from it. -You will need to install the following packages: - -- `pyshp `_ +You will need to install `pyshp `_ (``pip install pyshp``). For this example we will use the `Data on CO2 and Greenhouse Gas Emissions `_ by diff --git a/docs/index.rst b/docs/index.rst index ea4dd036..7113443e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,7 +23,8 @@ requirements, namely: :maxdepth: 2 quickstart - usage_guide + create_kml_files + working_with_kml configuration modules contributing diff --git a/docs/working_with_kml.rst b/docs/working_with_kml.rst new file mode 100644 index 00000000..a646d5c4 --- /dev/null +++ b/docs/working_with_kml.rst @@ -0,0 +1,53 @@ +Working with KML Files +====================== + +Import the necessary modules: + +.. code-block:: pycon + + >>> from fastkml.utils import find_all + >>> from fastkml import KML + >>> from fastkml import Placemark, Point + + +Open a KML file: + +.. code-block:: pycon + + >>> k = KML.parse("docs/Document-clean.kml") + +Extract all placemarks and print their geometries: + +.. code-block:: pycon + + >>> placemarks = list(find_all(k, of_type=Placemark)) + >>> for p in placemarks: + ... print(p.geometry) # doctest: +ELLIPSIS + ... + POINT Z (-123.93563168 49.16716103 5.0) + POLYGON Z ((-123.940449937288 49.16927524669021 17.0, ... + >>> pts = list(find_all(k, of_type=Point)) + >>> for point in pts: + ... print(point.geometry) + ... + POINT Z (-123.93563168 49.16716103 5.0) + POINT Z (-123.1097 49.2774 0.0) + POINT Z (-123.028369 49.26107900000001 0.0) + POINT Z (-123.3215766 49.2760338 0.0) + POINT Z (-123.2643704 49.3301853 0.0) + POINT Z (-123.2477084 49.2890857 0.0) + + + +You can also define what you are looking for by specifying additional parameters: + + +.. code-block:: pycon + + >>> al = list(find_all(k, name="Vancouver Film Studios")) + >>> al[0].name + 'Vancouver Film Studios' + >>> al[0].get_tag_name() + 'Placemark' + >>> list(find_all(k, href="http://www.vancouverfilmstudios.com/")) # doctest: +ELLIPSIS + [fastkml.atom.Link(ns=... diff --git a/fastkml/__init__.py b/fastkml/__init__.py index a6ca8b7e..d0cb773f 100644 --- a/fastkml/__init__.py +++ b/fastkml/__init__.py @@ -25,9 +25,9 @@ functionality that KML on google earth provides. """ from fastkml.about import __version__ # noqa: F401 -from fastkml.atom import Author -from fastkml.atom import Contributor -from fastkml.atom import Link +from fastkml.atom import Author as AtomAuthor +from fastkml.atom import Contributor as AtomContributor +from fastkml.atom import Link as AtomLink from fastkml.containers import Document from fastkml.containers import Folder from fastkml.data import Data @@ -35,7 +35,14 @@ from fastkml.data import Schema from fastkml.data import SchemaData from fastkml.features import Placemark +from fastkml.geometry import LinearRing +from fastkml.geometry import LineString +from fastkml.geometry import MultiGeometry +from fastkml.geometry import Point +from fastkml.geometry import Polygon from fastkml.kml import KML +from fastkml.links import Icon +from fastkml.links import Link from fastkml.overlays import GroundOverlay from fastkml.overlays import PhotoOverlay from fastkml.styles import BalloonStyle @@ -72,9 +79,16 @@ "PolyStyle", "LabelStyle", "BalloonStyle", + "AtomLink", + "Icon", "Link", - "Author", - "Contributor", + "Point", + "LineString", + "LinearRing", + "Polygon", + "MultiGeometry", + "AtomAuthor", + "AtomContributor", "Camera", "LookAt", ] diff --git a/fastkml/utils.py b/fastkml/utils.py index a4206c21..8872ddb9 100644 --- a/fastkml/utils.py +++ b/fastkml/utils.py @@ -10,7 +10,7 @@ def has_attribute_values(obj: object, **kwargs: Any) -> bool: """ - Check if an object has the given attribute values. + Check if an object has all of the given attribute values. Args: obj: The object to check. @@ -21,12 +21,9 @@ def has_attribute_values(obj: object, **kwargs: Any) -> bool: """ try: - for key, value in kwargs.items(): - if getattr(obj, key) != value: - return False + return all(getattr(obj, key) == value for key, value in kwargs.items()) except AttributeError: return False - return True def find_all( @@ -47,22 +44,23 @@ def find_all( An iterable of all instances of the given type in the given object. """ - if of_type is None or isinstance(obj, of_type): - if has_attribute_values(obj, **kwargs): - yield obj - else: + if (of_type is None or isinstance(obj, of_type)) and has_attribute_values( + obj, + **kwargs, + ): + yield obj + try: + attrs = [attr for attr in obj.__dict__ if not attr.startswith("_")] + except AttributeError: + return + for attr_name in attrs: + attr = getattr(obj, attr_name) + if callable(attr): + continue + if attr is obj: + continue try: - attrs = [attr for attr in obj.__dict__ if not attr.startswith("_")] - except AttributeError: - return - for attr_name in attrs: - attr = getattr(obj, attr_name) - if callable(attr): - continue - if attr is obj: - continue - try: - for item in attr: - yield from find_all(item, of_type=of_type, **kwargs) - except TypeError: - yield from find_all(attr, of_type=of_type, **kwargs) + for item in attr: + yield from find_all(item, of_type=of_type, **kwargs) + except TypeError: + yield from find_all(attr, of_type=of_type, **kwargs) diff --git a/tests/utils_test.py b/tests/utils_test.py index e3ea6a15..efbe07a7 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -108,6 +108,16 @@ def __init__(self, x: int) -> None: a1 = A(1) result = list(find_all(a1, of_type=None)) + assert result == [a1, 1] + + def test_find_all_no_type_attr_x(self) -> None: + class A: + def __init__(self, x: int) -> None: + self.x = x + + a1 = A(1) + + result = list(find_all(a1, x=1)) assert result == [a1] def test_find_schema_by_url(self) -> None: