Skip to content

Commit

Permalink
Add NetworkLink, improve handling of time span and stamp
Browse files Browse the repository at this point in the history
  • Loading branch information
cleder committed Nov 22, 2023
1 parent d9ed701 commit c0337ec
Show file tree
Hide file tree
Showing 11 changed files with 760 additions and 670 deletions.
156 changes: 89 additions & 67 deletions fastkml/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"""

import logging
from datetime import datetime
from typing import Any
from typing import Dict
from typing import Iterator
Expand Down Expand Up @@ -131,14 +130,7 @@ class _Feature(TimeMixin, _BaseObject):
# Placemark, or ScreenOverlay—the value for the Feature's inline
# style takes precedence over the value for the shared style.

_timespan: Optional[TimeSpan]
# Associates this Feature with a period of time.
_timestamp: Optional[TimeStamp]
# Associates this Feature with a point in time.

_camera: Optional[Camera]

_look_at: Optional[LookAt]
_view: Union[Camera, LookAt, None]

# TODO Region = None
# Features and geometry associated with a Region are drawn only when
Expand Down Expand Up @@ -173,18 +165,17 @@ def __init__(
styles: Optional[List[Style]] = None,
style_url: Optional[str] = None,
extended_data: None = None,
camera: Optional[Camera] = None,
look_at: Optional[LookAt] = None,
view: Optional[Union[Camera, LookAt]] = None,
address: Optional[str] = None,
phone_number: Optional[str] = None,
times: Optional[Union[TimeSpan, TimeStamp]] = None,
) -> None:
super().__init__(ns=ns, name_spaces=name_spaces, id=id, target_id=target_id)
self.name = name
self.description = description
self.style_url = style_url
self._styles = []
self._camera = camera
self._look_at = look_at
self._view = view
self.visibility = visibility
self.isopen = isopen
self.snippet = snippet
Expand All @@ -196,6 +187,7 @@ def __init__(
for style in styles:
self.append_style(style)
self.extended_data = extended_data
self._times = times

@property
def style_url(self) -> Optional[str]:
Expand All @@ -221,22 +213,12 @@ def style_url(self, styleurl: Union[str, StyleUrl, None]) -> None:
raise ValueError

@property
def camera(self):
return self._camera

@camera.setter
def camera(self, camera) -> None:
if isinstance(camera, Camera):
self._camera = camera

@property
def look_at(self) -> datetime:
return self._look_at
def view(self) -> Optional[Union[Camera, LookAt]]:
return self._view

@look_at.setter
def look_at(self, look_at) -> None:
if isinstance(look_at, LookAt):
self._look_at = look_at
@view.setter
def view(self, camera: Optional[Union[Camera, LookAt]]) -> None:
self._view = camera

@property
def link(self):
Expand Down Expand Up @@ -331,34 +313,20 @@ def snippet(self, snip=None) -> None:
)

@property
def address(self) -> None:
if self._address:
return self._address
return None
def address(self) -> Optional[str]:
return self._address

@address.setter
def address(self, address) -> None:
if isinstance(address, str):
self._address = address
elif address is None:
self._address = None
else:
raise ValueError
def address(self, address: Optional[str]) -> None:
self._address = address

@property
def phone_number(self) -> None:
if self._phone_number:
return self._phone_number
return None
def phone_number(self) -> Optional[str]:
return self._phone_number

@phone_number.setter
def phone_number(self, phone_number) -> None:
if isinstance(phone_number, str):
self._phone_number = phone_number
elif phone_number is None:
self._phone_number = None
else:
raise ValueError
def phone_number(self, phone_number: Optional[str]) -> None:
self._phone_number = phone_number

def etree_element(
self,
Expand All @@ -372,13 +340,8 @@ def etree_element(
if self.description:
description = config.etree.SubElement(element, f"{self.ns}description")
description.text = self.description
if (self.camera is not None) and (self.look_at is not None):
msg = "Either Camera or LookAt can be defined, not both"
raise ValueError(msg)
if self.camera is not None:
element.append(self._camera.etree_element())
elif self.look_at is not None:
element.append(self._look_at.etree_element())
if self._view is not None:
element.append(self._view.etree_element())
if self.visibility is not None:
visibility = config.etree.SubElement(element, f"{self.ns}visibility")
visibility.text = str(self.visibility)
Expand All @@ -398,13 +361,8 @@ def etree_element(
snippet.text = self.snippet["text"]
if self.snippet.get("maxLines"):
snippet.set("maxLines", str(self.snippet["maxLines"]))
if (self._timespan is not None) and (self._timestamp is not None):
msg = "Either Timestamp or Timespan can be defined, not both"
raise ValueError(msg)
elif self._timespan is not None:
element.append(self._timespan.etree_element())
elif self._timestamp is not None:
element.append(self._timestamp.etree_element())
elif self._times is not None:
element.append(self._times.etree_element())
if self._atom_link is not None:
element.append(self._atom_link.etree_element())
if self._atom_author is not None:
Expand Down Expand Up @@ -512,9 +470,18 @@ def from_element(self, element: Element, strict: bool = False) -> None:
self.phone_number = phone_number.text
camera = element.find(f"{self.ns}Camera")
if camera is not None:
s = Camera(self.ns)
s.from_element(camera)
self.camera = s
self._view = Camera.class_from_element(
ns=self.ns,
element=camera,
strict=strict,
)
lookat = element.find(f"{self.ns}LookAt")
if lookat is not None:
self._view = LookAt.class_from_element(
ns=self.ns,
element=lookat,
strict=strict,
)


class Placemark(_Feature):
Expand Down Expand Up @@ -632,3 +599,58 @@ def etree_element(
else:
logger.error("Object does not have a geometry")
return element


class NetworkLink(_Feature):
__name__ = "NetworkLink"
_nlink = None

def __init__(
self,
ns=None,
id=None,
name=None,
description=None,
styles=None,
styleUrl=None,
):
super().__init__(ns, id, name, description, styles, styleUrl)

@property
def link(self):
return self._nlink.href

@link.setter
def link(self, url):
if isinstance(url, basestring):
self._nlink = atom.Link(href=url)
elif isinstance(url, Link):
self._nlink = url
elif url is None:
self._nlink = None
else:
raise TypeError

def etree_element(self):
element = super().etree_element()
if self._nlink is not None:
element.append(self._nlink.etree_element())
return element

def from_element(self, element):
super(_Feature, self).from_element(element)
name = element.find(f"{self.ns}name")
if name is not None:
self.name = name.text
id = element.find(f"{self.ns}id")
if id is not None:
self.id = id.text
visibility = element.find(f"{self.ns}visibility")
if visibility is not None:
self.visibility = visibility.text

link = element.find(f"{self.ns}Link")
if link is not None:
s = Link()
s.from_element(link)
self._nlink = s
46 changes: 5 additions & 41 deletions fastkml/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"""Mixins for the KML classes."""
import logging
from typing import Optional
from typing import Union

from fastkml.times import KmlDateTime
from fastkml.times import TimeSpan
Expand All @@ -25,54 +26,17 @@


class TimeMixin:
_timespan: Optional[TimeSpan] = None
_timestamp: Optional[TimeStamp] = None
_times: Optional[Union[TimeSpan, TimeStamp]] = None

@property
def time_stamp(self) -> Optional[KmlDateTime]:
"""This just returns the datetime portion of the timestamp."""
return self._timestamp.timestamp if self._timestamp is not None else None

@time_stamp.setter
def time_stamp(self, timestamp: Optional[KmlDateTime]) -> None:
if self._timestamp is None:
self._timestamp = TimeStamp(timestamp=timestamp)
elif timestamp is None:
self._timestamp = None
else:
self._timestamp.timestamp = timestamp
if self._timespan and self._timestamp:
logger.warning("Setting a TimeStamp, TimeSpan deleted")
self._timespan = None
return self._times.timestamp if isinstance(self._times, TimeStamp) else None

@property
def begin(self) -> Optional[KmlDateTime]:
return self._timespan.begin if self._timespan is not None else None

@begin.setter
def begin(self, dt: Optional[KmlDateTime]) -> None:
if self._timespan is None:
self._timespan = TimeSpan(begin=dt)
elif dt is None and self._timespan.end is None:
self._timespan = None
else:
self._timespan.begin = dt
if self._timespan and self._timestamp:
logger.warning("Setting a TimeSpan, TimeStamp deleted")
self._timestamp = None
return self._times.begin if isinstance(self._times, TimeSpan) else None

@property
def end(self) -> Optional[KmlDateTime]:
return self._timespan.end if self._timespan is not None else None

@end.setter
def end(self, dt: Optional[KmlDateTime]) -> None:
if self._timespan is None:
self._timespan = TimeSpan(end=dt)
elif dt is None and self._timespan.begin is None:
self._timespan = None
else:
self._timespan.end = dt
if self._timespan and self._timestamp:
logger.warning("Setting a TimeSpan, TimeStamp deleted")
self._timestamp = None
return self._times.end if isinstance(self._times, TimeSpan) else None
32 changes: 30 additions & 2 deletions fastkml/overlays.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
"""

import logging
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from typing import Union

from fastkml import atom
from fastkml import config
from fastkml import gx
from fastkml.enums import GridOrigin
Expand All @@ -19,7 +22,12 @@
from fastkml.geometry import Point
from fastkml.geometry import Polygon
from fastkml.links import Icon
from fastkml.styles import Style
from fastkml.times import TimeSpan
from fastkml.times import TimeStamp
from fastkml.types import Element
from fastkml.views import Camera
from fastkml.views import LookAt

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -70,9 +78,19 @@ def __init__(
target_id: Optional[str] = None,
name: Optional[str] = None,
description: Optional[str] = None,
color: Optional[str] = None,
styles: None = None,
snippet: Optional[Union[str, Dict[str, Any]]] = None,
atom_author: Optional[atom.Author] = None,
atom_link: Optional[atom.Link] = None,
visibility: Optional[bool] = None,
isopen: Optional[bool] = None,
styles: Optional[List[Style]] = None,
style_url: Optional[str] = None,
extended_data: None = None,
view: Optional[Union[Camera, LookAt]] = None,
address: Optional[str] = None,
phone_number: Optional[str] = None,
times: Optional[Union[TimeSpan, TimeStamp]] = None,
color: Optional[str] = None,
draw_order: Optional[str] = None,
icon: Optional[Icon] = None,
) -> None:
Expand All @@ -85,6 +103,16 @@ def __init__(
description=description,
styles=styles,
style_url=style_url,
snippet=snippet,
atom_author=atom_author,
atom_link=atom_link,
visibility=visibility,
isopen=isopen,
extended_data=extended_data,
view=view,
address=address,
phone_number=phone_number,
times=times,
)
self._icon = icon
self._color = color
Expand Down
Loading

0 comments on commit c0337ec

Please sign in to comment.