diff --git a/fastkml/geometry.py b/fastkml/geometry.py
index e0827b44..6b314140 100644
--- a/fastkml/geometry.py
+++ b/fastkml/geometry.py
@@ -911,15 +911,15 @@ class InnerBoundaryIs(_XMLObject):
"""Represents the inner boundary of a polygon in KML."""
_default_nsid = config.KML
- kml_geometries: List[LinearRing]
+ kml_geometry: Optional[LinearRing]
def __init__(
self,
*,
ns: Optional[str] = None,
name_spaces: Optional[Dict[str, str]] = None,
- geometries: Optional[Iterable[geo.LinearRing]] = None,
- kml_geometries: Optional[Iterable[LinearRing]] = None,
+ geometry: Optional[geo.LinearRing] = None,
+ kml_geometry: Optional[LinearRing] = None,
**kwargs: Any,
) -> None:
"""
@@ -931,37 +931,31 @@ def __init__(
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.
- geometries : Optional[Iterable[geo.LinearRing]], optional
- The geometries to be converted to KML geometries, by default None.
- kml_geometries : Optional[Iterable[LinearRing]], optional
- The KML geometries, 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 `geometries` and `kml_geometries` are provided.
+ If both `geometry` and `kml_geometry` are provided.
Notes
-----
- - If `geometries` is provided, it will be converted to KML geometries and
- stored in `kml_geometries`.
- - If `geometries` is not provided, `kml_geometries` will be an empty list.
+ - 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.
"""
- if geometries is not None and kml_geometries is not None:
+ if geometry is not None and kml_geometry is not None:
raise GeometryError(MsgMutualExclusive)
- if kml_geometries is None:
- kml_geometries = (
- [
- LinearRing(ns=ns, name_spaces=name_spaces, geometry=lr)
- for lr in geometries
- ]
- if geometries
- else None
- )
- self.kml_geometries = list(kml_geometries) if kml_geometries else []
+ 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,
@@ -970,7 +964,7 @@ def __init__(
def __bool__(self) -> bool:
"""Return True if any of the inner boundary geometries exist."""
- return any(b.geometry for b in self.kml_geometries)
+ return bool(self.kml_geometry)
def __repr__(self) -> str:
"""Create a string (c)representation for InnerBoundaryIs."""
@@ -978,7 +972,7 @@ def __repr__(self) -> str:
f"{self.__class__.__module__}.{self.__class__.__name__}("
f"ns={self.ns!r}, "
f"name_spaces={self.name_spaces!r}, "
- f"kml_geometries={self.kml_geometries!r}, "
+ f"kml_geometry={self.kml_geometry!r}, "
f"**{self._get_splat()},"
")"
)
@@ -989,15 +983,13 @@ def get_tag_name(cls) -> str:
return "innerBoundaryIs"
@property
- def geometries(self) -> Optional[Iterable[geo.LinearRing]]:
+ def geometry(self) -> Optional[geo.LinearRing]:
"""
Return the list of LinearRing objects representing the inner boundary.
If no inner boundary geometries exist, returns None.
"""
- if not self.kml_geometries:
- return None
- return [lr.geometry for lr in self.kml_geometries if lr.geometry]
+ return self.kml_geometry.geometry if self.kml_geometry else None
registry.register(
@@ -1005,10 +997,10 @@ def geometries(self) -> Optional[Iterable[geo.LinearRing]]:
item=RegistryItem(
ns_ids=("kml",),
classes=(LinearRing,),
- attr_name="kml_geometries",
+ attr_name="kml_geometry",
node_name="LinearRing",
- get_kwarg=xml_subelement_list_kwarg,
- set_element=xml_subelement_list,
+ get_kwarg=xml_subelement_kwarg,
+ set_element=xml_subelement,
),
)
@@ -1042,8 +1034,8 @@ class Polygon(_Geometry):
https://developers.google.com/kml/documentation/kmlreference#polygon
"""
- outer_boundary_is: Optional[OuterBoundaryIs]
- inner_boundary_is: Optional[InnerBoundaryIs]
+ outer_boundary: Optional[OuterBoundaryIs]
+ inner_boundaries: List[InnerBoundaryIs]
def __init__(
self,
@@ -1055,8 +1047,8 @@ def __init__(
extrude: Optional[bool] = None,
tessellate: Optional[bool] = None,
altitude_mode: Optional[AltitudeMode] = None,
- outer_boundary_is: Optional[OuterBoundaryIs] = None,
- inner_boundary_is: Optional[InnerBoundaryIs] = None,
+ outer_boundary: Optional[OuterBoundaryIs] = None,
+ inner_boundaries: Optional[Iterable[InnerBoundaryIs]] = None,
geometry: Optional[geo.Polygon] = None,
**kwargs: Any,
) -> None:
@@ -1079,9 +1071,9 @@ def __init__(
The tessellate flag of the element.
altitude_mode : Optional[AltitudeMode]
The altitude mode of the element.
- outer_boundary_is : Optional[OuterBoundaryIs]
+ outer_boundary : Optional[OuterBoundaryIs]
The outer boundary of the element.
- inner_boundary_is : Optional[InnerBoundaryIs]
+ inner_boundaries : Optional[Iterable[InnerBoundaryIs]]
The inner boundaries of the element.
geometry : Optional[geo.Polygon]
The geometry object of the element.
@@ -1098,13 +1090,15 @@ def __init__(
None
"""
- if outer_boundary_is is not None and geometry is not None:
+ if outer_boundary is not None and geometry is not None:
raise GeometryError(MsgMutualExclusive)
if geometry is not None:
- outer_boundary_is = OuterBoundaryIs(geometry=geometry.exterior)
- inner_boundary_is = InnerBoundaryIs(geometries=geometry.interiors)
- self.outer_boundary_is = outer_boundary_is
- self.inner_boundary_is = inner_boundary_is
+ outer_boundary = OuterBoundaryIs(geometry=geometry.exterior)
+ inner_boundaries = [
+ InnerBoundaryIs(geometry=interior) for interior in geometry.interiors
+ ]
+ self.outer_boundary = outer_boundary
+ self.inner_boundaries = list(inner_boundaries) if inner_boundaries else []
super().__init__(
ns=ns,
name_spaces=name_spaces,
@@ -1126,7 +1120,7 @@ def __bool__(self) -> bool:
True if the outer boundary is defined, False otherwise.
"""
- return bool(self.outer_boundary_is)
+ return bool(self.outer_boundary)
@property
def geometry(self) -> Optional[geo.Polygon]:
@@ -1139,15 +1133,19 @@ def geometry(self) -> Optional[geo.Polygon]:
The geometry object representing the geometry of the Polygon.
"""
- if not self.outer_boundary_is:
+ if not self.outer_boundary:
return None
- if not self.inner_boundary_is:
+ if not self.inner_boundaries:
return geo.Polygon.from_linear_rings(
- cast(geo.LinearRing, self.outer_boundary_is.geometry),
+ cast(geo.LinearRing, self.outer_boundary.geometry),
)
- return geo.Polygon.from_linear_rings( # type: ignore[misc]
- cast(geo.LinearRing, self.outer_boundary_is.geometry),
- *self.inner_boundary_is.geometries,
+ return geo.Polygon.from_linear_rings(
+ cast(geo.LinearRing, self.outer_boundary.geometry),
+ *[
+ interior.geometry
+ for interior in self.inner_boundaries
+ if interior.geometry is not None
+ ],
)
def __repr__(self) -> str:
@@ -1169,8 +1167,8 @@ def __repr__(self) -> str:
f"extrude={self.extrude!r}, "
f"tessellate={self.tessellate!r}, "
f"altitude_mode={self.altitude_mode}, "
- f"outer_boundary_is={self.outer_boundary_is!r}, "
- f"inner_boundary_is={self.inner_boundary_is!r}, "
+ f"outer_boundary={self.outer_boundary!r}, "
+ f"inner_boundaries={self.inner_boundaries!r}, "
f"**{self._get_splat()!r},"
")"
)
@@ -1181,7 +1179,7 @@ def __repr__(self) -> str:
item=RegistryItem(
ns_ids=("kml",),
classes=(OuterBoundaryIs,),
- attr_name="outer_boundary_is",
+ attr_name="outer_boundary",
node_name="outerBoundaryIs",
get_kwarg=xml_subelement_kwarg,
set_element=xml_subelement,
@@ -1192,10 +1190,10 @@ def __repr__(self) -> str:
item=RegistryItem(
ns_ids=("kml",),
classes=(InnerBoundaryIs,),
- attr_name="inner_boundary_is",
+ attr_name="inner_boundaries",
node_name="innerBoundaryIs",
- get_kwarg=xml_subelement_kwarg,
- set_element=xml_subelement,
+ get_kwarg=xml_subelement_list_kwarg,
+ set_element=xml_subelement_list,
),
)
diff --git a/tests/geometries/boundaries_test.py b/tests/geometries/boundaries_test.py
index 2736d99d..4b413f08 100644
--- a/tests/geometries/boundaries_test.py
+++ b/tests/geometries/boundaries_test.py
@@ -59,11 +59,13 @@ def test_read_outer_boundary(self) -> None:
def test_inner_boundary(self) -> None:
"""Test the init method."""
coords = ((1, 2), (2, 0), (0, 0), (1, 2))
+
inner_boundary = InnerBoundaryIs(
- kml_geometries=[LinearRing(kml_coordinates=Coordinates(coords=coords))],
+ kml_geometry=LinearRing(kml_coordinates=Coordinates(coords=coords)),
)
- assert inner_boundary.geometries == [geo.LinearRing(coords)]
+ assert inner_boundary.geometry == geo.LinearRing(coords)
+ assert bool(inner_boundary)
assert inner_boundary.to_string(prettyprint=False).strip() == (
''
""
@@ -71,8 +73,13 @@ def test_inner_boundary(self) -> None:
""
)
- def test_read_inner_boundary(self) -> None:
- """Test the from_string method."""
+ def test_read_inner_boundary_multiple_linestrings(self) -> None:
+ """
+ Test the from_string method.
+
+ When there are multiple LinearRings in the innerBoundaryIs element
+ only the first one is used.
+ """
inner_boundary = InnerBoundaryIs.class_from_string(
''
""
@@ -85,18 +92,9 @@ def test_read_inner_boundary(self) -> None:
"",
)
- assert inner_boundary.geometries == [
- geo.LinearRing(((1, 4), (2, 0), (0, 0), (1, 4))),
- geo.LinearRing(
- (
- (-122.366212, 37.818977, 30),
- (-122.365424, 37.819294, 30),
- (-122.365704, 37.819731, 30),
- (-122.366488, 37.819402, 30),
- (-122.366212, 37.818977, 30),
- ),
- ),
- ]
+ assert inner_boundary.geometry == geo.LinearRing(
+ ((1, 4), (2, 0), (0, 0), (1, 4)),
+ )
class TestBoundariesLxml(Lxml, TestBoundaries):
diff --git a/tests/geometries/geometry_test.py b/tests/geometries/geometry_test.py
index 223990fe..7e96699f 100644
--- a/tests/geometries/geometry_test.py
+++ b/tests/geometries/geometry_test.py
@@ -64,7 +64,7 @@ def test_extrude(self) -> None:
assert g.extrude is True
- def test_tesselate(self) -> None:
+ def test_tessellate(self) -> None:
doc = """
0.000000,1.000000
1
@@ -224,7 +224,8 @@ def test_multipolygon(self) -> None:
g = MultiGeometry.class_from_string(doc)
- assert len(g.geometry) == 2 # type: ignore[arg-type]
+ assert g.geometry is not None
+ assert len(g.geometry) == 2
def test_geometrycollection(self) -> None:
doc = """
@@ -464,6 +465,7 @@ def test_create_kml_geometry_polygon(self) -> None:
g = create_kml_geometry(geo.Polygon([(0, 0), (1, 1), (1, 0), (0, 0)]))
assert isinstance(g, Polygon)
+ assert g.geometry is not None
assert g.geometry.__geo_interface__ == {
"type": "Polygon",
"bbox": (0.0, 0.0, 1.0, 1.0),
diff --git a/tests/geometries/polygon_test.py b/tests/geometries/polygon_test.py
index cecb1cdc..13763494 100644
--- a/tests/geometries/polygon_test.py
+++ b/tests/geometries/polygon_test.py
@@ -18,6 +18,7 @@
import pygeoif.geometry as geo
+from fastkml.geometry import OuterBoundaryIs
from fastkml.geometry import Polygon
from tests.base import Lxml
from tests.base import StdLibrary
@@ -179,7 +180,9 @@ def test_empty_polygon(self) -> None:
polygon = Polygon.class_from_string(doc)
assert not polygon.geometry
- assert polygon.outer_boundary_is is not None
+ assert polygon.outer_boundary is not None
+ assert isinstance(polygon.outer_boundary, OuterBoundaryIs)
+ assert len(polygon.inner_boundaries) == 0
assert "tessellate>1" in polygon.to_string()
diff --git a/tests/repr_eq_test.py b/tests/repr_eq_test.py
index 4e0a39f3..5f88e0c0 100644
--- a/tests/repr_eq_test.py
+++ b/tests/repr_eq_test.py
@@ -477,7 +477,7 @@ class TestRepr(StdLibrary):
extrude=True,
tessellate=None,
altitude_mode=AltitudeMode.absolute,
- outer_boundary_is=fastkml.geometry.OuterBoundaryIs(
+ outer_boundary=fastkml.geometry.OuterBoundaryIs(
ns="{http://www.opengis.net/kml/2.2}",
name_spaces={
"kml": "{http://www.opengis.net/kml/2.2}",
@@ -527,7 +527,6 @@ class TestRepr(StdLibrary):
),
),
),
- inner_boundary_is=None,
),
),
fastkml.features.Placemark(
@@ -576,7 +575,7 @@ class TestRepr(StdLibrary):
extrude=True,
tessellate=None,
altitude_mode=AltitudeMode.absolute,
- outer_boundary_is=fastkml.geometry.OuterBoundaryIs(
+ outer_boundary=fastkml.geometry.OuterBoundaryIs(
ns="{http://www.opengis.net/kml/2.2}",
name_spaces={
"kml": "{http://www.opengis.net/kml/2.2}",
@@ -626,7 +625,6 @@ class TestRepr(StdLibrary):
),
),
),
- inner_boundary_is=None,
),
),
fastkml.features.Placemark(
@@ -675,7 +673,7 @@ class TestRepr(StdLibrary):
extrude=True,
tessellate=None,
altitude_mode=AltitudeMode.absolute,
- outer_boundary_is=fastkml.geometry.OuterBoundaryIs(
+ outer_boundary=fastkml.geometry.OuterBoundaryIs(
ns="{http://www.opengis.net/kml/2.2}",
name_spaces={
"kml": "{http://www.opengis.net/kml/2.2}",
@@ -870,7 +868,6 @@ class TestRepr(StdLibrary):
),
),
),
- inner_boundary_is=None,
),
),
fastkml.features.Placemark(
@@ -919,7 +916,7 @@ class TestRepr(StdLibrary):
extrude=True,
tessellate=None,
altitude_mode=AltitudeMode.absolute,
- outer_boundary_is=fastkml.geometry.OuterBoundaryIs(
+ outer_boundary=fastkml.geometry.OuterBoundaryIs(
ns="{http://www.opengis.net/kml/2.2}",
name_spaces={
"kml": "{http://www.opengis.net/kml/2.2}",
@@ -1004,15 +1001,15 @@ class TestRepr(StdLibrary):
),
),
),
- inner_boundary_is=fastkml.geometry.InnerBoundaryIs(
- 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}",
- },
- kml_geometries=[
- fastkml.geometry.LinearRing(
+ inner_boundaries=[
+ fastkml.geometry.InnerBoundaryIs(
+ 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}",
+ },
+ kml_geometry=fastkml.geometry.LinearRing(
ns="{http://www.opengis.net/kml/2.2}",
name_spaces={
"kml": "{http://www.opengis.net/kml/2.2}",
@@ -1364,8 +1361,8 @@ class TestRepr(StdLibrary):
),
),
),
- ],
- ),
+ ),
+ ],
),
),
],
@@ -1796,7 +1793,7 @@ class TestRepr(StdLibrary):
address=None,
phone_number=None,
snippet=None,
- description="Vue du ciel, la rivière des Outaouais est le principal élément naturel\n de la vallée du même nom. Cette splendide rivière, la deuxième plus\n grande dans l'est du Canada, possède un bassin hydrographique de 140 000\n km2 et s'étend sur plus de 1 271 km, en majeure partie dans le Bouclier\n canadien.",
+ description="Vue du ciel, la rivière des Outaouais est le principal élément naturel\n de la vallée du même nom. Cette splendide rivière, la deuxième plus\n grande dans l'est du Canada, possède un bassin hydrographique de 140 000\n km2 et s'étend sure plus de 1 271 km, en majeure partie dans le Bouclier\n canadien.",
view=None,
times=None,
style_url=None,