Skip to content

Commit

Permalink
use xml objects and registry for simple and schema data
Browse files Browse the repository at this point in the history
  • Loading branch information
cleder committed Mar 16, 2024
1 parent 7a7c498 commit ba1d68a
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 167 deletions.
281 changes: 150 additions & 131 deletions fastkml/data.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2022 - 2023 Christian Ledermann
# Copyright (C) 2022 - 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
Expand All @@ -19,8 +19,6 @@
https://developers.google.com/kml/documentation/extendeddata#example
"""
import logging
from dataclasses import dataclass
from typing import Any
from typing import Dict
from typing import Iterable
from typing import List
Expand All @@ -31,17 +29,19 @@
from fastkml.base import _BaseObject
from fastkml.base import _XMLObject
from fastkml.enums import DataType
from fastkml.enums import Verbosity
from fastkml.exceptions import KMLSchemaError
from fastkml.helpers import attribute_enum_kwarg
from fastkml.helpers import attribute_text_kwarg
from fastkml.helpers import enum_attribute
from fastkml.helpers import node_text
from fastkml.helpers import node_text_kwarg
from fastkml.helpers import subelement_text_kwarg
from fastkml.helpers import text_attribute
from fastkml.helpers import text_subelement
from fastkml.helpers import xml_subelement_list
from fastkml.helpers import xml_subelement_list_kwarg
from fastkml.registry import RegistryItem
from fastkml.registry import registry
from fastkml.types import Element

__all__ = [
"Data",
Expand All @@ -54,8 +54,7 @@
logger = logging.getLogger(__name__)


@dataclass(frozen=True)
class SimpleField:
class SimpleField(_BaseObject):
"""
A SimpleField always has both name and type attributes.
Expand All @@ -78,14 +77,61 @@ class SimpleField:
HTML markup.
"""

name: str
type: DataType
display_name: Optional[str] = None
name: Optional[str]
type: Optional[DataType]
display_name: Optional[str]

def __init__(
self,
ns: Optional[str] = None,
name_spaces: Optional[Dict[str, str]] = None,
id: Optional[str] = None,
target_id: Optional[str] = None,
name: Optional[str] = None,
type: Optional[DataType] = None,
display_name: Optional[str] = None,
) -> None:
super().__init__(ns=ns, name_spaces=name_spaces, id=id, target_id=target_id)
self.name = name
self.type = type
self.display_name = display_name

def __bool__(self) -> bool:
return bool(self.name) and bool(self.type)


registry.register(
SimpleField,
RegistryItem(
attr_name="name",
node_name="name",
classes=(str,),
get_kwarg=attribute_text_kwarg,
set_element=text_attribute,
),
)
registry.register(
SimpleField,
RegistryItem(
attr_name="type",
node_name="type",
classes=(DataType,),
get_kwarg=attribute_enum_kwarg,
set_element=enum_attribute,
),
)
registry.register(
SimpleField,
RegistryItem(
attr_name="display_name",
node_name="displayName",
classes=(str,),
get_kwarg=subelement_text_kwarg,
set_element=text_subelement,
),
)


class Schema(_BaseObject):
"""
Specifies a custom KML schema that is used to add custom data to KML Features.
Expand All @@ -97,6 +143,9 @@ class Schema(_BaseObject):
"""

name: Optional[str]
fields: List[SimpleField]

def __init__(
self,
ns: Optional[str] = None,
Expand All @@ -111,86 +160,36 @@ def __init__(
raise KMLSchemaError(msg)
super().__init__(ns=ns, name_spaces=name_spaces, id=id, target_id=target_id)
self.name = name
self.simple_fields = list(fields) if fields else []
self.fields = list(fields) if fields else []

def append(self, field: SimpleField) -> None:
"""Append a field."""
self.simple_fields.append(field)
self.fields.append(field)

def etree_element(
self,
precision: Optional[int] = None,
verbosity: Verbosity = Verbosity.normal,
) -> Element:
element = super().etree_element(precision=precision, verbosity=verbosity)
if self.name:
element.set("name", self.name)
for simple_field in self.simple_fields:
if not simple_field:
continue
sf = config.etree.SubElement( # type: ignore[attr-defined]
element,
f"{self.ns}SimpleField",
)
sf.set("type", simple_field.type.value)
sf.set("name", simple_field.name)
if simple_field.display_name:
dn = config.etree.SubElement( # type: ignore[attr-defined]
sf,
f"{self.ns}displayName",
)
dn.text = simple_field.display_name
return element

@classmethod
def _get_fields_kwargs_from_element(
cls,
*,
ns: str,
name_spaces: Optional[Dict[str, str]] = None,
element: Element,
strict: bool,
) -> List[SimpleField]:
def get_display_name(field: Element) -> Optional[str]:
display_name = field.find(f"{ns}displayName")
return display_name.text if display_name is not None else None

return [
SimpleField(
name=field.get("name"),
type=DataType(field.get("type")),
display_name=get_display_name(field),
)
for field in element.findall(f"{ns}SimpleField")
if field is not None
]

@classmethod
def _get_kwargs(
cls,
*,
ns: str,
name_spaces: Optional[Dict[str, str]] = None,
element: Element,
strict: bool,
) -> Dict[str, Any]:
kwargs = super()._get_kwargs(
ns=ns,
name_spaces=name_spaces,
element=element,
strict=strict,
)
kwargs["name"] = element.get("name")
kwargs["fields"] = cls._get_fields_kwargs_from_element(
ns=ns,
name_spaces=name_spaces,
element=element,
strict=strict,
)
return kwargs


class Data(_XMLObject):

registry.register(
Schema,
RegistryItem(
attr_name="name",
node_name="name",
classes=(str,),
get_kwarg=attribute_text_kwarg,
set_element=text_attribute,
),
)
registry.register(
Schema,
RegistryItem(
attr_name="fields",
node_name="SimpleField",
classes=(SimpleField,),
get_kwarg=xml_subelement_list_kwarg,
set_element=xml_subelement_list,
),
)


class Data(_BaseObject):
"""Represents an untyped name/value pair with optional display name."""

_default_ns = config.KMLNS
Expand All @@ -203,12 +202,13 @@ def __init__(
self,
ns: Optional[str] = None,
name_spaces: Optional[Dict[str, str]] = None,
id: Optional[str] = None,
target_id: Optional[str] = None,
name: Optional[str] = None,
value: Optional[str] = None,
display_name: Optional[str] = None,
) -> None:
super().__init__(ns=ns, name_spaces=name_spaces)

super().__init__(ns=ns, name_spaces=name_spaces, id=id, target_id=target_id)
self.name = name
self.value = value
self.display_name = display_name
Expand Down Expand Up @@ -250,10 +250,47 @@ def __bool__(self) -> bool:
)


@dataclass(frozen=True)
class SimpleData:
name: str
value: Union[int, str, float, bool]
class SimpleData(_BaseObject):
name: Optional[str]
value: Optional[Union[int, str, float, bool]]

def __init__(
self,
ns: Optional[str] = None,
name_spaces: Optional[Dict[str, str]] = None,
id: Optional[str] = None,
target_id: Optional[str] = None,
name: Optional[str] = None,
value: Optional[Union[int, str, float, bool]] = None,
) -> None:
super().__init__(ns=ns, name_spaces=name_spaces, id=id, target_id=target_id)
self.name = name
self.value = value

def __bool__(self) -> bool:
return bool(self.name) and self.value is not None


registry.register(
SimpleData,
RegistryItem(
attr_name="value",
node_name="value",
classes=(str,),
get_kwarg=node_text_kwarg,
set_element=node_text,
),
)
registry.register(
SimpleData,
RegistryItem(
attr_name="name",
node_name="name",
classes=(str,),
get_kwarg=attribute_text_kwarg,
set_element=text_attribute,
),
)


class SchemaData(_XMLObject):
Expand Down Expand Up @@ -291,45 +328,27 @@ def __bool__(self) -> bool:
def append_data(self, data: SimpleData) -> None:
self.data.append(data)

def etree_element(
self,
precision: Optional[int] = None,
verbosity: Verbosity = Verbosity.normal,
) -> Element:
element = super().etree_element(precision=precision, verbosity=verbosity)
if self.schema_url:
element.set("schemaUrl", self.schema_url)
for data in self.data:
sd = config.etree.SubElement( # type: ignore[attr-defined]
element,
f"{self.ns}SimpleData",
)
sd.set("name", data.name)
sd.text = data.value
return element

@classmethod
def _get_kwargs(
cls,
*,
ns: str,
name_spaces: Optional[Dict[str, str]] = None,
element: Element,
strict: bool,
) -> Dict[str, Any]:
kwargs = super()._get_kwargs(
ns=ns,
name_spaces=name_spaces,
element=element,
strict=strict,
)
kwargs["schema_url"] = element.get("schemaUrl")
kwargs["data"] = [
SimpleData(name=sd.get("name"), value=sd.text)
for sd in element.findall(f"{ns}SimpleData")
if sd is not None
]
return kwargs

registry.register(
SchemaData,
RegistryItem(
attr_name="schema_url",
node_name="schemaUrl",
classes=(str,),
get_kwarg=attribute_text_kwarg,
set_element=text_attribute,
),
)
registry.register(
SchemaData,
RegistryItem(
attr_name="data",
node_name="SimpleData",
classes=(SimpleData,),
get_kwarg=xml_subelement_list_kwarg,
set_element=xml_subelement_list,
),
)


class ExtendedData(_XMLObject):
Expand Down

0 comments on commit ba1d68a

Please sign in to comment.