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

274 refactor kmlpy #279

Merged
merged 23 commits into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
568b643
Refactor code
cleder Nov 20, 2023
37c914c
WIP move Icon into links.py
cleder Nov 21, 2023
84103d9
fix visibility and tests
cleder Nov 21, 2023
3f3b367
Refactor Icon class to use classmethod for element parsing
cleder Nov 21, 2023
d9ed701
Split features, containers, overlays from kml.py
cleder Nov 22, 2023
c0337ec
Add NetworkLink, improve handling of time span and stamp
cleder Nov 22, 2023
773c45a
Add Region class to fastkml.views.py #20 #212
cleder Nov 22, 2023
42e5061
implement Region, Link and NetworkLink fixes #212 closes #20
cleder Nov 23, 2023
262050a
LatLonBox and Lod inherit from _XMLObject
cleder Nov 23, 2023
2b64050
make Snippet s dataclass
cleder Nov 23, 2023
4c29a81
take and return atom author and link rather than strings
cleder Nov 23, 2023
0cf42fa
update type hints
cleder Nov 23, 2023
b9767c0
fix type annotations for container
cleder Nov 23, 2023
a9bd4aa
refactor overlays
cleder Nov 23, 2023
6877c42
fix super call
cleder Nov 23, 2023
40d3506
Refactor _get_kwargs method in containers.py and overlays.py
cleder Nov 24, 2023
4909d9d
Refactor _get_kwargs method in containers.py and overlays.py
cleder Nov 24, 2023
9bd8afc
remove from_element in feattures, containers, overlays
cleder Nov 24, 2023
283bf25
Old style type annotation for python 3.8
cleder Nov 24, 2023
6f0cb52
fix parsing for KML.class_from_string
cleder Nov 25, 2023
1aa798a
remove KML.from_string
cleder Nov 25, 2023
07f9a76
Fix type hints and import statements
cleder Nov 25, 2023
ed7f69e
Update import statements in fastkml/__init__.py
cleder Nov 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/run-all-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# run tests and lint with a variety of Python versions
---
name: Tests
on: [push, pull_request]

Check warning on line 5 in .github/workflows/run-all-tests.yml

View workflow job for this annotation

GitHub Actions / static-tests (3.12)

5:1 [truthy] truthy value should be one of [false, true]

Check warning on line 5 in .github/workflows/run-all-tests.yml

View workflow job for this annotation

GitHub Actions / static-tests (3.12)

5:1 [truthy] truthy value should be one of [false, true]

jobs:
cpython:
Expand Down Expand Up @@ -128,13 +128,13 @@
--outdir dist/
- name: Publish distribution 📦 to Test PyPI for tags
if: startsWith(github.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@master
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
repository_url: https://test.pypi.org/legacy/
- name: Publish distribution 📦 to PyPI for push to main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: pypa/gh-action-pypi-publish@master
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
...
5 changes: 5 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ repos:
rev: "v6.2.0"
hooks:
- id: rstcheck
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.7.0
hooks:
- id: mypy
additional_dependencies: [pygeoif]
# - repo: https://github.com/mgedmin/check-manifest
# rev: "0.49"
# hooks:
Expand Down
10 changes: 7 additions & 3 deletions fastkml/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,16 @@
from fastkml.atom import Author
from fastkml.atom import Contributor
from fastkml.atom import Link
from fastkml.containers import Document
from fastkml.containers import Folder
from fastkml.data import Data
from fastkml.data import ExtendedData
from fastkml.data import Schema
from fastkml.data import SchemaData
from fastkml.features import Placemark
from fastkml.kml import KML
from fastkml.kml import Document
from fastkml.kml import Folder
from fastkml.kml import Placemark
from fastkml.overlays import GroundOverlay
from fastkml.overlays import PhotoOverlay
from fastkml.styles import BalloonStyle
from fastkml.styles import IconStyle
from fastkml.styles import LabelStyle
Expand All @@ -53,11 +55,13 @@
"KML",
"Document",
"Folder",
"GroundOverlay",
"Placemark",
"TimeSpan",
"TimeStamp",
"ExtendedData",
"Data",
"PhotoOverlay",
"Schema",
"SchemaData",
"StyleUrl",
Expand Down
2 changes: 1 addition & 1 deletion fastkml/about.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

The only purpose of this module is to provide a version number for the package.
"""
__version__ = "1.0.a7"
__version__ = "1.0.a8"
274 changes: 274 additions & 0 deletions fastkml/containers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
"""Container classes for KML elements."""
import logging
import urllib.parse as urlparse
from typing import Any
from typing import Dict
from typing import Iterable
from typing import Iterator
from typing import List
from typing import Optional
from typing import Union

from fastkml import atom
from fastkml import gx
from fastkml.data import ExtendedData
from fastkml.data import Schema
from fastkml.enums import Verbosity
from fastkml.features import Placemark
from fastkml.features import Snippet
from fastkml.features import _Feature
from fastkml.geometry import LinearRing
from fastkml.geometry import LineString
from fastkml.geometry import MultiGeometry
from fastkml.geometry import Point
from fastkml.geometry import Polygon
from fastkml.styles import Style
from fastkml.styles import StyleMap
from fastkml.styles import StyleUrl
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
from fastkml.views import Region

logger = logging.getLogger(__name__)

KmlGeometry = Union[
Point,
LineString,
LinearRing,
Polygon,
MultiGeometry,
gx.MultiTrack,
gx.Track,
]


class _Container(_Feature):
"""
abstract element; do not create
A Container element holds one or more Features and allows the
creation of nested hierarchies.
subclasses are:
Document,
Folder.
"""

_features: Optional[List[_Feature]]

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,
visibility: Optional[bool] = None,
isopen: Optional[bool] = None,
atom_link: Optional[atom.Link] = None,
atom_author: Optional[atom.Author] = None,
address: Optional[str] = None,
phone_number: Optional[str] = None,
snippet: Optional[Snippet] = None,
description: Optional[str] = None,
view: Optional[Union[Camera, LookAt]] = None,
times: Optional[Union[TimeSpan, TimeStamp]] = None,
style_url: Optional[StyleUrl] = None,
styles: Optional[Iterable[Union[Style, StyleMap]]] = None,
region: Optional[Region] = None,
extended_data: Optional[ExtendedData] = None,
# Container specific
features: Optional[List[_Feature]] = None,
) -> None:
super().__init__(
ns=ns,
name_spaces=name_spaces,
id=id,
target_id=target_id,
name=name,
visibility=visibility,
isopen=isopen,
atom_link=atom_link,
atom_author=atom_author,
address=address,
phone_number=phone_number,
snippet=snippet,
description=description,
view=view,
times=times,
style_url=style_url,
styles=styles,
region=region,
extended_data=extended_data,
)
self._features = features or []

def features(self) -> Iterator[_Feature]:
"""Iterate over features."""
assert self._features is not None
yield from self._features

def etree_element(
self,
precision: Optional[int] = None,
verbosity: Verbosity = Verbosity.normal,
) -> Element:
element = super().etree_element(precision=precision, verbosity=verbosity)
for feature in self.features():
element.append(feature.etree_element())
return element

def append(self, kmlobj: _Feature) -> None:
"""Append a feature."""
if kmlobj is self:
msg = "Cannot append self"
raise ValueError(msg)

Check warning on line 126 in fastkml/containers.py

View check run for this annotation

Codecov / codecov/patch

fastkml/containers.py#L125-L126

Added lines #L125 - L126 were not covered by tests
assert self._features is not None
self._features.append(kmlobj)

@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["features"] = []
folders = element.findall(f"{ns}Folder")
kwargs["features"] += [
Folder.class_from_element(ns=ns, element=folder, strict=strict)
for folder in folders
]
placemarks = element.findall(f"{ns}Placemark")
kwargs["features"] += [
Placemark.class_from_element(ns=ns, element=placemark, strict=strict)
for placemark in placemarks
]
documents = element.findall(f"{ns}Document")
kwargs["features"] += [
Document.class_from_element(ns=ns, element=document, strict=strict)
for document in documents
]
return kwargs


class Folder(_Container):
"""
A Folder is used to arrange other Features hierarchically
(Folders, Placemarks, #NetworkLinks, or #Overlays).
"""

__name__ = "Folder"


class Document(_Container):
"""
A Document is a container for features and styles. This element is
required if your KML file uses shared styles or schemata for typed
extended data.
"""

__name__ = "Document"
_schemata: Optional[List[Schema]]

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,
visibility: Optional[bool] = None,
isopen: Optional[bool] = None,
atom_link: Optional[atom.Link] = None,
atom_author: Optional[atom.Author] = None,
address: Optional[str] = None,
phone_number: Optional[str] = None,
snippet: Optional[Snippet] = None,
description: Optional[str] = None,
view: Optional[Union[Camera, LookAt]] = None,
times: Optional[Union[TimeSpan, TimeStamp]] = None,
style_url: Optional[StyleUrl] = None,
styles: Optional[Iterable[Union[Style, StyleMap]]] = None,
region: Optional[Region] = None,
extended_data: Optional[ExtendedData] = None,
features: Optional[List[_Feature]] = None,
schemata: Optional[Iterable[Schema]] = None,
) -> None:
super().__init__(
ns=ns,
name_spaces=name_spaces,
id=id,
target_id=target_id,
name=name,
visibility=visibility,
isopen=isopen,
atom_link=atom_link,
atom_author=atom_author,
address=address,
phone_number=phone_number,
snippet=snippet,
description=description,
view=view,
times=times,
style_url=style_url,
styles=styles,
region=region,
extended_data=extended_data,
features=features,
)
self._schemata = list(schemata) if schemata else []

def schemata(self) -> Iterator[Schema]:
if self._schemata:
yield from self._schemata

def append_schema(self, schema: Schema) -> None:
assert self._schemata is not None
self._schemata.append(schema)

Check warning on line 237 in fastkml/containers.py

View check run for this annotation

Codecov / codecov/patch

fastkml/containers.py#L236-L237

Added lines #L236 - L237 were not covered by tests

def etree_element(
self,
precision: Optional[int] = None,
verbosity: Verbosity = Verbosity.normal,
) -> Element:
element = super().etree_element(precision=precision, verbosity=verbosity)
if self._schemata is not None:
for schema in self._schemata:
element.append(schema.etree_element())
return element

def get_style_by_url(self, style_url: str) -> Optional[Union[Style, StyleMap]]:
id_ = urlparse.urlparse(style_url).fragment
return next((style for style in self.styles() if style.id == id_), 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,
)
schemata = element.findall(f"{ns}Schema")
kwargs["schemata"] = [
Schema.class_from_element(ns=ns, element=schema, strict=strict)
for schema in schemata
]
return kwargs
Loading
Loading