Skip to content

Commit

Permalink
add basic parse class method to KML #284
Browse files Browse the repository at this point in the history
  • Loading branch information
cleder committed Jul 26, 2024
1 parent 931b155 commit 1dc645d
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 0 deletions.
53 changes: 53 additions & 0 deletions fastkml/kml.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@
"""
import logging
from pathlib import Path
from typing import IO
from typing import Any
from typing import AnyStr
from typing import Dict
from typing import Iterable
from typing import List
Expand Down Expand Up @@ -199,6 +202,56 @@ def class_from_string(
element=element,
)

@classmethod
def parse(
cls,
file: Union[Path, str, IO[AnyStr]],
*,
ns: Optional[str] = None,
name_spaces: Optional[Dict[str, str]] = None,
strict: bool = True,
) -> Self:
"""
Parse a KML file and return a KML object.
Args:
----
file: The file to parse
Keyword Args:
------------
ns (Optional[str]): The namespace of the KML file.
If not provided, it will be inferred from the root element.
name_spaces (Optional[Dict[str, str]]): Additional namespaces.
strict (bool): Whether to enforce strict parsing rules. Defaults to True.
Returns:
-------
KML object: The parsed KML object.
"""
try:
tree = config.etree.parse(
file,
parser=config.etree.XMLParser(
huge_tree=True,
recover=True,
),
)
except TypeError:
tree = config.etree.parse(file)
root = tree.getroot()
if ns is None:
ns = cast(str, root.tag[:-3] if root.tag.endswith("kml") else "")
name_spaces = name_spaces or {}
name_spaces = {**config.NAME_SPACES, **name_spaces}
return cls.class_from_element(
ns=ns,
name_spaces=name_spaces,
strict=strict,
element=root,
)


registry.register(
KML,
Expand Down
81 changes: 81 additions & 0 deletions tests/kml_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

"""Test the kml class."""
import pathlib

import pygeoif as geo

from fastkml import features
from fastkml import kml
from fastkml.containers import Document
from fastkml.features import Placemark
from tests.base import Lxml
from tests.base import StdLibrary

BASEDIR = pathlib.Path(__file__).parent
KMLFILEDIR = BASEDIR / "ogc_conformance" / "data" / "kml"


class TestStdLibrary(StdLibrary):
"""Test with the standard library."""
Expand Down Expand Up @@ -52,6 +58,77 @@ def test_kml(self) -> None:
assert k.to_string() == k2.to_string()


class TestParseKML(StdLibrary):
def test_parse_kml(self) -> None:
empty_placemark = KMLFILEDIR / "emptyPlacemarkWithoutId.xml"

doc = kml.KML.parse(empty_placemark)

assert doc == kml.KML(
ns="{http://www.opengis.net/kml/2.2}",
features=[
Document(
ns="{http://www.opengis.net/kml/2.2}",
id="doc-001",
target_id="",
name="Vestibulum eleifend lobortis lorem.",
features=[
Placemark(
ns="{http://www.opengis.net/kml/2.2}",
),
],
schemata=[],
),
],
)

def test_parse_kml_filename(self) -> None:
empty_placemark = str(KMLFILEDIR / "emptyPlacemarkWithoutId.xml")

doc = kml.KML.parse(empty_placemark)

assert doc == kml.KML(
ns="{http://www.opengis.net/kml/2.2}",
features=[
Document(
ns="{http://www.opengis.net/kml/2.2}",
id="doc-001",
target_id="",
name="Vestibulum eleifend lobortis lorem.",
features=[
Placemark(
ns="{http://www.opengis.net/kml/2.2}",
),
],
schemata=[],
),
],
)

def test_parse_kml_fileobject(self) -> None:
empty_placemark = KMLFILEDIR / "emptyPlacemarkWithoutId.xml"
with empty_placemark.open() as f:
doc = kml.KML.parse(f)

assert doc == kml.KML(
ns="{http://www.opengis.net/kml/2.2}",
features=[
Document(
ns="{http://www.opengis.net/kml/2.2}",
id="doc-001",
target_id="",
name="Vestibulum eleifend lobortis lorem.",
features=[
Placemark(
ns="{http://www.opengis.net/kml/2.2}",
),
],
schemata=[],
),
],
)


class TestLxml(Lxml, TestStdLibrary):
"""Test with lxml."""

Expand All @@ -65,3 +142,7 @@ def test_from_string_with_unbound_prefix(self) -> None:
k = kml.KML.class_from_string(doc)
assert len(k.features) == 1
assert isinstance(k.features[0], features.Placemark)


class TestLxmlParseKML(Lxml, TestParseKML):
"""Test with Lxml."""

0 comments on commit 1dc645d

Please sign in to comment.