Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Network LInk Control #392

Merged
16 changes: 16 additions & 0 deletions docs/network.kml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:xal="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"
hint="A processing hint for a KML consumer">
<NetworkLinkControl>
<minRefreshPeriod>43200</minRefreshPeriod>
<maxSessionLength>-1</maxSessionLength>
<linkSnippet>
<![CDATA[
<p xmlns="http://www.w3.org/1999/xhtml">A snippet of <a href="http://www.w3.org/TR/xhtml-basic/">XHTML</a></p>
]]>
</linkSnippet>
<expires>2008-05-30</expires>
</NetworkLinkControl>
</kml>
2 changes: 2 additions & 0 deletions fastkml/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -119,6 +120,7 @@
"Model",
"MultiGeometry",
"NetworkLink",
"NetworkLinkControl",
"Orientation",
"OuterBoundaryIs",
"OverlayXY",
Expand Down
27 changes: 23 additions & 4 deletions fastkml/kml.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
from fastkml import validator
from fastkml.base import _XMLObject
from fastkml.containers import Document
from fastkml.network_link_control import NetworkLinkControl
from fastkml.containers import Folder
from fastkml.enums import Verbosity
from fastkml.features import NetworkLink
Expand All @@ -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(
Expand Down Expand Up @@ -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,
Expand Down
219 changes: 219 additions & 0 deletions fastkml/network_link_control.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
# Copyright (C) 2012-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
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 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):

_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] # TODO: Add Update field to the parameters

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:
super().__init__(
ns=ns,
name_spaces=name_spaces,
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,
**kwargs,
)

def __repr__(self) -> str:
"""
Return a string representation of the NetworkLinkControl object.

Returns
-------
str: A string representation of the NetworkLinkControl object.

"""
return (

Check warning on line 98 in fastkml/network_link_control.py

View check run for this annotation

Codecov / codecov/patch

fastkml/network_link_control.py#L98

Added line #L98 was not covered by tests
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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider introducing helper functions to abstract common registry field patterns.

The registration code can be simplified by introducing helper functions for common field types while maintaining the same functionality. Here's how:

def register_string_field(cls, attr_name: str, node_name: str):
    registry.register(
        cls,
        RegistryItem(
            ns_ids=("kml",),
            attr_name=attr_name,
            node_name=node_name,
            classes=(str,),
            get_kwarg=subelement_text_kwarg,
            set_element=text_subelement,
        ),
    )

def register_float_field(cls, attr_name: str, node_name: str):
    registry.register(
        cls,
        RegistryItem(
            ns_ids=("kml",),
            attr_name=attr_name,
            node_name=node_name,
            classes=(float,),
            get_kwarg=subelement_float_kwarg,
            set_element=float_subelement,
        ),
    )

# Usage:
register_float_field(NetworkLinkControl, "min_refresh_period", "minRefreshPeriod")
register_float_field(NetworkLinkControl, "max_session_length", "maxSessionLength")
register_string_field(NetworkLinkControl, "cookie", "cookie")
register_string_field(NetworkLinkControl, "message", "message")
register_string_field(NetworkLinkControl, "link_name", "linkName")

This approach:

  1. Reduces duplication while keeping the code explicit
  2. Makes it easier to maintain consistent registration patterns
  3. Reduces the chance of copy-paste errors
  4. Still allows custom registration for special cases (like the view field)

NetworkLinkControl,
RegistryItem(
ns_ids=("kml",),
attr_name="min_refresh_period",
node_name="minRefreshPeriod",
classes=(float,),
get_kwarg=subelement_float_kwarg,
set_element=float_subelement,
cleder marked this conversation as resolved.
Show resolved Hide resolved
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,
),
cleder marked this conversation as resolved.
Show resolved Hide resolved
)
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,
),
)
79 changes: 79 additions & 0 deletions tests/network_link_control_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# 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 fastkml.network_link_control import NetworkLinkControl
from fastkml.times import KmlDateTime
from tests.base import StdLibrary
from fastkml import views


class TestStdLibrary(StdLibrary):
"""Test with the standard library."""

def test_network_link_control_obj(self) -> None:
dt = datetime.datetime.now()
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 = (
'<kml:NetworkLinkControl xmlns:kml="http://www.opengis.net/kml/2.2">'
"<kml:minRefreshPeriod>432000</kml:minRefreshPeriod>"
"<kml:maxSessionLength>-1</kml:maxSessionLength>"
"<kml:linkSnippet>A Snippet</kml:linkSnippet>"
"<kml:expires>2008-05-30</kml:expires>"
"</kml:NetworkLinkControl>"
)

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,
)

cleder marked this conversation as resolved.
Show resolved Hide resolved
assert nc == nc_obj
Loading