From 852402584450024019d75c1640eff477688144ea Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Thu, 7 Nov 2024 00:38:33 -0500 Subject: [PATCH 01/10] KML Write Function --- fastkml/kml.py | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/fastkml/kml.py b/fastkml/kml.py index 7c5dab89..e5d62ea5 100644 --- a/fastkml/kml.py +++ b/fastkml/kml.py @@ -27,6 +27,7 @@ """ import logging from pathlib import Path +import pathlib from typing import IO from typing import Any from typing import AnyStr @@ -122,7 +123,7 @@ def etree_element( """ # self.ns may be empty, which leads to unprefixed kml elements. # However, in this case the xlmns should still be mentioned on the kml - # element, just without prefix. + # element, just without prefix.""" if not self.ns: root = config.etree.Element( f"{self.ns}{self.get_tag_name()}", @@ -164,26 +165,6 @@ def parse( 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. - Can be a file path (str or Path), or a file-like object. - - 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, @@ -208,6 +189,27 @@ def parse( element=root, ) + def write( + self, + file_name:pathlib.Path, + *, + prettyprint:bool=False, + xml_declaration:bool=False + ) -> None: + + element = self.etree_element() + + tree = config.etree.tostring( + element, + encoding="UTF-8", + pretty_print=prettyprint, + ).decode( + "UTF-8", + ) + + with open(file_name, "wb") as file: + tree.write(file, encoding="UTF-8", xml_declaration=xml_declaration) + registry.register( KML, From c33c8750645b6e7fda0e41703cbb0fde8a7d6d08 Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Thu, 7 Nov 2024 00:46:23 -0500 Subject: [PATCH 02/10] doc string --- fastkml/kml.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/fastkml/kml.py b/fastkml/kml.py index e5d62ea5..1a364c21 100644 --- a/fastkml/kml.py +++ b/fastkml/kml.py @@ -165,6 +165,22 @@ def parse( 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. + Can be a file path (str or Path), or a file-like object. + 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, @@ -193,10 +209,21 @@ def write( self, file_name:pathlib.Path, *, - prettyprint:bool=False, - xml_declaration:bool=False + prettyprint:bool=True, + xml_declaration:bool=True ) -> None: + """ + Write KML to a file + Args: + ---- + file_name: The file name where to save the file. + Can be any string value + prettyprint : bool, default=True + Whether to pretty print the XML. + xml_declarationL bool, default=True + For True, value is '' else '' + """ element = self.etree_element() tree = config.etree.tostring( From 7ad88502d141e21fa31e497eb4319627d4447f7a Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Thu, 7 Nov 2024 00:47:25 -0500 Subject: [PATCH 03/10] spaces --- fastkml/kml.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fastkml/kml.py b/fastkml/kml.py index 1a364c21..861e239c 100644 --- a/fastkml/kml.py +++ b/fastkml/kml.py @@ -167,19 +167,23 @@ def parse( ) -> Self: """ Parse a KML file and return a KML object. + Args: ---- file: The file to parse. Can be a file path (str or Path), or a file-like object. + 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( From 49a92e1fb8d9e398256a71f2f975f8fa8f934193 Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Fri, 8 Nov 2024 04:04:56 -0500 Subject: [PATCH 04/10] zip file --- fastkml/kml.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/fastkml/kml.py b/fastkml/kml.py index 861e239c..c8bd7be1 100644 --- a/fastkml/kml.py +++ b/fastkml/kml.py @@ -26,6 +26,7 @@ """ import logging +import os from pathlib import Path import pathlib from typing import IO @@ -39,6 +40,7 @@ from typing import cast from typing_extensions import Self +import zipfile from fastkml import config from fastkml.base import _XMLObject @@ -211,11 +213,11 @@ def parse( def write( self, - file_name:pathlib.Path, + file_path: pathlib.Path, *, - prettyprint:bool=True, - xml_declaration:bool=True - ) -> None: + prettyprint: bool = True, + xml_declaration: bool = True + ) -> None: """ Write KML to a file Args: @@ -226,10 +228,10 @@ def write( Whether to pretty print the XML. xml_declarationL bool, default=True For True, value is '' else '' - + """ element = self.etree_element() - + tree = config.etree.tostring( element, encoding="UTF-8", @@ -238,8 +240,12 @@ def write( "UTF-8", ) - with open(file_name, "wb") as file: - tree.write(file, encoding="UTF-8", xml_declaration=xml_declaration) + if file_path.suffix == ".kmz": + with zipfile.ZipFile(file_path, 'w', zipfile.ZIP_DEFLATED) as kmz: + kmz.write(file_path, arcname=os.path.basename(file_path)) + else: + with open(file_path, "wb") as file: + tree.write(file, encoding="UTF-8", xml_declaration=xml_declaration) registry.register( From fb606331ae4dc152da63564fbaf426d76167154a Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Tue, 12 Nov 2024 01:22:25 -0500 Subject: [PATCH 05/10] Test cases --- fastkml/kml.py | 32 ++++++++----- tests/kml_test.py | 56 ++++++++++++++++++++++ tests/ogc_conformance/data/kml/output.kml | 2 + tests/ogc_conformance/data/kml/output.kmz | Bin 0 -> 120 bytes 4 files changed, 79 insertions(+), 11 deletions(-) create mode 100644 tests/ogc_conformance/data/kml/output.kml create mode 100644 tests/ogc_conformance/data/kml/output.kmz diff --git a/fastkml/kml.py b/fastkml/kml.py index c8bd7be1..25ace12b 100644 --- a/fastkml/kml.py +++ b/fastkml/kml.py @@ -28,7 +28,6 @@ import logging import os from pathlib import Path -import pathlib from typing import IO from typing import Any from typing import AnyStr @@ -213,7 +212,7 @@ def parse( def write( self, - file_path: pathlib.Path, + file_path: Path, *, prettyprint: bool = True, xml_declaration: bool = True @@ -232,20 +231,31 @@ def write( """ element = self.etree_element() - tree = config.etree.tostring( - element, - encoding="UTF-8", - pretty_print=prettyprint, - ).decode( - "UTF-8", - ) + try: + + tree = config.etree.tostring( + element, + encoding="UTF-8", + pretty_print=prettyprint, + xml_declaration=xml_declaration + ).decode( + "UTF-8", + ) + except TypeError: + tree = config.etree.tostring( + element, + encoding="UTF-8", + xml_declaration=xml_declaration + ).decode( + "UTF-8", + ) if file_path.suffix == ".kmz": with zipfile.ZipFile(file_path, 'w', zipfile.ZIP_DEFLATED) as kmz: kmz.write(file_path, arcname=os.path.basename(file_path)) else: - with open(file_path, "wb") as file: - tree.write(file, encoding="UTF-8", xml_declaration=xml_declaration) + with open(file_path, "w") as file: + file.write(tree) registry.register( diff --git a/tests/kml_test.py b/tests/kml_test.py index e7189417..48b79e67 100644 --- a/tests/kml_test.py +++ b/tests/kml_test.py @@ -184,6 +184,62 @@ def test_kml_parse(self) -> None: assert doc.ns == "None" +class TestWriteKML(StdLibrary): + + def test_write_kml_file(self) -> None: + + doc = kml.KML( + ns="{http://www.opengis.net/kml/2.2}", + name="Vestibulum eleifend lobortis lorem.", + 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=[], + ), + ], + ) + + file_path = KMLFILEDIR / "output.kml" + kml.KML.write(doc, file_path=file_path, prettyprint=True, xml_declaration=True) + + assert file_path.is_file(), "KML file was not created." + + def test_write_kmz_file(self) -> None: + + doc = kml.KML( + ns="{http://www.opengis.net/kml/2.2}", + name="Vestibulum eleifend lobortis lorem.", + 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=[], + ), + ], + ) + + file_path = KMLFILEDIR / "output.kmz" + + kml.KML.write(doc, file_path=file_path, prettyprint=True, xml_declaration=True) + + assert file_path.is_file(), "KMZ file was not created." + + class TestLxml(Lxml, TestStdLibrary): """Test with lxml.""" diff --git a/tests/ogc_conformance/data/kml/output.kml b/tests/ogc_conformance/data/kml/output.kml new file mode 100644 index 00000000..1bf68287 --- /dev/null +++ b/tests/ogc_conformance/data/kml/output.kml @@ -0,0 +1,2 @@ + +Vestibulum eleifend lobortis lorem. \ No newline at end of file diff --git a/tests/ogc_conformance/data/kml/output.kmz b/tests/ogc_conformance/data/kml/output.kmz new file mode 100644 index 0000000000000000000000000000000000000000..a71b613ed95716eba56991a5222206cacd64ba70 GIT binary patch literal 120 zcmWIWW@Zs#U|`^2aN^2|1Tq+yKm-tQ0dan5NkM6eUUqI3GedwkBa;X-Zna=dKw?QF Xh=tG@;LXYg5@7^F3m~lp;xGUJZ7dF6 literal 0 HcmV?d00001 From 5e57936741b6a70a31f886464178af974fcff72c Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Wed, 13 Nov 2024 02:18:52 -0500 Subject: [PATCH 06/10] indentation fix --- fastkml/kml.py | 2 +- tests/kml_test.py | 105 +++++++++++++++++++++++----------------------- 2 files changed, 53 insertions(+), 54 deletions(-) diff --git a/fastkml/kml.py b/fastkml/kml.py index 25ace12b..085c060c 100644 --- a/fastkml/kml.py +++ b/fastkml/kml.py @@ -232,7 +232,7 @@ def write( element = self.etree_element() try: - + tree = config.etree.tostring( element, encoding="UTF-8", diff --git a/tests/kml_test.py b/tests/kml_test.py index 48b79e67..f0ded846 100644 --- a/tests/kml_test.py +++ b/tests/kml_test.py @@ -185,59 +185,58 @@ def test_kml_parse(self) -> None: class TestWriteKML(StdLibrary): - - def test_write_kml_file(self) -> None: - - doc = kml.KML( - ns="{http://www.opengis.net/kml/2.2}", - name="Vestibulum eleifend lobortis lorem.", - 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=[], - ), - ], - ) - - file_path = KMLFILEDIR / "output.kml" - kml.KML.write(doc, file_path=file_path, prettyprint=True, xml_declaration=True) - - assert file_path.is_file(), "KML file was not created." - - def test_write_kmz_file(self) -> None: - - doc = kml.KML( - ns="{http://www.opengis.net/kml/2.2}", - name="Vestibulum eleifend lobortis lorem.", - 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=[], - ), - ], - ) - - file_path = KMLFILEDIR / "output.kmz" - - kml.KML.write(doc, file_path=file_path, prettyprint=True, xml_declaration=True) - - assert file_path.is_file(), "KMZ file was not created." + def test_write_kml_file(self) -> None: + + doc = kml.KML( + ns="{http://www.opengis.net/kml/2.2}", + name="Vestibulum eleifend lobortis lorem.", + 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=[], + ), + ], + ) + + file_path = KMLFILEDIR / "output.kml" + kml.KML.write(doc, file_path=file_path, prettyprint=True, xml_declaration=True) + + assert file_path.is_file(), "KML file was not created." + + def test_write_kmz_file(self) -> None: + + doc = kml.KML( + ns="{http://www.opengis.net/kml/2.2}", + name="Vestibulum eleifend lobortis lorem.", + 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=[], + ), + ], + ) + + file_path = KMLFILEDIR / "output.kmz" + + kml.KML.write(doc, file_path=file_path, prettyprint=True, xml_declaration=True) + + assert file_path.is_file(), "KMZ file was not created." class TestLxml(Lxml, TestStdLibrary): From 6b2cf51ed7ee3cd4e51f18d7581c770801996351 Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Wed, 13 Nov 2024 02:49:24 -0500 Subject: [PATCH 07/10] requested changes --- fastkml/kml.py | 7 +++---- tests/kml_test.py | 4 ++-- tests/ogc_conformance/data/kml/output.kmz | Bin 120 -> 273 bytes 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/fastkml/kml.py b/fastkml/kml.py index 4ade8ce2..b3db3621 100644 --- a/fastkml/kml.py +++ b/fastkml/kml.py @@ -27,7 +27,6 @@ """ import logging -import os from pathlib import Path from typing import IO from typing import Any @@ -257,7 +256,7 @@ def write( Write KML to a file Args: ---- - file_name: The file name where to save the file. + file_path: The file name where to save the file. Can be any string value prettyprint : bool, default=True Whether to pretty print the XML. @@ -288,9 +287,9 @@ def write( if file_path.suffix == ".kmz": with zipfile.ZipFile(file_path, 'w', zipfile.ZIP_DEFLATED) as kmz: - kmz.write(file_path, arcname=os.path.basename(file_path)) + kmz.writestr('doc.kml', tree) else: - with open(file_path, "w") as file: + with open(file_path, "w", encoding="UTF-8") as file: file.write(tree) diff --git a/tests/kml_test.py b/tests/kml_test.py index e5daeea7..f449a360 100644 --- a/tests/kml_test.py +++ b/tests/kml_test.py @@ -209,7 +209,7 @@ def test_write_kml_file(self) -> None: ) file_path = KMLFILEDIR / "output.kml" - kml.KML.write(doc, file_path=file_path, prettyprint=True, xml_declaration=True) + doc.write(file_path=file_path, prettyprint=True, xml_declaration=True) assert file_path.is_file(), "KML file was not created." @@ -236,7 +236,7 @@ def test_write_kmz_file(self) -> None: file_path = KMLFILEDIR / "output.kmz" - kml.KML.write(doc, file_path=file_path, prettyprint=True, xml_declaration=True) + doc.write(file_path=file_path, prettyprint=True, xml_declaration=True) assert file_path.is_file(), "KMZ file was not created." diff --git a/tests/ogc_conformance/data/kml/output.kmz b/tests/ogc_conformance/data/kml/output.kmz index a71b613ed95716eba56991a5222206cacd64ba70..bc737f2d7353ab51602cd510b5a879b62ffd1180 100644 GIT binary patch literal 273 zcmWIWW@Zs#U|`^2kQB>}RBcVVun@?*0mSS;oRXicmz|qq+q;pk$v~o|eEy=umoD5s zZ5K94q_dZ@|60xaHvW>>$B%0}_@>`^y8E>0+tX5?Smd`jJ9>u&Fj-hE{iyfiY0a7M zTc++#+p^40X=U%j$1dMG3RhXiPix)D`C%QibBB##+(xa*ub(|=yUFCXXzGb6(w`P- zn77Pjt!x!2wV!=c$Y4Q!Lzl#LYxz=-W6i0&|F>Vid$NT2#<~8M)B3;X<`{bJV+` Date: Thu, 14 Nov 2024 01:54:59 -0500 Subject: [PATCH 08/10] Requested changes and deleting the output files --- tests/kml_test.py | 23 ++++++++++++++++++++-- tests/ogc_conformance/data/kml/output.kml | 2 -- tests/ogc_conformance/data/kml/output.kmz | Bin 273 -> 0 bytes 3 files changed, 21 insertions(+), 4 deletions(-) delete mode 100644 tests/ogc_conformance/data/kml/output.kml delete mode 100644 tests/ogc_conformance/data/kml/output.kmz diff --git a/tests/kml_test.py b/tests/kml_test.py index fb04fb25..547161b7 100644 --- a/tests/kml_test.py +++ b/tests/kml_test.py @@ -18,6 +18,7 @@ import io import pathlib +import zipfile import pygeoif as geo import pytest @@ -209,10 +210,16 @@ def test_write_kml_file(self) -> None: ) file_path = KMLFILEDIR / "output.kml" - doc.write(file_path=file_path, prettyprint=True, xml_declaration=True) + doc.write(file_path=file_path, prettyprint=True, xml_declaration=False) assert file_path.is_file(), "KML file was not created." + parsed_doc = kml.KML.parse(file_path) + + assert parsed_doc.to_string() == doc.to_string(), "Written and original documents don't match" + + file_path.unlink() + def test_write_kmz_file(self) -> None: doc = kml.KML( @@ -236,10 +243,22 @@ def test_write_kmz_file(self) -> None: file_path = KMLFILEDIR / "output.kmz" - doc.write(file_path=file_path, prettyprint=True, xml_declaration=True) + doc.write(file_path=file_path, prettyprint=True, xml_declaration=False) assert file_path.is_file(), "KMZ file was not created." + tree = doc.to_string() + + with zipfile.ZipFile(file_path, 'r') as kmz: + assert 'doc.kml' in kmz.namelist(), "doc.kml not found in the KMZ file" + + with kmz.open('doc.kml') as doc_kml: + kml_content = doc_kml.read().decode("utf-8") + + assert kml_content == tree, "KML content does not match expected content" + + file_path.unlink() + class TestLxml(Lxml, TestStdLibrary): """Test with lxml.""" diff --git a/tests/ogc_conformance/data/kml/output.kml b/tests/ogc_conformance/data/kml/output.kml deleted file mode 100644 index 1bf68287..00000000 --- a/tests/ogc_conformance/data/kml/output.kml +++ /dev/null @@ -1,2 +0,0 @@ - -Vestibulum eleifend lobortis lorem. \ No newline at end of file diff --git a/tests/ogc_conformance/data/kml/output.kmz b/tests/ogc_conformance/data/kml/output.kmz deleted file mode 100644 index bc737f2d7353ab51602cd510b5a879b62ffd1180..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 273 zcmWIWW@Zs#U|`^2kQB>}RBcVVun@?*0mSS;oRXicmz|qq+q;pk$v~o|eEy=umoD5s zZ5K94q_dZ@|60xaHvW>>$B%0}_@>`^y8E>0+tX5?Smd`jJ9>u&Fj-hE{iyfiY0a7M zTc++#+p^40X=U%j$1dMG3RhXiPix)D`C%QibBB##+(xa*ub(|=yUFCXXzGb6(w`P- zn77Pjt!x!2wV!=c$Y4Q!Lzl#LYxz=-W6i0&|F>Vid$NT2#<~8M)B3;X<`{bJV+` Date: Thu, 14 Nov 2024 02:07:36 -0500 Subject: [PATCH 09/10] indentation fix --- tests/kml_test.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/kml_test.py b/tests/kml_test.py index 547161b7..8615b961 100644 --- a/tests/kml_test.py +++ b/tests/kml_test.py @@ -216,7 +216,7 @@ def test_write_kml_file(self) -> None: parsed_doc = kml.KML.parse(file_path) - assert parsed_doc.to_string() == doc.to_string(), "Written and original documents don't match" + assert parsed_doc.to_string() == doc.to_string() file_path.unlink() @@ -250,12 +250,12 @@ def test_write_kmz_file(self) -> None: tree = doc.to_string() with zipfile.ZipFile(file_path, 'r') as kmz: - assert 'doc.kml' in kmz.namelist(), "doc.kml not found in the KMZ file" - - with kmz.open('doc.kml') as doc_kml: - kml_content = doc_kml.read().decode("utf-8") - - assert kml_content == tree, "KML content does not match expected content" + assert 'doc.kml' in kmz.namelist(), "doc.kml not found in the KMZ file" + + with kmz.open('doc.kml') as doc_kml: + kml_content = doc_kml.read().decode("utf-8") + + assert kml_content == tree, "KML content does not match expected content" file_path.unlink() From e1aa026cd2a956d4a28e1bcbc535769ccb85f785 Mon Sep 17 00:00:00 2001 From: apurvabanka Date: Thu, 14 Nov 2024 02:09:21 -0500 Subject: [PATCH 10/10] white spaces --- tests/kml_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/kml_test.py b/tests/kml_test.py index 8615b961..f1376769 100644 --- a/tests/kml_test.py +++ b/tests/kml_test.py @@ -251,10 +251,10 @@ def test_write_kmz_file(self) -> None: with zipfile.ZipFile(file_path, 'r') as kmz: assert 'doc.kml' in kmz.namelist(), "doc.kml not found in the KMZ file" - + with kmz.open('doc.kml') as doc_kml: kml_content = doc_kml.read().decode("utf-8") - + assert kml_content == tree, "KML content does not match expected content" file_path.unlink()