diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d7fcf74d..c67b7bdf 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,7 +1,7 @@
---
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.4.0
+ rev: v4.5.0
hooks:
- id: check-added-large-files
- id: check-docstring-first
@@ -21,7 +21,7 @@ repos:
- id: requirements-txt-fixer
- id: trailing-whitespace
- repo: https://github.com/ikamensh/flynt/
- rev: "0.78"
+ rev: "1.0.1"
hooks:
- id: flynt
- repo: https://github.com/MarcoGorelli/absolufy-imports
@@ -29,21 +29,21 @@ repos:
hooks:
- id: absolufy-imports
- repo: https://github.com/hakancelikdev/unimport
- rev: 0.15.0
+ rev: 1.0.0
hooks:
- id: unimport
args: [--remove, --include-star-import, --ignore-init, --gitignore]
- repo: https://github.com/asottile/pyupgrade
- rev: v3.3.1
+ rev: v3.15.0
hooks:
- id: pyupgrade
args: ["--py3-plus", "--py37-plus"]
- repo: https://github.com/psf/black
- rev: 23.3.0
+ rev: 23.9.1
hooks:
- id: black
- repo: https://github.com/PyCQA/flake8
- rev: 6.0.0
+ rev: 6.1.0
hooks:
- id: flake8
- repo: https://github.com/pycqa/isort
@@ -51,11 +51,11 @@ repos:
hooks:
- id: isort
- repo: https://github.com/charliermarsh/ruff-pre-commit
- rev: 'v0.0.259'
+ rev: 'v0.0.292'
hooks:
- id: ruff
- repo: https://github.com/PyCQA/flake8
- rev: 6.0.0
+ rev: 6.1.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/pygrep-hooks
diff --git a/fastkml/geometry.py b/fastkml/geometry.py
index 837deb8d..07333c3b 100644
--- a/fastkml/geometry.py
+++ b/fastkml/geometry.py
@@ -376,7 +376,7 @@ def _get_linear_ring(self, element: Element) -> Optional[geo.LinearRing]:
if lr is not None:
coords = self._get_coordinates(lr)
return geo.LinearRing(coords)
- return None
+ return None # type: ignore[unreachable]
@no_type_check
def _get_geometry(self, element: Element) -> Optional[GeometryType]:
@@ -413,6 +413,11 @@ def _get_multigeometry(self, element: Element) -> Optional[MultiGeometryType]:
# MultiGeometry
geoms: List[Union[AnyGeometryType, None]] = []
if element.tag == f"{self.ns}MultiGeometry":
+ multigeometries = element.findall(f"{self.ns}MultiGeometry")
+ for multigeometry in multigeometries:
+ geom = Geometry(ns=self.ns)
+ geom.from_element(multigeometry)
+ geoms.append(geom.geometry)
points = element.findall(f"{self.ns}Point")
for point in points:
self._get_geometry_spec(point)
@@ -445,23 +450,23 @@ def _get_multigeometry(self, element: Element) -> Optional[MultiGeometryType]:
geom_types = {geom.geom_type for geom in clean_geoms}
if len(geom_types) > 1:
return geo.GeometryCollection(
- clean_geoms,
+ clean_geoms, # type: ignore[arg-type]
)
if "Point" in geom_types:
return geo.MultiPoint.from_points(
- *clean_geoms,
+ *clean_geoms, # type: ignore[arg-type]
)
elif "LineString" in geom_types:
return geo.MultiLineString.from_linestrings(
- *clean_geoms,
+ *clean_geoms, # type: ignore[arg-type]
)
elif "Polygon" in geom_types:
return geo.MultiPolygon.from_polygons(
- *clean_geoms,
+ *clean_geoms, # type: ignore[arg-type]
)
elif "LinearRing" in geom_types:
return geo.GeometryCollection(
- clean_geoms,
+ clean_geoms, # type: ignore[arg-type]
)
return None
diff --git a/pyproject.toml b/pyproject.toml
index 0a29180a..4ec400ee 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -47,3 +47,7 @@ module = [
"fastkml.tests.oldunit_test", "fastkml.tests.config_test"
]
ignore_errors = true
+
+[tool.ruff]
+[tool.ruff.extend-per-file-ignores]
+"tests/oldunit_test.py" = ["E501"]
diff --git a/tests/oldunit_test.py b/tests/oldunit_test.py
index da3e00ff..348c372e 100644
--- a/tests/oldunit_test.py
+++ b/tests/oldunit_test.py
@@ -1381,6 +1381,319 @@ def test_geometrycollection(self) -> None:
assert "coordinates>0.000000,1.000000" in str(g.to_string())
+def test_nested_multigeometry():
+ doc = """
+
+
+
+
+
+ -122.366278,37.818844,0 -122.365248,37.819267,0 -122.365640,37.819875,0 -122.366278,37.818844,0
+
+
+
+
+
+ -122.365,37.819,0
+
+
+
+
+ -122.365278,37.819000,0 -122.365248,37.819267,0
+
+
+
+
+
+
+ -122.365248,37.819267,0 -122.365640,37.819875,0 -122.366278,37.818844,0 -122.365248,37.819267,0
+
+
+
+
+
+
+
+ """
+
+ k = kml.KML()
+ k.from_string(doc)
+ placemark = list(list(k.features())[0].features())[0]
+
+ first_multigeometry = placemark.geometry
+ assert len(list(first_multigeometry.geoms)) == 3
+
+ second_multigeometry = [
+ g for g in first_multigeometry.geoms if g.geom_type == "GeometryCollection"
+ ][0]
+ assert len(list(second_multigeometry.geoms)) == 2
+
+
+class TestGetGeometry:
+ def test_altitude_mode(self):
+ doc = """
+ 0.000000,1.000000
+ clampToGround
+ """
+
+ g = Geometry()
+ assert g.altitude_mode is None
+ g.from_string(doc)
+ assert g.altitude_mode.value == "clampToGround"
+
+ def test_extrude(self):
+ doc = """
+ 0.000000,1.000000
+ 1
+ """
+
+ g = Geometry()
+ assert g.extrude is False
+ g.from_string(doc)
+ assert g.extrude is True
+
+ def test_tesselate(self):
+ doc = """
+ 0.000000,1.000000
+ 1
+ """
+
+ g = Geometry()
+ assert g.tessellate is False
+ g.from_string(doc)
+ assert g.tessellate is True
+
+ def test_point(self):
+ doc = """
+ 0.000000,1.000000
+ """
+
+ g = Geometry()
+ g.from_string(doc)
+ assert g.geometry.__geo_interface__ == {
+ "type": "Point",
+ "bbox": (0.0, 1.0, 0.0, 1.0),
+ "coordinates": (0.0, 1.0),
+ }
+
+ def test_linestring(self):
+ doc = """
+ 0.000000,0.000000 1.000000,1.000000
+ """
+
+ g = Geometry()
+ g.from_string(doc)
+ assert g.geometry.__geo_interface__ == {
+ "type": "LineString",
+ "bbox": (0.0, 0.0, 1.0, 1.0),
+ "coordinates": ((0.0, 0.0), (1.0, 1.0)),
+ }
+
+ def test_linearring(self):
+ doc = """
+ 0.000000,0.000000 1.000000,0.000000 1.000000,1.000000 0.000000,0.000000
+
+ """
+
+ g = Geometry()
+ g.from_string(doc)
+ assert g.geometry.__geo_interface__ == {
+ "type": "LinearRing",
+ "bbox": (0.0, 0.0, 1.0, 1.0),
+ "coordinates": ((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)),
+ }
+
+ def test_polygon(self):
+ doc = """
+
+
+ 0.000000,0.000000 1.000000,0.000000 1.000000,1.000000 0.000000,0.000000
+
+
+
+ """
+
+ g = Geometry()
+ g.from_string(doc)
+ assert g.geometry.__geo_interface__ == {
+ "type": "Polygon",
+ "bbox": (0.0, 0.0, 1.0, 1.0),
+ "coordinates": (((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)),),
+ }
+ doc = """
+
+
+ -1.000000,-1.000000 2.000000,-1.000000 2.000000,2.000000 -1.000000,-1.000000
+
+
+
+
+ 0.000000,0.000000 1.000000,0.000000 1.000000,1.000000 0.000000,0.000000
+
+
+
+ """
+
+ g.from_string(doc)
+ assert g.geometry.__geo_interface__ == {
+ "type": "Polygon",
+ "bbox": (-1.0, -1.0, 2.0, 2.0),
+ "coordinates": (
+ ((-1.0, -1.0), (2.0, -1.0), (2.0, 2.0), (-1.0, -1.0)),
+ ((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)),
+ ),
+ }
+
+ def test_multipoint(self):
+ doc = """
+
+
+ 0.000000,1.000000
+
+
+ 1.000000,1.000000
+
+
+ """
+
+ g = Geometry()
+ g.from_string(doc)
+ assert len(g.geometry) == 2
+
+ def test_multilinestring(self):
+ doc = """
+
+
+ 0.000000,0.000000 1.000000,0.000000
+
+
+ 0.000000,1.000000 1.000000,1.000000
+
+
+ """
+
+ g = Geometry()
+ g.from_string(doc)
+ assert len(g.geometry) == 2
+
+ def test_multipolygon(self):
+ doc = """
+
+
+
+
+ -1.000000,-1.000000 2.000000,-1.000000 2.000000,2.000000 -1.000000,-1.000000
+
+
+
+
+ 0.000000,0.000000 1.000000,0.000000 1.000000,1.000000 0.000000,0.000000
+
+
+
+
+
+
+ 3.000000,0.000000 4.000000,0.000000 4.000000,1.000000 3.000000,0.000000
+
+
+
+
+ """
+
+ g = Geometry()
+ g.from_string(doc)
+ assert len(g.geometry) == 2
+
+ def test_geometrycollection(self):
+ doc = """
+
+
+
+
+ 3,0 4,0 4,1 3,0
+
+
+
+
+ 0.000000,1.000000
+
+
+ 0.000000,0.000000 1.000000,1.000000
+
+
+ 0.0,0.0 1.0,0.0 1.0,1.0 0.0,1.0 0.0,0.0
+
+
+ """
+
+ g = Geometry()
+ g.from_string(doc)
+ assert len(g.geometry) == 4
+ doc = """
+
+
+ 3.0,0.0 4.0,0.0 4.0,1.0 3.0,0.0
+
+
+ 0.0,0.0 1.0,0.0 1.0,1.0 0.0,0.0
+
+
+ """
+
+ g = Geometry()
+ g.from_string(doc)
+ assert len(g.geometry) == 2
+ assert g.geometry.geom_type == "GeometryCollection"
+
+ def test_nested_multigeometry(self):
+ doc = """
+
+
+
+
+
+ -122.366278,37.818844,0 -122.365248,37.819267,0 -122.365640,37.819875,0 -122.366278,37.818844,0
+
+
+
+
+
+ -122.365,37.819,0
+
+
+
+
+ -122.365278,37.819000,0 -122.365248,37.819267,0
+
+
+
+
+
+
+ -122.365248,37.819267,0 -122.365640,37.819875,0 -122.366278,37.818844,0 -122.365248,37.819267,0
+
+
+
+
+
+
+
+ """
+
+ k = kml.KML()
+ k.from_string(doc)
+ placemark = list(list(k.features())[0].features())[0]
+
+ first_multigeometry = placemark.geometry
+ assert len(list(first_multigeometry.geoms)) == 3
+
+ second_multigeometry = [
+ g for g in first_multigeometry.geoms if g.geom_type == "GeometryCollection"
+ ][0]
+ assert len(list(second_multigeometry.geoms)) == 2
+
+
class TestGetGxGeometry:
def test_track(self) -> None:
doc = """