diff --git a/fastkml/geometry.py b/fastkml/geometry.py
index f341f6b1..50dd48e5 100644
--- a/fastkml/geometry.py
+++ b/fastkml/geometry.py
@@ -817,9 +817,9 @@ def geometry(self) -> Optional[geo.LinearRing]:
return None
-class OuterBoundaryIs(_XMLObject):
+class BoundaryIs(_XMLObject):
"""
- Represents the outer boundary of a polygon in KML.
+ Represents the inner or outer boundary of a polygon in KML.
Attributes
----------
@@ -905,19 +905,6 @@ def __repr__(self) -> str:
")"
)
- @classmethod
- def get_tag_name(cls) -> str:
- """
- Get the tag name for the OuterBoundaryIs object.
-
- Returns
- -------
- str
- The tag name.
-
- """
- return "outerBoundaryIs"
-
@property
def geometry(self) -> Optional[geo.LinearRing]:
"""
@@ -932,106 +919,34 @@ def geometry(self) -> Optional[geo.LinearRing]:
return self.kml_geometry.geometry if self.kml_geometry else None
-registry.register(
- OuterBoundaryIs,
- item=RegistryItem(
- ns_ids=("kml", ""),
- classes=(LinearRing,),
- attr_name="kml_geometry",
- node_name="LinearRing",
- get_kwarg=xml_subelement_kwarg,
- set_element=xml_subelement,
- ),
-)
+class OuterBoundaryIs(BoundaryIs):
+ """Represents the outer boundary of a polygon in KML."""
-
-class InnerBoundaryIs(_XMLObject):
- """Represents the inner boundary of a polygon in KML."""
-
- _default_nsid = config.KML
- kml_geometry: Optional[LinearRing]
-
- def __init__(
- self,
- *,
- ns: Optional[str] = None,
- name_spaces: Optional[Dict[str, str]] = None,
- geometry: Optional[geo.LinearRing] = None,
- kml_geometry: Optional[LinearRing] = None,
- **kwargs: Any,
- ) -> None:
+ @classmethod
+ def get_tag_name(cls) -> str:
"""
- Initialize a Geometry object.
-
- Parameters
- ----------
- ns : Optional[str], optional
- The namespace for the KML element, by default None.
- name_spaces : Optional[Dict[str, str]], optional
- The namespace dictionary for the KML element, by default None.
- geometry : Optional[geo.LinearRing], optional
- The geometry to be converted to a KML geometry, by default None.
- kml_geometry : Optional[LinearRing], optional
- The KML geometry, by default None.
- **kwargs : Any
- Additional keyword arguments.
-
- Raises
- ------
- GeometryError
- If both `geometry` and `kml_geometry` are provided.
+ Get the tag name for the OuterBoundaryIs object.
- Notes
- -----
- - If `geometry` is provided, it will be converted to KML geometries and
- stored in `kml_geometry`.
- - If `geometry` and `kml_geometry` are both provided, a GeometryError will be
- raised.
+ Returns
+ -------
+ str
+ The tag name.
"""
- if geometry is not None and kml_geometry is not None:
- raise GeometryError(MsgMutualExclusive)
- if kml_geometry is None:
- kml_geometry = LinearRing(ns=ns, name_spaces=name_spaces, geometry=geometry)
- self.kml_geometry = kml_geometry
- super().__init__(
- ns=ns,
- name_spaces=name_spaces,
- **kwargs,
- )
+ return "outerBoundaryIs"
- def __bool__(self) -> bool:
- """Return True if any of the inner boundary geometries exist."""
- return bool(self.kml_geometry)
- def __repr__(self) -> str:
- """Create a string (c)representation for InnerBoundaryIs."""
- return (
- f"{self.__class__.__module__}.{self.__class__.__name__}("
- f"ns={self.ns!r}, "
- f"name_spaces={self.name_spaces!r}, "
- f"kml_geometry={self.kml_geometry!r}, "
- f"**{self._get_splat()},"
- ")"
- )
+class InnerBoundaryIs(BoundaryIs):
+ """Represents the inner boundary of a polygon in KML."""
@classmethod
def get_tag_name(cls) -> str:
"""Return the tag name of the element."""
return "innerBoundaryIs"
- @property
- def geometry(self) -> Optional[geo.LinearRing]:
- """
- Return the list of LinearRing objects representing the inner boundary.
-
- If no inner boundary geometries exist, returns None.
- """
- return self.kml_geometry.geometry if self.kml_geometry else None
-
registry.register(
- InnerBoundaryIs,
+ BoundaryIs,
item=RegistryItem(
ns_ids=("kml",),
classes=(LinearRing,),
diff --git a/fastkml/utils.py b/fastkml/utils.py
index 604d422b..2851cb5c 100644
--- a/fastkml/utils.py
+++ b/fastkml/utils.py
@@ -88,7 +88,4 @@ def find(
The first instance of the given type in the given object or None if not found.
"""
- try:
- return next(find_all(obj, of_type=of_type, **kwargs))
- except StopIteration:
- return None
+ return next(find_all(obj, of_type=of_type, **kwargs), None)
diff --git a/tests/geometries/boundaries_test.py b/tests/geometries/boundaries_test.py
index 3ee79abc..8e422a68 100644
--- a/tests/geometries/boundaries_test.py
+++ b/tests/geometries/boundaries_test.py
@@ -22,6 +22,7 @@
import pygeoif.geometry as geo
import pytest
+import fastkml
from fastkml.exceptions import GeometryError
from fastkml.geometry import Coordinates
from fastkml.geometry import InnerBoundaryIs
@@ -122,6 +123,22 @@ def test_read_inner_boundary_multiple_linestrings(self) -> None:
((1, 4), (2, 0), (0, 0), (1, 4)),
)
+ def test_inner_boundary_repr_roundtrip(self) -> None:
+ """Test that repr(obj) can be eval'd back to obj."""
+ coords = ((1, 2), (2, 0), (0, 0), (1, 2))
+ inner_boundary = InnerBoundaryIs(
+ kml_geometry=LinearRing(kml_coordinates=Coordinates(coords=coords)),
+ )
+
+ assert inner_boundary == eval( # noqa: S307
+ repr(inner_boundary),
+ {},
+ {
+ "fastkml": fastkml,
+ "LinearRing": geo.LinearRing,
+ },
+ )
+
class TestBoundariesLxml(Lxml, TestBoundaries):
pass
diff --git a/tests/geometries/point_test.py b/tests/geometries/point_test.py
index 888be317..32fb08df 100644
--- a/tests/geometries/point_test.py
+++ b/tests/geometries/point_test.py
@@ -196,6 +196,30 @@ def test_from_string_uppercase_altitude_mode_strict(self) -> None:
"",
)
+ def test_from_string_invalid_altitude_mode_strict(self) -> None:
+ with pytest.raises(
+ KMLParseError,
+ match=r"^Error parsing '<",
+ ):
+ assert Point.from_string(
+ ''
+ "INVALID"
+ "1.000000,2.000000"
+ "",
+ )
+
+ def test_from_string_invalid_altitude_mode_relaxed(self) -> None:
+ point = Point.from_string(
+ ''
+ "invalid"
+ "1.000000,2.000000"
+ "",
+ strict=False,
+ )
+
+ assert point.geometry == geo.Point(1, 2)
+ assert not point.altitude_mode
+
def test_from_string_3d(self) -> None:
"""Test the from_string method for a 3 dimensional point."""
point = Point.from_string(
diff --git a/tests/gx_test.py b/tests/gx_test.py
index 33c6f8bc..8ad064d6 100644
--- a/tests/gx_test.py
+++ b/tests/gx_test.py
@@ -18,6 +18,7 @@
import datetime
import pygeoif.geometry as geo
+import pytest
from dateutil.tz import tzoffset
from dateutil.tz import tzutc
@@ -175,6 +176,48 @@ def test_track_from_track_items(self) -> None:
assert "angles>" in track.to_string()
assert ">0.0 0.0 0.0" in track.to_string()
+ def test_track_from_whens_and_coords(self) -> None:
+ whens = [
+ KmlDateTime(
+ datetime.datetime(2023, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc),
+ ),
+ ]
+ coords = [(1, 2)]
+
+ track = Track(
+ whens=whens,
+ coords=coords,
+ )
+
+ assert "when>" in track.to_string()
+ assert ">2023-01-01T00:00:00+00:00" in track.to_string()
+ assert "coord>" in track.to_string()
+ assert ">1 2" in track.to_string()
+ assert track.coords == ((1, 2),)
+
+ def test_track_from_whens_and_coords_and_track_items(self) -> None:
+ whens = [
+ KmlDateTime(
+ datetime.datetime(2023, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc),
+ ),
+ ]
+ coords = [(1, 2)]
+ time1 = KmlDateTime(
+ datetime.datetime(2023, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc),
+ )
+ angle = Angle()
+ track_items = [TrackItem(when=time1, coord=geo.Point(1, 2), angle=angle)]
+
+ with pytest.raises(
+ ValueError,
+ match="^Cannot specify both geometry and track_items$",
+ ):
+ Track(
+ whens=whens,
+ coords=coords,
+ track_items=track_items,
+ )
+
def test_track_precision(self) -> None:
track = Track(
id="x",
@@ -335,6 +378,20 @@ def test_track_from_str_invalid_when(self) -> None:
assert track.track_items == []
+ def test_track_from_str_invalid_coord(self) -> None:
+ doc = """
+
+ 2010-02-14T02:02:09Z
+ 45.54676 66.2342 77.0
+ XYZ 37.371915 156.000000
+
+ """
+
+ track = Track.from_string(doc, strict=False)
+
+ assert track.track_items == []
+
class TestMultiTrack(StdLibrary):
"""Test gx.MultiTrack."""