diff --git a/docs/HISTORY.rst b/docs/HISTORY.rst
index 360ef1f5..4e171fa8 100644
--- a/docs/HISTORY.rst
+++ b/docs/HISTORY.rst
@@ -6,6 +6,7 @@ Changelog
- Add support for ScreenOverlay and Model.
- allow parsing kml files without namespace declarations.
+- Add support for NetworkLinkControl. [Apurva Banka]
1.0 (2024/11/19)
diff --git a/docs/network.kml b/docs/network.kml
new file mode 100644
index 00000000..48f6004d
--- /dev/null
+++ b/docs/network.kml
@@ -0,0 +1,16 @@
+
+
+
+ 43200
+ -1
+
+ A snippet of XHTML
+ ]]>
+
+ 2008-05-30
+
+
\ No newline at end of file
diff --git a/fastkml/__init__.py b/fastkml/__init__.py
index bf8afff5..e94dae15 100644
--- a/fastkml/__init__.py
+++ b/fastkml/__init__.py
@@ -58,6 +58,7 @@
from fastkml.model import Orientation
from fastkml.model import ResourceMap
from fastkml.model import Scale
+from fastkml.network_link_control import NetworkLinkControl
from fastkml.overlays import GroundOverlay
from fastkml.overlays import ImagePyramid
from fastkml.overlays import LatLonBox
@@ -119,6 +120,7 @@
"Model",
"MultiGeometry",
"NetworkLink",
+ "NetworkLinkControl",
"Orientation",
"OuterBoundaryIs",
"OverlayXY",
diff --git a/fastkml/kml.py b/fastkml/kml.py
index aa9c4db1..aee6f9cd 100644
--- a/fastkml/kml.py
+++ b/fastkml/kml.py
@@ -51,6 +51,7 @@
from fastkml.features import Placemark
from fastkml.helpers import xml_subelement_list
from fastkml.helpers import xml_subelement_list_kwarg
+from fastkml.network_link_control import NetworkLinkControl
from fastkml.overlays import GroundOverlay
from fastkml.overlays import PhotoOverlay
from fastkml.registry import RegistryItem
@@ -59,7 +60,14 @@
logger = logging.getLogger(__name__)
-kml_children = Union[Folder, Document, Placemark, GroundOverlay, PhotoOverlay]
+kml_children = Union[
+ Folder,
+ Document,
+ Placemark,
+ GroundOverlay,
+ PhotoOverlay,
+ NetworkLinkControl,
+]
def lxml_parse_and_validate(
@@ -285,9 +293,20 @@ def write(
registry.register(
KML,
RegistryItem(
- ns_ids=("kml", ""),
- classes=(Document, Folder, Placemark, GroundOverlay, PhotoOverlay, NetworkLink),
- node_name="Document,Folder,Placemark,GroundOverlay,PhotoOverlay,NetworkLink",
+ ns_ids=("kml",),
+ classes=(
+ Document,
+ Folder,
+ Placemark,
+ GroundOverlay,
+ PhotoOverlay,
+ NetworkLink,
+ NetworkLinkControl,
+ ),
+ node_name=(
+ "Document,Folder,Placemark,GroundOverlay,PhotoOverlay,NetworkLink,"
+ "NetworkLinkControl"
+ ),
attr_name="features",
get_kwarg=xml_subelement_list_kwarg,
set_element=xml_subelement_list,
diff --git a/fastkml/network_link_control.py b/fastkml/network_link_control.py
new file mode 100644
index 00000000..01d2d066
--- /dev/null
+++ b/fastkml/network_link_control.py
@@ -0,0 +1,261 @@
+# Copyright (C) 2024 Christian Ledermann
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+NetworkLinkControl class.
+
+Controls the behavior of files fetched by a .
+
+https://developers.google.com/kml/documentation/kmlreference#networklinkcontrol
+"""
+
+import logging
+from typing import Any
+from typing import Dict
+from typing import Optional
+from typing import Union
+
+from fastkml import config
+from fastkml.base import _XMLObject
+from fastkml.helpers import clean_string
+from fastkml.helpers import datetime_subelement
+from fastkml.helpers import datetime_subelement_kwarg
+from fastkml.helpers import float_subelement
+from fastkml.helpers import subelement_float_kwarg
+from fastkml.helpers import subelement_text_kwarg
+from fastkml.helpers import text_subelement
+from fastkml.helpers import xml_subelement
+from fastkml.helpers import xml_subelement_kwarg
+from fastkml.registry import RegistryItem
+from fastkml.registry import registry
+from fastkml.times import KmlDateTime
+from fastkml.views import Camera
+from fastkml.views import LookAt
+
+__all__ = [
+ "NetworkLinkControl",
+]
+
+logger = logging.getLogger(__name__)
+
+
+class NetworkLinkControl(_XMLObject):
+ """Controls the behavior of files fetched by a ."""
+
+ _default_nsid = config.KML
+
+ min_refresh_period: Optional[float]
+ max_session_length: Optional[float]
+ cookie: Optional[str]
+ message: Optional[str]
+ link_name: Optional[str]
+ link_description: Optional[str]
+ link_snippet: Optional[str]
+ expires: Optional[KmlDateTime]
+ view: Union[Camera, LookAt, None]
+
+ def __init__(
+ self,
+ ns: Optional[str] = None,
+ name_spaces: Optional[Dict[str, str]] = None,
+ min_refresh_period: Optional[float] = None,
+ max_session_length: Optional[float] = None,
+ cookie: Optional[str] = None,
+ message: Optional[str] = None,
+ link_name: Optional[str] = None,
+ link_description: Optional[str] = None,
+ link_snippet: Optional[str] = None,
+ expires: Optional[KmlDateTime] = None,
+ view: Optional[Union[Camera, LookAt]] = None,
+ **kwargs: Any,
+ ) -> None:
+ """
+ Create a NetworkLinkControl object.
+
+ Parameters
+ ----------
+ ns : str, optional
+ The namespace to use for the NetworkLinkControl object.
+ name_spaces : dict, optional
+ A dictionary of namespaces to use for the NetworkLinkControl object.
+ min_refresh_period : float, optional
+ The minimum number of seconds between fetches. A value of -1 indicates that
+ the NetworkLinkControl object should be fetched only once.
+ max_session_length : float, optional
+ The maximum number of seconds that the link should be followed.
+ cookie : str, optional
+ A string value that can be used to identify the client request.
+ message : str, optional
+ A message to be displayed to the user in case of a failure.
+ link_name : str, optional
+ The name of the link.
+ link_description : str, optional
+ A description of the link.
+ link_snippet : str, optional
+ A snippet of text to be displayed in the link.
+ expires : KmlDateTime, optional
+ The time at which the link should expire.
+ view : Camera or LookAt, optional
+ The view to be used when the link is followed.
+ **kwargs : Any, optional
+ Additional keyword arguments.
+
+ """
+ super().__init__(
+ ns=ns,
+ name_spaces=name_spaces,
+ **kwargs,
+ )
+ self.min_refresh_period = min_refresh_period
+ self.max_session_length = max_session_length
+ self.cookie = clean_string(cookie)
+ self.message = clean_string(message)
+ self.link_name = clean_string(link_name)
+ self.link_description = clean_string(link_description)
+ self.link_snippet = clean_string(link_snippet)
+ self.expires = expires
+ self.view = view
+
+ def __repr__(self) -> str:
+ """
+ Return a string representation of the NetworkLinkControl object.
+
+ Returns
+ -------
+ str: A string representation of the NetworkLinkControl object.
+
+ """
+ return (
+ f"{self.__class__.__module__}.{self.__class__.__name__}("
+ f"ns={self.ns!r}, "
+ f"name_spaces={self.name_spaces!r}, "
+ f"min_refresh_period={self.min_refresh_period!r}, "
+ f"max_session_length={self.max_session_length!r}, "
+ f"cookie={self.cookie!r}, "
+ f"message={self.message!r}, "
+ f"link_name={self.link_name!r}, "
+ f"link_description={self.link_description!r}, "
+ f"link_snippet={self.link_snippet!r}, "
+ f"expires={self.expires!r}, "
+ f"view={self.view!r}, "
+ f"**{self._get_splat()!r},"
+ ")"
+ )
+
+
+registry.register(
+ NetworkLinkControl,
+ RegistryItem(
+ ns_ids=("kml",),
+ attr_name="min_refresh_period",
+ node_name="minRefreshPeriod",
+ classes=(float,),
+ get_kwarg=subelement_float_kwarg,
+ set_element=float_subelement,
+ default=0,
+ ),
+)
+registry.register(
+ NetworkLinkControl,
+ RegistryItem(
+ ns_ids=("kml",),
+ attr_name="max_session_length",
+ node_name="maxSessionLength",
+ classes=(float,),
+ get_kwarg=subelement_float_kwarg,
+ set_element=float_subelement,
+ default=-1,
+ ),
+)
+registry.register(
+ NetworkLinkControl,
+ RegistryItem(
+ ns_ids=("kml",),
+ attr_name="cookie",
+ node_name="cookie",
+ classes=(str,),
+ get_kwarg=subelement_text_kwarg,
+ set_element=text_subelement,
+ ),
+)
+registry.register(
+ NetworkLinkControl,
+ RegistryItem(
+ ns_ids=("kml",),
+ attr_name="message",
+ node_name="message",
+ classes=(str,),
+ get_kwarg=subelement_text_kwarg,
+ set_element=text_subelement,
+ ),
+)
+registry.register(
+ NetworkLinkControl,
+ RegistryItem(
+ ns_ids=("kml",),
+ attr_name="link_name",
+ node_name="linkName",
+ classes=(str,),
+ get_kwarg=subelement_text_kwarg,
+ set_element=text_subelement,
+ ),
+)
+registry.register(
+ NetworkLinkControl,
+ RegistryItem(
+ ns_ids=("kml",),
+ attr_name="link_description",
+ node_name="linkDescription",
+ classes=(str,),
+ get_kwarg=subelement_text_kwarg,
+ set_element=text_subelement,
+ ),
+)
+registry.register(
+ NetworkLinkControl,
+ RegistryItem(
+ ns_ids=("kml",),
+ attr_name="link_snippet",
+ node_name="linkSnippet",
+ classes=(str,),
+ get_kwarg=subelement_text_kwarg,
+ set_element=text_subelement,
+ ),
+)
+registry.register(
+ NetworkLinkControl,
+ item=RegistryItem(
+ ns_ids=("kml",),
+ classes=(KmlDateTime,),
+ attr_name="expires",
+ node_name="expires",
+ get_kwarg=datetime_subelement_kwarg,
+ set_element=datetime_subelement,
+ ),
+)
+registry.register(
+ NetworkLinkControl,
+ RegistryItem(
+ ns_ids=("kml",),
+ attr_name="view",
+ node_name="Camera,LookAt",
+ classes=(
+ Camera,
+ LookAt,
+ ),
+ get_kwarg=xml_subelement_kwarg,
+ set_element=xml_subelement,
+ ),
+)
diff --git a/tests/hypothesis/network_link_control_test.py b/tests/hypothesis/network_link_control_test.py
new file mode 100644
index 00000000..3ff1d963
--- /dev/null
+++ b/tests/hypothesis/network_link_control_test.py
@@ -0,0 +1,122 @@
+# Copyright (C) 2024 Christian Ledermann
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+"""Hypothesis tests for the fastkml.network_link_control module."""
+
+import typing
+
+from hypothesis import given
+from hypothesis import strategies as st
+
+import fastkml
+import fastkml.enums
+import fastkml.model
+import fastkml.views
+from tests.base import Lxml
+from tests.hypothesis.common import assert_repr_roundtrip
+from tests.hypothesis.common import assert_str_roundtrip
+from tests.hypothesis.common import assert_str_roundtrip_terse
+from tests.hypothesis.common import assert_str_roundtrip_verbose
+from tests.hypothesis.strategies import kml_datetimes
+from tests.hypothesis.strategies import xml_text
+
+
+class TestLxml(Lxml):
+ @given(
+ min_refresh_period=st.one_of(
+ st.none(),
+ st.floats(allow_nan=False, allow_infinity=False).filter(lambda x: x != 0),
+ ),
+ max_session_length=st.one_of(
+ st.none(),
+ st.floats(allow_nan=False, allow_infinity=False).filter(lambda x: x != -1),
+ ),
+ cookie=st.one_of(st.none(), xml_text()),
+ message=st.one_of(st.none(), xml_text()),
+ link_name=st.one_of(st.none(), xml_text()),
+ link_description=st.one_of(st.none(), xml_text()),
+ link_snippet=st.one_of(st.none(), xml_text()),
+ expires=st.one_of(
+ st.none(),
+ kml_datetimes(),
+ ),
+ view=st.one_of(
+ st.none(),
+ st.builds(
+ fastkml.views.Camera,
+ longitude=st.floats(
+ allow_nan=False,
+ allow_infinity=False,
+ min_value=-180,
+ max_value=180,
+ ).filter(lambda x: x != 0),
+ latitude=st.floats(
+ allow_nan=False,
+ allow_infinity=False,
+ min_value=-90,
+ max_value=90,
+ ).filter(lambda x: x != 0),
+ altitude=st.floats(allow_nan=False, allow_infinity=False).filter(
+ lambda x: x != 0,
+ ),
+ ),
+ st.builds(
+ fastkml.views.LookAt,
+ longitude=st.floats(
+ allow_nan=False,
+ allow_infinity=False,
+ min_value=-180,
+ max_value=180,
+ ).filter(lambda x: x != 0),
+ latitude=st.floats(
+ allow_nan=False,
+ allow_infinity=False,
+ min_value=-90,
+ max_value=90,
+ ).filter(lambda x: x != 0),
+ altitude=st.floats(allow_nan=False, allow_infinity=False).filter(
+ lambda x: x != 0,
+ ),
+ ),
+ ),
+ )
+ def test_fuzz_network_link_control(
+ self,
+ min_refresh_period: typing.Optional[float],
+ max_session_length: typing.Optional[float],
+ cookie: typing.Optional[str],
+ message: typing.Optional[str],
+ link_name: typing.Optional[str],
+ link_description: typing.Optional[str],
+ link_snippet: typing.Optional[str],
+ expires: typing.Optional[fastkml.KmlDateTime],
+ view: typing.Union[fastkml.Camera, fastkml.LookAt, None],
+ ) -> None:
+ nlc = fastkml.NetworkLinkControl(
+ min_refresh_period=min_refresh_period,
+ max_session_length=max_session_length,
+ cookie=cookie,
+ message=message,
+ link_name=link_name,
+ link_description=link_description,
+ link_snippet=link_snippet,
+ expires=expires,
+ view=view,
+ )
+
+ assert_repr_roundtrip(nlc)
+ assert_str_roundtrip(nlc)
+ assert_str_roundtrip_terse(nlc)
+ assert_str_roundtrip_verbose(nlc)
diff --git a/tests/network_link_control_test.py b/tests/network_link_control_test.py
new file mode 100644
index 00000000..5cfa0ac4
--- /dev/null
+++ b/tests/network_link_control_test.py
@@ -0,0 +1,81 @@
+# Copyright (C) 2021 - 2022 Christian Ledermann
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+"""Test the Network Link Control classes."""
+
+import datetime
+
+from dateutil.tz import tzutc
+
+from fastkml import views
+from fastkml.network_link_control import NetworkLinkControl
+from fastkml.times import KmlDateTime
+from tests.base import StdLibrary
+
+
+class TestStdLibrary(StdLibrary):
+ """Test with the standard library."""
+
+ def test_network_link_control_obj(self) -> None:
+ dt = datetime.datetime.now(tz=tzutc())
+ kml_datetime = KmlDateTime(dt=dt)
+ view = views.Camera()
+
+ network_control_obj = NetworkLinkControl(
+ min_refresh_period=1.1,
+ max_session_length=100.1,
+ cookie="cookie",
+ message="message",
+ link_name="link_name",
+ link_description="link_description",
+ link_snippet="link_snippet",
+ expires=kml_datetime,
+ view=view,
+ )
+
+ assert network_control_obj.min_refresh_period == 1.1
+ assert network_control_obj.max_session_length == 100.1
+ assert network_control_obj.cookie == "cookie"
+ assert network_control_obj.message == "message"
+ assert network_control_obj.link_name == "link_name"
+ assert network_control_obj.link_description == "link_description"
+ assert network_control_obj.link_snippet == "link_snippet"
+ assert str(network_control_obj.expires) == str(kml_datetime)
+ assert str(network_control_obj.view) == str(view)
+
+ def test_network_link_control_kml(self) -> None:
+ doc = (
+ ''
+ "432000"
+ "-1"
+ "A Snippet"
+ "2008-05-30"
+ ""
+ )
+
+ nc = NetworkLinkControl.from_string(doc)
+
+ dt = datetime.date(2008, 5, 30)
+ kml_datetime = KmlDateTime(dt=dt)
+
+ nc_obj = NetworkLinkControl(
+ min_refresh_period=432000,
+ max_session_length=-1,
+ link_snippet="A Snippet",
+ expires=kml_datetime,
+ )
+
+ assert nc == nc_obj