Commit 365ed6e5 authored by Christian Ledermann's avatar Christian Ledermann

Merge pull request #13 from IanLee1521/pep8

Pep8
parents 9eac7e71 d8d77db0
......@@ -17,7 +17,8 @@ install:
# command to run tests, e.g. python setup.py test
script:
coverage run --source=fastkml setup.py test
- pep8 --exclude test_main.py fastkml
- coverage run --source=fastkml setup.py test
after_success:
coveralls
......
......@@ -5,4 +5,4 @@ Contributors
- Jeremy Blalock
- Denis Krienbühl
- Egil Möller
- Ian Lee
- Ian Lee <IanLee1521@gmail.com>
......@@ -6,6 +6,8 @@ Changelog
----------------
- test case additions and lxml warning [Ian Lee]
- pep8-ify source code (except test_main.py) [Ian Lee]
- pyflakes-ify source code (except __init__.py) [Ian Lee]
0.6 (2014/05/29)
----------------
......
# -*- coding: utf-8 -*-
# Copyright (C) 2012 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
# Copyright (C) 2012 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
from .kml import KML, Document, Folder, Placemark
from .kml import TimeSpan, TimeStamp
......
# -*- coding: utf-8 -*-
# Copyright (C) 2012 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
# Copyright (C) 2012 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
"""
KML 2.2 supports new elements for including data about the author and
related website in your KML file. This information is displayed in geo
......@@ -82,10 +82,11 @@ class Link(object):
length = None
# the length of the resource, in bytes
def __init__(self, ns=None, href=None, rel=None, type=None,
hreflang=None, title=None, length=None):
if ns == None:
def __init__(
self, ns=None, href=None, rel=None, type=None,
hreflang=None, title=None, length=None
):
if ns is None:
self.ns = NS
else:
self.ns = ns
......@@ -99,7 +100,6 @@ class Link(object):
def from_string(self, xml_string):
self.from_element(etree.XML(xml_string))
def from_element(self, element):
if self.ns + self.__name__.lower() != element.tag:
raise TypeError
......@@ -120,7 +120,6 @@ class Link(object):
if element.get('length'):
self.length = element.get('length')
def etree_element(self):
element = etree.Element(self.ns + self.__name__.lower())
if self.href:
......@@ -142,11 +141,14 @@ class Link(object):
def to_string(self, prettyprint=True):
""" Return the ATOM Object as serialized xml """
if LXML and prettyprint:
return etree.tostring(self.etree_element(), encoding='utf-8',
pretty_print=True).decode('UTF-8')
return etree.tostring(
self.etree_element(),
encoding='utf-8',
pretty_print=True).decode('UTF-8')
else:
return etree.tostring(self.etree_element(),
encoding='utf-8').decode('UTF-8')
return etree.tostring(
self.etree_element(),
encoding='utf-8').decode('UTF-8')
class _Person(object):
......@@ -159,16 +161,16 @@ class _Person(object):
ns = None
name = None
#conveys a human-readable name for the person.
# conveys a human-readable name for the person.
uri = None
#contains a home page for the person.
# contains a home page for the person.
email = None
#contains an email address for the person.
# contains an email address for the person.
def __init__(self, ns=None, name=None, uri=None, email=None):
if ns == None:
if ns is None:
self.ns = NS
else:
self.ns = ns
......@@ -176,27 +178,24 @@ class _Person(object):
self.uri = uri
self.email = email
def etree_element(self):
element = etree.Element(self.ns + self.__name__.lower())
if self.name:
name = etree.SubElement(element, "%sname" %self.ns)
name = etree.SubElement(element, "%sname" % self.ns)
name.text = self.name
#else:
# else:
# logger.critical('No Name for person defined')
# raise TypeError
if self.uri:
#XXX validate uri
uri = etree.SubElement(element, "%suri" %self.ns)
# XXX validate uri
uri = etree.SubElement(element, "%suri" % self.ns)
uri.text = self.uri
if self.email:
if check_email(self.email):
email = etree.SubElement(element, "%semail" %self.ns)
email = etree.SubElement(element, "%semail" % self.ns)
email.text = self.email
return element
def from_string(self, xml_string):
self.from_element(etree.XML(xml_string))
......@@ -204,13 +203,13 @@ class _Person(object):
if self.ns + self.__name__.lower() != element.tag:
raise TypeError
else:
name = element.find('%sname' %self.ns)
name = element.find('%sname' % self.ns)
if name is not None:
self.name = name.text
uri = element.find('%suri' %self.ns)
uri = element.find('%suri' % self.ns)
if uri is not None:
self.uri = uri.text
email = element.find('%semail' %self.ns)
email = element.find('%semail' % self.ns)
if email is not None:
if check_email(email.text):
self.email = email.text
......@@ -218,21 +217,23 @@ class _Person(object):
def to_string(self, prettyprint=True):
""" Return the ATOM Object as serialized xml """
if LXML and prettyprint:
return etree.tostring(self.etree_element(), encoding='utf-8',
pretty_print=True).decode('UTF-8')
return etree.tostring(
self.etree_element(),
encoding='utf-8',
pretty_print=True).decode('UTF-8')
else:
return etree.tostring(self.etree_element(),
encoding='utf-8').decode('UTF-8')
return etree.tostring(
self.etree_element(),
encoding='utf-8').decode('UTF-8')
class Author(_Person):
""" Names one author of the feed/entry. A feed/entry may have
multiple authors."""
__name__ = "Author"
class Contributor(_Person):
""" Names one contributor to the feed/entry. A feed/entry may have
multiple contributor elements."""
__name__ = "Contributor"
# -*- coding: utf-8 -*-
# Copyright (C) 2012 Christian Ledermann
# Copyright (C) 2012 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 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.
# 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
# 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
""" abstract base classes"""
import fastkml.config as config
from fastkml.config import etree
class _XMLObject(object):
""" XML Baseclass"""
......@@ -27,7 +28,7 @@ class _XMLObject(object):
ns = None
def __init__(self, ns=None):
if ns == None:
if ns is None:
self.ns = config.NS
else:
self.ns = ns
......@@ -36,12 +37,16 @@ class _XMLObject(object):
if self.__name__:
element = etree.Element(self.ns + self.__name__)
else:
raise NotImplementedError("Call of abstract base class, subclasses implement this!")
raise NotImplementedError(
"Call of abstract base class, subclasses implement this!"
)
return element
def from_element(self, element):
if self.ns + self.__name__ != element.tag:
raise TypeError("Call of abstract base class, subclasses implement this!")
raise TypeError(
"Call of abstract base class, subclasses implement this!"
)
def from_string(self, xml_string):
self.from_element(etree.XML(xml_string))
......@@ -49,11 +54,15 @@ class _XMLObject(object):
def to_string(self, prettyprint=True):
""" Return the KML Object as serialized xml """
if config.LXML and prettyprint:
return etree.tostring(self.etree_element(), encoding='utf-8',
pretty_print=True).decode('UTF-8')
return etree.tostring(
self.etree_element(),
encoding='utf-8',
pretty_print=True).decode('UTF-8')
else:
return etree.tostring(self.etree_element(),
encoding='utf-8').decode('UTF-8')
return etree.tostring(
self.etree_element(),
encoding='utf-8').decode('UTF-8')
class _BaseObject(_XMLObject):
""" This is an abstract base class and cannot be used directly in a
......@@ -68,7 +77,7 @@ class _BaseObject(_XMLObject):
def __init__(self, ns=None, id=None):
super(_BaseObject, self).__init__(ns)
self.id = id
if ns == None:
if ns is None:
self.ns = config.NS
else:
self.ns = ns
......@@ -81,12 +90,9 @@ class _BaseObject(_XMLObject):
element.set('targetId', self.targetId)
return element
def from_element(self, element):
super(_BaseObject, self).from_element(element)
if element.get('id'):
self.id = element.get('id')
if element.get('targetId'):
self.targetId = element.get('targetId')
# -*- coding: utf-8 -*-
# Copyright (C) 2012 Christian Ledermann
# Copyright (C) 2012 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 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.
# 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
# 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
"""frequently used constants and abstract base classes"""
import logging
......
# -*- coding: utf-8 -*-
# Copyright (C) 2012 Christian Ledermann
# Copyright (C) 2012 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 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.
# 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
# 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 the geometries from shapely if it is installed
or otherwise from Pygeoif
......@@ -22,7 +22,7 @@ try:
from shapely.geometry import Point, LineString, Polygon
from shapely.geometry import MultiPoint, MultiLineString, MultiPolygon
from shapely.geometry.polygon import LinearRing
#from shapely.geometry import GeometryCollection
# from shapely.geometry import GeometryCollection
# Sean Gillies:
# I deliberately omitted a geometry collection constructor because
# there was almost no support in GEOS for operations on them. You
......@@ -40,10 +40,7 @@ except ImportError:
from pygeoif.geometry import GeometryCollection
from pygeoif.geometry import as_shape as asShape
try:
import fastkml.config as config
except ImportError:
import config
import fastkml.config as config
from .config import etree
......@@ -63,9 +60,10 @@ class Geometry(_BaseObject):
tessellate = False
altitude_mode = None
def __init__(self, ns=None, id=None, geometry=None, extrude=False,
tessellate=False, altitude_mode=None):
def __init__(
self, ns=None, id=None, geometry=None, extrude=False,
tessellate=False, altitude_mode=None
):
"""
geometry: a geometry that implements the __geo_interface__ convention
......@@ -115,9 +113,14 @@ class Geometry(_BaseObject):
self.tessellate = tessellate
self.altitude_mode = altitude_mode
if geometry:
if isinstance(geometry, (Point, LineString, Polygon,
MultiPoint, MultiLineString, MultiPolygon,
LinearRing, GeometryCollection)):
if isinstance(
geometry,
(
Point, LineString, Polygon,
MultiPoint, MultiLineString, MultiPolygon,
LinearRing, GeometryCollection
)
):
self.geometry = geometry
else:
self.geometry = asShape(geometry)
......@@ -126,35 +129,43 @@ class Geometry(_BaseObject):
def _set_altitude_mode(self, element):
if self.altitude_mode:
assert(self.altitude_mode in ['clampToGround',
#'relativeToSeaFloor', 'clampToSeaFloor',
'relativeToGround', 'absolute'])
assert(self.altitude_mode in [
'clampToGround',
# 'relativeToSeaFloor', 'clampToSeaFloor',
'relativeToGround', 'absolute'
])
if self.altitude_mode != 'clampToGround':
am_element = etree.SubElement(element, "%saltitudeMode" %self.ns)
am_element = etree.SubElement(
element, "%saltitudeMode" % self.ns
)
am_element.text = self.altitude_mode
def _set_extrude(self, element):
if self.extrude and self.altitude_mode in ['relativeToGround',
#'relativeToSeaFloor',
'absolute']:
et_element = etree.SubElement(element, "%sextrude" %self.ns)
if self.extrude and self.altitude_mode in [
'relativeToGround',
# 'relativeToSeaFloor',
'absolute'
]:
et_element = etree.SubElement(element, "%sextrude" % self.ns)
et_element.text = '1'
def _etree_coordinates(self, coordinates):
clampToGround = (self.altitude_mode == 'clampToGround') or (self.altitude_mode == None)
element = etree.Element("%scoordinates" %self.ns)
# clampToGround = (
# (self.altitude_mode == 'clampToGround')
# or (self.altitude_mode is None)
# )
element = etree.Element("%scoordinates" % self.ns)
if len(coordinates[0]) == 2:
if config.FORCE3D: # and not clampToGround:
if config.FORCE3D: # and not clampToGround:
tuples = ('%f,%f,0.000000' % tuple(c) for c in coordinates)
else:
tuples = ('%f,%f' % tuple(c) for c in coordinates)
elif len(coordinates[0]) == 3:
#if clampToGround:
# if clampToGround:
# if the altitude is ignored anyway, we may as well
# ignore the z-value
# tuples = ('%f,%f' % tuple(c[:2]) for c in coordinates)
#else:
# else:
tuples = ('%f,%f,%f' % tuple(c) for c in coordinates)
else:
raise ValueError("Invalid dimensions")
......@@ -162,7 +173,7 @@ class Geometry(_BaseObject):
return element
def _etree_point(self, point):
element = etree.Element("%sPoint" %self.ns)
element = etree.Element("%sPoint" % self.ns)
self._set_extrude(element)
self._set_altitude_mode(element)
coords = list(point.coords)
......@@ -170,24 +181,26 @@ class Geometry(_BaseObject):
return element
def _etree_linestring(self, linestring):
element = etree.Element("%sLineString" %self.ns)
element = etree.Element("%sLineString" % self.ns)
self._set_extrude(element)
self._set_altitude_mode(element)
if self.tessellate and self.altitude_mode in ['clampToGround',
'clampToSeaFloor']:
ts_element = etree.SubElement(element, "%stessellate" %self.ns)
if self.tessellate and self.altitude_mode in [
'clampToGround',
'clampToSeaFloor'
]:
ts_element = etree.SubElement(element, "%stessellate" % self.ns)
ts_element.text = '1'
coords = list(linestring.coords)
element.append(self._etree_coordinates(coords))
return element
def _etree_linearring(self, linearring):
element = etree.Element("%sLinearRing" %self.ns)
element = etree.Element("%sLinearRing" % self.ns)
self._set_extrude(element)
self._set_altitude_mode(element)
# tesseleation is ignored by polygon and tesselation together with
# LinearRing without a polygon very rare Edgecase -> ignore for now
#if self.tessellate and self.altitude_mode in ['clampToGround',
# if self.tessellate and self.altitude_mode in ['clampToGround',
# 'clampToSeaFloor']:
# element.set('tessellate', '1')
coords = list(linearring.coords)
......@@ -195,36 +208,40 @@ class Geometry(_BaseObject):
return element
def _etree_polygon(self, polygon):
element = etree.Element("%sPolygon" %self.ns)
element = etree.Element("%sPolygon" % self.ns)
self._set_extrude(element)
self._set_altitude_mode(element)
outer_boundary = etree.SubElement(element, "%souterBoundaryIs" %self.ns)
outer_boundary = etree.SubElement(
element, "%souterBoundaryIs" % self.ns
)
outer_boundary.append(self._etree_linearring(polygon.exterior))
for ib in polygon.interiors:
inner_boundary = etree.SubElement(element, "%sinnerBoundaryIs" %self.ns)
inner_boundary = etree.SubElement(
element, "%sinnerBoundaryIs" % self.ns
)
inner_boundary.append(self._etree_linearring(ib))
return element
def _etree_multipoint(self, points):
element = etree.Element("%sMultiGeometry" %self.ns)
element = etree.Element("%sMultiGeometry" % self.ns)
for point in points.geoms:
element.append(self._etree_point(point))
return element
def _etree_multilinestring(self, linestrings):
element = etree.Element("%sMultiGeometry" %self.ns)
element = etree.Element("%sMultiGeometry" % self.ns)
for linestring in linestrings.geoms:
element.append(self._etree_linestring(linestring))
return element
def _etree_multipolygon(self, polygons):
element = etree.Element("%sMultiGeometry" %self.ns)
element = etree.Element("%sMultiGeometry" % self.ns)
for polygon in polygons.geoms:
element.append(self._etree_polygon(polygon))
return element
def _etree_collection(self, features):
element = etree.Element("%sMultiGeometry" %self.ns)
element = etree.Element("%sMultiGeometry" % self.ns)
for feature in features.geoms:
if feature.geom_type == "Point":
element.append(self._etree_point(feature))
......@@ -282,16 +299,17 @@ class Geometry(_BaseObject):
altitude_mode = element.find('%saltitudeMode' % self.ns)
if altitude_mode is not None:
am = altitude_mode.text.strip()
if am in ['clampToGround',
#'relativeToSeaFloor', 'clampToSeaFloor',
'relativeToGround', 'absolute']:
if am in [
'clampToGround',
# 'relativeToSeaFloor', 'clampToSeaFloor',
'relativeToGround', 'absolute'
]:
self.altitude_mode = am
else:
self.altitude_mode = None
else:
self.altitude_mode = None
def _get_coordinates(self, element):
coordinates = element.find('%scoordinates' % self.ns)
if coordinates is not None:
......@@ -321,9 +339,9 @@ class Geometry(_BaseObject):
return LineString(coords)
if element.tag == ('%sPolygon' % self.ns):
self._get_geometry_spec(element)
outer_boundary = element.find('%souterBoundaryIs' %self.ns)
outer_boundary = element.find('%souterBoundaryIs' % self.ns)
ob = self._get_linear_ring(outer_boundary)
inner_boundaries = element.findall('%sinnerBoundaryIs' %self.ns)
inner_boundaries = element.findall('%sinnerBoundaryIs' % self.ns)
ibs = []
for inner_boundary in inner_boundaries:
ibs.append(self._get_linear_ring(inner_boundary))
......@@ -333,8 +351,6 @@ class Geometry(_BaseObject):
self._get_geometry_spec(element)
return LinearRing(coords)
def _get_multigeometry(self, element):
# MultiGeometry
geoms = []
......@@ -353,9 +369,13 @@ class Geometry(_BaseObject):
if polygons:
for polygon in polygons:
self._get_geometry_spec(polygon)
outer_boundary = polygon.find('%souterBoundaryIs' %self.ns)
outer_boundary = polygon.find(
'%souterBoundaryIs' % self.ns
)
ob = self._get_linear_ring(outer_boundary)
inner_boundaries = polygon.findall('%sinnerBoundaryIs' %self.ns)
inner_boundaries = polygon.findall(
'%sinnerBoundaryIs' % self.ns
)
ibs = []
for inner_boundary in inner_boundaries:
ibs.append(self._get_linear_ring(inner_boundary))
......
# -*- coding: utf-8 -*-
# Copyright (C) 2012 Christian Ledermann
# Copyright (C) 2012 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 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.
# 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
# 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
"""
With the launch of Google Earth 5.0, Google has provided extensions to KML
......@@ -76,9 +76,6 @@ located at http://developers.google.com/kml/schema/kml22gx.xsd.
import logging
logger = logging.getLogger('fastkml.gx')
from .config import etree
from .config import GXNS as NS
from .config import LXML
# from .config import etree
# from .config import GXNS as NS
# from .config import LXML
# -*- coding: utf-8 -*-
# Copyright (C) 2012 Christian Ledermann
# Copyright (C) 2012 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 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.
# 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
# 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
"""
KML is an open standard officially named the OpenGIS KML Encoding Standard
......@@ -31,9 +31,9 @@ except ImportError:
import urllib.parse as urlparse
import warnings
from .geometry import Point, LineString, Polygon
from .geometry import MultiPoint, MultiLineString, MultiPolygon
from .geometry import LinearRing
# from .geometry import Point, LineString, Polygon
# from .geometry import MultiPoint, MultiLineString, MultiPolygon
# from .geometry import LinearRing
from .geometry import Geometry
from datetime import datetime, date
......@@ -54,7 +54,7 @@ from .base import _BaseObject, _XMLObject
from .styles import StyleUrl, Style, StyleMap, _StyleSelector
import fastkml.atom as atom
import fastkml.gx as gx
# import fastkml.gx as gx
import fastkml.config as config
try:
......@@ -76,7 +76,7 @@ class KML(object):
to be initialized with empty namespace as well in this case.
"""
self._features =[]
self._features = []
if ns is None:
self.ns = config.NS
......@@ -86,7 +86,10 @@ class KML(object):
def from_string(self, xml_string):
""" create a KML object from a xml string"""
if config.LXML:
element = etree.fromstring(xml_string, parser=etree.XMLParser(huge_tree=True))
element = etree.fromstring(
xml_string,
parser=etree.XMLParser(huge_tree=True)
)
else:
element = etree.XML(xml_string)
......@@ -119,22 +122,27 @@ class KML(object):
root.set('xmlns', config.NS[1:-1])
else:
if config.LXML:
root = etree.Element('%skml' % self.ns, nsmap={None:self.ns[1:-1]})
root = etree.Element(
'%skml' % self.ns,
nsmap={None: self.ns[1:-1]}
)
else:
root = etree.Element('%skml' % self.ns)
for feature in self.features():
root.append(feature.etree_element())
return root
def to_string(self, prettyprint=False):
""" Return the KML Object as serialized xml """
if config.LXML and prettyprint:
return etree.tostring(self.etree_element(), encoding='utf-8',
pretty_print=True).decode('UTF-8')
return etree.tostring(
self.etree_element(),
encoding='utf-8',
pretty_print=True).decode('UTF-8')
else:
return etree.tostring(self.etree_element(),
encoding='utf-8').decode('UTF-8')
return etree.tostring(
self.etree_element(),
encoding='utf-8').decode('UTF-8')
def features(self):
""" iterate over features """
......@@ -143,7 +151,9 @@ class KML(object):
yield feature
else:
raise TypeError(
"Features must be instances of (Document, Folder, Placemark)")
"Features must be instances of "
"(Document, Folder, Placemark)"
)
def append(self, kmlobj):
""" append a feature """
......@@ -151,7 +161,8 @@ class KML(object):
self._features.append(kmlobj)
else:
raise TypeError(
"Features must be instances of (Document, Folder, Placemark)")
"Features must be instances of (Document, Folder, Placemark)")
class _Feature(_BaseObject):
"""
......@@ -188,17 +199,17 @@ class _Feature(_BaseObject):
_atom_link = None
# Specifies the URL of the website containing this KML or KMZ file.
#TODO address = None
# TODO address = None
# A string value representing an unstructured address written as a
# standard street, city, state address, and/or as a postal code.
# You can use the <address> tag to specify the location of a point
# instead of using latitude and longitude coordinates.
#TODO phoneNumber = None
# TODO phoneNumber = None
# A string value representing a telephone number.
# This element is used by Google Maps Mobile only.
_snippet = None #XXX
_snippet = None # XXX
# _snippet is eiter a tuple of a string Snippet.text and an integer
# Snippet.maxLines or a string
#
......@@ -213,7 +224,7 @@ class _Feature(_BaseObject):
# that specifies the maximum number of lines to display.
description = None
#User-supplied content that appears in the description balloon.
# User-supplied content that appears in the description balloon.
_styleUrl = None
# URL of a <Style> or <StyleMap> defined in a Document.
......@@ -240,11 +251,11 @@ class _Feature(_BaseObject):
_time_stamp = None
# Associates this Feature with a point in time.
#TODO Region = None
# TODO Region = None
# Features and geometry associated with a Region are drawn only when
# the Region is active.
#TODO ExtendedData = None
# TODO ExtendedData = None
# Allows you to add custom data to a KML file. This data can be
# (1) data that references an external XML schema,
# (2) untyped data/value pairs, or
......@@ -257,11 +268,13 @@ class _Feature(_BaseObject):
# <Metadata> (deprecated in KML 2.2; use <ExtendedData> instead)
extended_data = None
def __init__(self, ns=None, id=None, name=None, description=None,
styles=None, styleUrl=None, extended_data=None):
def __init__(
self, ns=None, id=None, name=None, description=None,
styles=None, styleUrl=None, extended_data=None
):
super(_Feature, self).__init__(ns, id)
self.name=name
self.description=description
self.name = name
self.description = description
self.styleUrl = styleUrl
self._styles = []
if styles:
......@@ -297,7 +310,7 @@ class _Feature(_BaseObject):
@timeStamp.setter
def timeStamp(self, dt):
if dt == None:
if dt is None:
self._time_stamp = None
else:
self._time_stamp = TimeStamp(timestamp=dt)
......@@ -370,12 +383,11 @@ class _Feature(_BaseObject):
self._atom_author = atom.Author(name=name)
else:
self._atom_author.name = name
elif name == None:
elif name is None:
self._atom_author = None
else:
raise TypeError
def append_style(self, style):
""" append a style to the feature """
if isinstance(style, _StyleSelector):
......@@ -390,6 +402,7 @@ class _Feature(_BaseObject):
yield style
else:
raise TypeError
@property
def snippet(self):
if self._snippet:
......@@ -406,7 +419,10 @@ class _Feature(_BaseObject):
elif isinstance(self._snippet, basestring):
return self._snippet
else:
raise ValueError("Snippet must be dict of {'text':t, 'maxLines':i} or string")
raise ValueError(
"Snippet must be dict of "
"{'text':t, 'maxLines':i} or string"
)
@snippet.setter
def snippet(self, snip=None):
......@@ -421,27 +437,29 @@ class _Feature(_BaseObject):
elif snip is None:
self._snippet = None
else:
raise ValueError("Snippet must be dict of {'text':t, 'maxLines':i} or string")
raise ValueError(
"Snippet must be dict of {'text':t, 'maxLines':i} or string"
)
def etree_element(self):
element = super(_Feature, self).etree_element()
if self.name:
name = etree.SubElement(element, "%sname" %self.ns)
name = etree.SubElement(element, "%sname" % self.ns)
name.text = self.name
if self.description:
description =etree.SubElement(element, "%sdescription" %self.ns)
description = etree.SubElement(element, "%sdescription" % self.ns)
description.text = self.description
visibility = etree.SubElement(element, "%svisibility" %self.ns)
visibility = etree.SubElement(element, "%svisibility" % self.ns)
visibility.text = str(self.visibility)
if self.isopen:
isopen = etree.SubElement(element, "%sopen" %self.ns)
isopen = etree.SubElement(element, "%sopen" % self.ns)
isopen.text = str(self.isopen)
if self._styleUrl is not None:
element.append(self._styleUrl.etree_element())
for style in self.styles():
element.append(style.etree_element())
if self.snippet:
snippet = etree.SubElement(element, "%sSnippet" %self.ns)
snippet = etree.SubElement(element, "%sSnippet" % self.ns)
if isinstance(self.snippet, basestring):
snippet.text = self.snippet
else:
......@@ -450,7 +468,9 @@ class _Feature(_BaseObject):
if self.snippet.get('maxLines'):
snippet.set('maxLines', str(self.snippet['maxLines']))
if (self._time_span is not None) and (self._time_stamp is not None):
raise ValueError('Either Timestamp or Timespan can be defined, not both')
raise ValueError(
'Either Timestamp or Timespan can be defined, not both'
)
elif self._time_span is not None:
element.append(self._time_span.etree_element())
elif self._time_stamp is not None:
......@@ -463,19 +483,18 @@ class _Feature(_BaseObject):
element.append(self.extended_data.etree_element())
return element
def from_element(self, element):
super(_Feature, self).from_element(element)
name = element.find('%sname' %self.ns)
name = element.find('%sname' % self.ns)
if name is not None:
self.name = name.text
description = element.find('%sdescription' %self.ns)
description = element.find('%sdescription' % self.ns)
if description is not None:
self.description = description.text
visibility = element.find('%svisibility' %self.ns)
visibility = element.find('%svisibility' % self.ns)
if visibility is not None:
self.visibility = int(visibility.text)
isopen = element.find('%sopen' %self.ns)
isopen = element.find('%sopen' % self.ns)
if isopen is not None:
self.isopen = int(isopen.text)
styles = element.findall('%sStyle' % self.ns)
......@@ -524,14 +543,12 @@ class _Feature(_BaseObject):
x = ExtendedData(self.ns)
x.from_element(extended_data)
self.extended_data = x
#else:
# else:
# logger.warn(
# 'arbitrary or typed extended data is not yet supported'
# )
class _Container(_Feature):
"""
abstract element; do not create
......@@ -544,10 +561,14 @@ class _Container(_Feature):
_features = []
def __init__(self, ns=None, id=None, name=None, description=None,
styles=None, styleUrl=None):
super(_Container, self).__init__(ns, id, name, description, styles, styleUrl)
self._features =[]
def __init__(
self, ns=None, id=None, name=None, description=None,
styles=None, styleUrl=None
):
super(_Container, self).__init__(
ns, id, name, description, styles, styleUrl
)
self._features = []
def features(self):
""" iterate over features """
......@@ -564,18 +585,17 @@ class _Container(_Feature):
element.append(feature.etree_element())
return element
def append(self, kmlobj):
""" append a feature """
if isinstance(kmlobj, (Folder, Placemark)):
self._features.append(kmlobj)
else:
raise TypeError(
"Features must be instances of (Folder, Placemark)")
"Features must be instances of (Folder, Placemark)"
)
assert(kmlobj != self)
class Document(_Container):
"""
A Document is a container for features and styles. This element is
......@@ -613,7 +633,7 @@ class Document(_Container):
self.append(feature)
schemata = element.findall('%sSchema' % self.ns)
for schema in schemata:
s = Schema(self.ns, id = 'default')
s = Schema(self.ns, id='default')
s.from_element(schema)
self.append_schema(s)
......@@ -651,6 +671,7 @@ class Folder(_Container):
feature.from_element(placemark)
self.append(feature)
class Placemark(_Feature):
"""
A Placemark is a Feature with associated Geometry.
......@@ -715,6 +736,7 @@ class Placemark(_Feature):
logger.error('Object does not have a geometry')
return element
class _TimePrimitive(_BaseObject):
""" The dateTime is defined according to XML Schema time.
The value can be expressed as yyyy-mm-ddThh:mm:sszzzzzz, where T is
......@@ -748,7 +770,6 @@ class _TimePrimitive(_BaseObject):
resolution = None
return resolution
def parse_str(self, datestr):
resolution = 'dateTime'
year = 0
......@@ -778,7 +799,6 @@ class _TimePrimitive(_BaseObject):
raise ValueError
return [dt, resolution]
def date_to_string(self, dt, resolution=None):
if isinstance(dt, (date, datetime)):
resolution = self.get_resolution(dt, resolution)
......@@ -807,18 +827,17 @@ class TimeStamp(_TimePrimitive):
def etree_element(self):
element = super(TimeStamp, self).etree_element()
when = etree.SubElement(element, "%swhen" %self.ns)
when = etree.SubElement(element, "%swhen" % self.ns)
when.text = self.date_to_string(*self.timestamp)
return element
def from_element(self, element):
super(TimeStamp, self).from_element(element)
when = element.find('%swhen' %self.ns)
when = element.find('%swhen' % self.ns)
if when is not None:
self.timestamp = self.parse_str(when.text)
class TimeSpan(_TimePrimitive):
""" Represents an extent in time bounded by begin and end dateTimes.
"""
......@@ -826,8 +845,10 @@ class TimeSpan(_TimePrimitive):
begin = None
end = None
def __init__(self, ns=None, id=None, begin=None, begin_res=None,
end=None, end_res=None):
def __init__(
self, ns=None, id=None, begin=None, begin_res=None,
end=None, end_res=None
):
super(TimeSpan, self).__init__(ns, id)
if begin:
resolution = self.get_resolution(begin, begin_res)
......@@ -838,10 +859,10 @@ class TimeSpan(_TimePrimitive):
def from_element(self, element):
super(TimeSpan, self).from_element(element)
begin = element.find('%sbegin' %self.ns)
begin = element.find('%sbegin' % self.ns)
if begin is not None:
self.begin = self.parse_str(begin.text)
end = element.find('%send' %self.ns)
end = element.find('%send' % self.ns)
if end is not None:
self.end = self.parse_str(end.text)
......@@ -850,16 +871,16 @@ class TimeSpan(_TimePrimitive):
if self.begin is not None:
text = self.date_to_string(*self.begin)
if text:
begin = etree.SubElement(element, "%sbegin" %self.ns)
begin = etree.SubElement(element, "%sbegin" % self.ns)
begin.text = text
if self.end is not None:
text = self.date_to_string(*self.end)
if text:
end = etree.SubElement(element, "%send" %self.ns)
end = etree.SubElement(element, "%send" % self.ns)
end.text = text
if self.begin == self.end == None:
if self.begin == self.end is None:
raise ValueError("Either begin, end or both must be set")
#TODO test if end > begin
# TODO test if end > begin
return element
......@@ -890,9 +911,11 @@ class Schema(_BaseObject):
sfs = []
for simple_field in self._simple_fields:
if simple_field.get('type') and simple_field.get('name'):
sfs.append( {'type': simple_field['type'],
sfs.append({
'type': simple_field['type'],
'name': simple_field['name'],
'displayName': simple_field.get('displayName')})
'displayName': simple_field.get('displayName')
})
return tuple(sfs)
@simple_fields.setter
......@@ -933,12 +956,17 @@ class Schema(_BaseObject):
the Google Earth user. Use the [CDATA] element to escape standard
HTML markup.
"""
allowed_types= ['string', 'int', 'uint', 'short', 'ushort',
'float', 'double', 'bool']
allowed_types = [
'string', 'int', 'uint', 'short', 'ushort',
'float', 'double', 'bool'
]
if type not in allowed_types:
raise TypeError("type must be one of 'string', 'int', 'uint', 'short', 'ushort', 'float', 'double', 'bool'")
raise TypeError(
"type must be one of ""'string', 'int', 'uint', 'short', "
"'ushort', 'float', 'double', 'bool'"
)
else:
#TODO explicit type conversion to check for the right type
# TODO explicit type conversion to check for the right type
pass
self._simple_fields.append({'type': type, 'name': name,
'displayName': displayName})
......@@ -963,11 +991,11 @@ class Schema(_BaseObject):
if self.name:
element.set('name', self.name)
for simple_field in self.simple_fields:
sf = etree.SubElement(element, "%sSimpleField" %self.ns)
sf = etree.SubElement(element, "%sSimpleField" % self.ns)
sf.set('type', simple_field['type'])
sf.set('name', simple_field['name'])
if simple_field.get('displayName'):
dn = etree.SubElement(sf, "%sdisplayName" %self.ns)
dn = etree.SubElement(sf, "%sdisplayName" % self.ns)
dn.text = simple_field['displayName']
return element
......@@ -1008,10 +1036,12 @@ class ExtendedData(_XMLObject):
class UntypedExtendedData(ExtendedData):
def __init__(self, ns=None, elements=None):
super(UntypedExtendedData, self).__init__(ns, elements)
warnings.warn("UntypedExtendedData is deprecated use ExtendedData instead", DeprecationWarning)
warnings.warn(
"UntypedExtendedData is deprecated use ExtendedData instead",
DeprecationWarning
)
class Data(_XMLObject):
......@@ -1044,10 +1074,17 @@ class Data(_XMLObject):
if display_name is not None:
self.display_name = display_name.text
class UntypedExtendedDataElement(Data):
def __init__(self, ns=None, name=None, value=None, display_name=None):
super(UntypedExtendedDataElement, self).__init__(ns, name, value, display_name)
warnings.warn("UntypedExtendedDataElement is deprecated use Data instead", DeprecationWarning)
super(UntypedExtendedDataElement, self).__init__(
ns, name, value, display_name
)
warnings.warn(
"UntypedExtendedDataElement is deprecated use Data instead",
DeprecationWarning
)
class SchemaData(_XMLObject):
"""
......@@ -1093,7 +1130,7 @@ class SchemaData(_XMLObject):
def append_data(self, name, value):
if isinstance(name, basestring) and name:
self._data.append({'name':name, 'value': value})
self._data.append({'name': name, 'value': value})
else:
raise TypeError('name must be a nonempty string')
......@@ -1101,7 +1138,7 @@ class SchemaData(_XMLObject):
element = super(SchemaData, self).etree_element()
element.set('schemaUrl', self.schema_url)
for data in self.data:
sd = etree.SubElement(element, "%sSimpleData" %self.ns)
sd = etree.SubElement(element, "%sSimpleData" % self.ns)
sd.set('name', data['name'])
sd.text = data['value']
return element
......@@ -1113,5 +1150,3 @@ class SchemaData(_XMLObject):
simple_data = element.findall('%sSimpleData' % self.ns)
for sd in simple_data:
self.append_data(sd.get('name'), sd.text)
# -*- coding: utf-8 -*-
# Copyright (C) 2012 Christian Ledermann
# Copyright (C) 2012 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 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.
# 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
# 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
"""
Once you've created features within Google Earth and examined the KML
code Google Earth generates, you'll notice how styles are an important
......@@ -23,13 +23,10 @@ part of how your data is displayed.
import logging
logger = logging.getLogger('fastkml.styles')
import fastkml.config as config
from fastkml.config import etree
from fastkml.base import _BaseObject
class StyleUrl(_BaseObject):
"""
URL of a <Style> or <StyleMap> defined in a Document. If the style
......@@ -78,7 +75,7 @@ class Style(_StyleSelector):
__name__ = "Style"
_styles = None
def __init__(self, ns=None, id=None, styles = None):
def __init__(self, ns=None, id=None, styles=None):
super(Style, self).__init__(ns, id)
self._styles = []
if styles:
......@@ -100,27 +97,27 @@ class Style(_StyleSelector):
def from_element(self, element):
super(Style, self).from_element(element)
style = element.find('%sIconStyle' %self.ns)
style = element.find('%sIconStyle' % self.ns)
if style is not None:
thestyle = IconStyle(self.ns)
thestyle.from_element(style)
self.append_style(thestyle)
style = element.find('%sLineStyle' %self.ns)
style = element.find('%sLineStyle' % self.ns)
if style is not None:
thestyle = LineStyle(self.ns)
thestyle.from_element(style)
self.append_style(thestyle)
style = element.find('%sPolyStyle' %self.ns)
style = element.find('%sPolyStyle' % self.ns)
if style is not None:
thestyle = PolyStyle(self.ns)
thestyle.from_element(style)
self.append_style(thestyle)
style = element.find('%sLabelStyle' %self.ns)
style = element.find('%sLabelStyle' % self.ns)
if style is not None:
thestyle = LabelStyle(self.ns)
thestyle.from_element(style)
self.append_style(thestyle)
style = element.find('%sBalloonStyle' %self.ns)
style = element.find('%sBalloonStyle' % self.ns)
if style is not None:
thestyle = BalloonStyle(self.ns)
thestyle.from_element(style)
......@@ -148,14 +145,13 @@ class StyleMap(_StyleSelector):
super(StyleMap, self).__init__(ns, id)
pass
def from_element(self, element):
super(StyleMap, self).from_element(element)
pairs = element.findall('%sPair' %self.ns)
pairs = element.findall('%sPair' % self.ns)
for pair in pairs:
key = pair.find('%skey' %self.ns)
style = pair.find('%sStyle' %self.ns)
style_url = pair.find('%sstyleUrl' %self.ns)
key = pair.find('%skey' % self.ns)
style = pair.find('%sStyle' % self.ns)
style_url = pair.find('%sstyleUrl' % self.ns)
if key.text == "highlight":
if style is not None:
highlight = Style(self.ns)
......@@ -179,20 +175,18 @@ class StyleMap(_StyleSelector):
else:
raise ValueError
def etree_element(self):
element = super(StyleMap, self).etree_element()
if self.normal:
if isinstance(self.normal, (Style, StyleUrl)):
pair = etree.SubElement(element, "%sPair" %self.ns)
key = etree.SubElement(pair, "%skey" %self.ns)
pair = etree.SubElement(element, "%sPair" % self.ns)
key = etree.SubElement(pair, "%skey" % self.ns)
key.text = 'normal'
pair.append(self.normal.etree_element())
if self.highlight:
if isinstance(self.highlight, (Style, StyleUrl)):
pair = etree.SubElement(element, "%sPair" %self.ns)
key = etree.SubElement(pair, "%skey" %self.ns)
pair = etree.SubElement(element, "%sPair" % self.ns)
key = etree.SubElement(pair, "%skey" % self.ns)
key.text = 'highlight'
pair.append(self.highlight.etree_element())
return element
......@@ -224,27 +218,26 @@ class _ColorStyle(_BaseObject):
self.color = color
self.colorMode = colorMode
def etree_element(self):
element = super(_ColorStyle, self).etree_element()
if self.color:
color = etree.SubElement(element, "%scolor" %self.ns)
color = etree.SubElement(element, "%scolor" % self.ns)
color.text = self.color
if self.colorMode:
colorMode = etree.SubElement(element, "%scolorMode" %self.ns)
colorMode = etree.SubElement(element, "%scolorMode" % self.ns)
colorMode.text = self.colorMode
return element
def from_element(self, element):
super(_ColorStyle, self).from_element(element)
colorMode = element.find('%scolorMode' %self.ns)
colorMode = element.find('%scolorMode' % self.ns)
if colorMode is not None:
self.colorMode = colorMode.text
color = element.find('%scolor' %self.ns)
color = element.find('%scolor' % self.ns)
if color is not None:
self.color = color.text
class IconStyle(_ColorStyle):
""" Specifies how icons for point Placemarks are drawn """
__name__ = "IconStyle"
......@@ -256,8 +249,10 @@ class IconStyle(_ColorStyle):
icon_href = None
# An HTTP address or a local file specification used to load an icon.
def __init__(self, ns=None, id=None, color=None, colorMode=None,
scale=1.0, heading=None, icon_href=None):
def __init__(
self, ns=None, id=None, color=None, colorMode=None, scale=1.0,
heading=None, icon_href=None
):
super(IconStyle, self).__init__(ns, id, color, colorMode)
self.scale = scale
self.heading = heading
......@@ -266,33 +261,32 @@ class IconStyle(_ColorStyle):
def etree_element(self):
element = super(IconStyle, self).etree_element()
if self.scale is not None:
scale = etree.SubElement(element, "%sscale" %self.ns)
scale = etree.SubElement(element, "%sscale" % self.ns)
scale.text = str(self.scale)
if self.heading:
heading = etree.SubElement(element, "%sheading" %self.ns)
heading = etree.SubElement(element, "%sheading" % self.ns)
heading.text = str(self.heading)
if self.icon_href:
icon = etree.SubElement(element, "%sIcon" %self.ns)
href = etree.SubElement(icon, "%shref" %self.ns)
icon = etree.SubElement(element, "%sIcon" % self.ns)
href = etree.SubElement(icon, "%shref" % self.ns)
href.text = self.icon_href
return element
def from_element(self, element):
super(IconStyle, self).from_element(element)
scale = element.find('%sscale' %self.ns)
scale = element.find('%sscale' % self.ns)
if scale is not None:
self.scale = float(scale.text)
heading = element.find('%sheading' %self.ns)
heading = element.find('%sheading' % self.ns)
if heading is not None:
self.heading = float(heading.text)
icon = element.find('%sIcon' %self.ns)
icon = element.find('%sIcon' % self.ns)
if icon is not None:
href = icon.find('%shref' %self.ns)
href = icon.find('%shref' % self.ns)
if href is not None:
self.icon_href = href.text
class LineStyle(_ColorStyle):
"""
Specifies the drawing style (color, color mode, and line width)
......@@ -304,24 +298,26 @@ class LineStyle(_ColorStyle):
width = 1.0
# Width of the line, in pixels.
def __init__(self, ns=None, id=None, color=None, colorMode=None,
width=1):
def __init__(
self, ns=None, id=None, color=None, colorMode=None, width=1
):
super(LineStyle, self).__init__(ns, id, color, colorMode)
self.width = width
def etree_element(self):
element = super(LineStyle, self).etree_element()
if self.width is not None:
width = etree.SubElement(element, "%swidth" %self.ns)
width = etree.SubElement(element, "%swidth" % self.ns)
width.text = str(self.width)
return element
def from_element(self, element):
super(LineStyle, self).from_element(element)
width = element.find('%swidth' %self.ns)
width = element.find('%swidth' % self.ns)
if width is not None:
self.width = float(width.text)
class PolyStyle(_ColorStyle):
"""
Specifies the drawing style for all polygons, including polygon
......@@ -335,8 +331,9 @@ class PolyStyle(_ColorStyle):
# Boolean value. Specifies whether to outline the polygon.
# Polygon outlines use the current LineStyle.
def __init__(self, ns=None, id=None, color=None, colorMode=None,
fill=1, outline=1):
def __init__(
self, ns=None, id=None, color=None, colorMode=None, fill=1, outline=1
):
super(PolyStyle, self).__init__(ns, id, color, colorMode)
self.fill = fill
self.outline = outline
......@@ -344,19 +341,19 @@ class PolyStyle(_ColorStyle):
def etree_element(self):
element = super(PolyStyle, self).etree_element()
if self.fill is not None:
fill = etree.SubElement(element, "%sfill" %self.ns)
fill = etree.SubElement(element, "%sfill" % self.ns)
fill.text = str(self.fill)
if self.outline is not None:
outline = etree.SubElement(element, "%soutline" %self.ns)
outline = etree.SubElement(element, "%soutline" % self.ns)
outline.text = str(self.outline)
return element
def from_element(self, element):
super(PolyStyle, self).from_element(element)
fill = element.find('%sfill' %self.ns)
fill = element.find('%sfill' % self.ns)
if fill is not None:
self.fill = int(fill.text)
outline = element.find('%soutline' %self.ns)
outline = element.find('%soutline' % self.ns)
if outline is not None:
self.outline = int(outline.text)
......@@ -369,24 +366,26 @@ class LabelStyle(_ColorStyle):
scale = 1.0
# Resizes the label.
def __init__(self, ns=None, id=None, color=None, colorMode=None,
scale=1.0):
def __init__(
self, ns=None, id=None, color=None, colorMode=None, scale=1.0
):
super(LabelStyle, self).__init__(ns, id, color, colorMode)
self.scale = scale
def etree_element(self):
element = super(LabelStyle, self).etree_element()
if self.scale is not None:
scale = etree.SubElement(element, "%sscale" %self.ns)
scale = etree.SubElement(element, "%sscale" % self.ns)
scale.text = str(self.scale)
return element
def from_element(self, element):
super(LabelStyle, self).from_element(element)
scale = element.find('%sscale' %self.ns)
scale = element.find('%sscale' % self.ns)
if scale is not None:
self.scale = float(scale.text)
class BalloonStyle(_BaseObject):
""" Specifies how the description balloon for placemarks is drawn.
The <bgColor>, if specified, is used as the background color of
......@@ -395,15 +394,15 @@ class BalloonStyle(_BaseObject):
__name__ = "BalloonStyle"
bgColor = None
#Background color of the balloon (optional). Color and opacity (alpha)
#values are expressed in hexadecimal notation. The range of values for
#any one color is 0 to 255 (00 to ff). The order of expression is
# Background color of the balloon (optional). Color and opacity (alpha)
# values are expressed in hexadecimal notation. The range of values for
# any one color is 0 to 255 (00 to ff). The order of expression is
# aabbggrr, where aa=alpha (00 to ff); bb=blue (00 to ff);
# gg=green (00 to ff); rr=red (00 to ff).
# For alpha, 00 is fully transparent and ff is fully opaque.
# For example, if you want to apply a blue color with 50 percent
# opacity to an overlay, you would specify the following:
#<bgColor>7fff0000</bgColor>, where alpha=0x7f, blue=0xff, green=0x00,
# <bgColor>7fff0000</bgColor>, where alpha=0x7f, blue=0xff, green=0x00,
# and red=0x00. The default is opaque white (ffffffff).
# Note: The use of the <color> element within <BalloonStyle> has been
# deprecated. Use <bgColor> instead.
......@@ -412,33 +411,36 @@ class BalloonStyle(_BaseObject):
# Foreground color for text. The default is black (ff000000).
text = None
#Text displayed in the balloon. If no text is specified, Google Earth
#draws the default balloon (with the Feature <name> in boldface,
#the Feature <description>, links for driving directions, a white
#background, and a tail that is attached to the point coordinates of
#the Feature, if specified).
#You can add entities to the <text> tag using the following format to
#refer to a child element of Feature: $[name], $[description], $[address],
#$[id], $[Snippet]. Google Earth looks in the current Feature for the
#corresponding string entity and substitutes that information in the balloon.
#To include To here - From here driving directions in the balloon,
#use the $[geDirections] tag. To prevent the driving directions links
#from appearing in a balloon, include the <text> element with some content,
#or with $[description] to substitute the basic Feature <description>.
#For example, in the following KML excerpt, $[name] and $[description]
#fields will be replaced by the <name> and <description> fields found
#in the Feature elements that use this BalloonStyle:
#<text>This is $[name], whose description is:<br/>$[description]</text>
# Text displayed in the balloon. If no text is specified, Google Earth
# draws the default balloon (with the Feature <name> in boldface,
# the Feature <description>, links for driving directions, a white
# background, and a tail that is attached to the point coordinates of
# the Feature, if specified).
# You can add entities to the <text> tag using the following format to
# refer to a child element of Feature: $[name], $[description], $[address],
# $[id], $[Snippet]. Google Earth looks in the current Feature for the
# corresponding string entity and substitutes that information in the
# balloon.
# To include To here - From here driving directions in the balloon,
# use the $[geDirections] tag. To prevent the driving directions links
# from appearing in a balloon, include the <text> element with some content
# or with $[description] to substitute the basic Feature <description>.
# For example, in the following KML excerpt, $[name] and $[description]
# fields will be replaced by the <name> and <description> fields found
# in the Feature elements that use this BalloonStyle:
# <text>This is $[name], whose description is:<br/>$[description]</text>
displayMode = None
#If <displayMode> is default, Google Earth uses the information supplied
#in <text> to create a balloon . If <displayMode> is hide, Google Earth
#does not display the balloon. In Google Earth, clicking the List View
#icon for a Placemark whose balloon's <displayMode> is hide causes
#Google Earth to fly to the Placemark.
def __init__(self, ns=None, id=None, bgColor=None, textColor=None,
text=None, displayMode=None):
# If <displayMode> is default, Google Earth uses the information supplied
# in <text> to create a balloon . If <displayMode> is hide, Google Earth
# does not display the balloon. In Google Earth, clicking the List View
# icon for a Placemark whose balloon's <displayMode> is hide causes
# Google Earth to fly to the Placemark.
def __init__(
self, ns=None, id=None, bgColor=None, textColor=None, text=None,
displayMode=None
):
super(BalloonStyle, self).__init__(ns, id)
self.bgColor = bgColor
self.textColor = textColor
......@@ -447,35 +449,35 @@ class BalloonStyle(_BaseObject):
def from_element(self, element):
super(BalloonStyle, self).from_element(element)
bgColor = element.find('%sbgColor' %self.ns)
bgColor = element.find('%sbgColor' % self.ns)
if bgColor is not None:
self.bgColor = bgColor.text
else:
bgColor = element.find('%scolor' %self.ns)
bgColor = element.find('%scolor' % self.ns)
if bgColor is not None:
self.bgColor = bgColor.text
textColor = element.find('%stextColor' %self.ns)
textColor = element.find('%stextColor' % self.ns)
if textColor is not None:
self.textColor = textColor.text
text = element.find('%stext' %self.ns)
text = element.find('%stext' % self.ns)
if text is not None:
self.text = text.text
displayMode = element.find('%sdisplayMode' %self.ns)
displayMode = element.find('%sdisplayMode' % self.ns)
if displayMode is not None:
self.displayMode = displayMode.text
def etree_element(self):
element = super(BalloonStyle, self).etree_element()
if self.bgColor is not None:
elem = etree.SubElement(element, "%sbgColor" %self.ns)
elem = etree.SubElement(element, "%sbgColor" % self.ns)
elem.text = self.bgColor
if self.textColor is not None:
elem = etree.SubElement(element, "%stextColor" %self.ns)
elem = etree.SubElement(element, "%stextColor" % self.ns)
elem.text = self.textColor
if self.text is not None:
elem = etree.SubElement(element, "%stext" %self.ns)
elem = etree.SubElement(element, "%stext" % self.ns)
elem.text = self.text
if self.displayMode is not None:
elem = etree.SubElement(element, "%sdisplayMode" %self.ns)
elem = etree.SubElement(element, "%sdisplayMode" % self.ns)
elem.text = self.displayMode
return element
# -*- coding: utf-8 -*-
# Copyright (C) 2012 Christian Ledermann
# Copyright (C) 2012 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 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.
# 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
# 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 unittest
......@@ -23,7 +23,6 @@ from fastkml import base
from fastkml import atom
from fastkml import config
import datetime
from dateutil.tz import tzutc, tzoffset
......@@ -33,7 +32,6 @@ from fastkml.geometry import Point, LineString, Polygon
from fastkml.geometry import MultiPoint, MultiLineString, MultiPolygon
from fastkml.geometry import LinearRing, GeometryCollection
from fastkml.geometry import Geometry
from fastkml.geometry import asShape
class BaseClassesTestCase(unittest.TestCase):
......@@ -48,8 +46,8 @@ class BaseClassesTestCase(unittest.TestCase):
self.assertEqual(bo.__name__, None)
bo.targetId = 'target'
self.assertEqual(bo.targetId, 'target')
bo.ns =''
bo.id =None
bo.ns = ''
bo.id = None
self.assertEqual(bo.id, None)
self.assertEqual(bo.ns, '')
self.assertRaises(NotImplementedError, bo.etree_element)
......@@ -58,7 +56,7 @@ class BaseClassesTestCase(unittest.TestCase):
self.assertRaises(TypeError, bo.from_element, element)
bo.__name__ = 'NotABaseObject'
self.assertRaises(TypeError, bo.from_element, element)
#Note that we can coax baseclasses not to throw errors
# Note that we can coax baseclasses not to throw errors
bo.__name__ = 'Base'
bo.ns = config.NS
bo.from_element(element)
......@@ -75,23 +73,22 @@ class BaseClassesTestCase(unittest.TestCase):
self.assertEqual(f.isopen, 0)
self.assertEqual(f._atom_author, None)
self.assertEqual(f._atom_link, None)
#self.assertEqual(f.address, None)
#self.assertEqual(f.phoneNumber, None)
# self.assertEqual(f.address, None)
# self.assertEqual(f.phoneNumber, None)
self.assertEqual(f._snippet, None)
self.assertEqual(f.description, None)
self.assertEqual(f._styleUrl, None)
self.assertEqual(f._styles, [])
self.assertEqual(f._time_span, None)
self.assertEqual(f._time_stamp, None)
#self.assertEqual(f.region, None)
#self.assertEqual(f.extended_data, None)
# self.assertEqual(f.region, None)
# self.assertEqual(f.extended_data, None)
f.__name__ = 'Feature'
f.styleUrl = '#default'
self.assertTrue('Feature>' in str(f.to_string()))
self.assertTrue('#default' in str(f.to_string()))
def test_Container(self):
f = kml._Container(name='A Container')
d = kml.Document()
......@@ -100,7 +97,6 @@ class BaseClassesTestCase(unittest.TestCase):
f.append(p)
self.assertRaises(NotImplementedError, f.etree_element)
def test_TimePrimitive(self):
pass
......@@ -113,22 +109,29 @@ class BaseClassesTestCase(unittest.TestCase):
def test_Person(self):
pass
class BuildKmlTestCase(unittest.TestCase):
""" Build a simple KML File """
def test_kml(self):
""" kml file without contents """
k = kml.KML()
self.assertEqual(len( list(k.features())),0)
self.assertEqual(len(list(k.features())), 0)
if config.LXML:
self.assertEqual( str(k.to_string())[:43],
'<kml xmlns="http://www.opengis.net/kml/2.2"/>'[:43])
self.assertEqual(
str(k.to_string())[:43],
'<kml xmlns="http://www.opengis.net/kml/2.2"/>'[:43]
)
else:
if hasattr(etree, 'register_namespace'):
self.assertEqual(str(k.to_string())[:51],
'<kml:kml xmlns:kml="http://www.opengis.net/kml/2.2" />'[:51])
self.assertEqual(
str(k.to_string())[:51],
'<kml:kml xmlns:kml="http://www.opengis.net/kml/2.2" />'[:51]
)
else:
self.assertEqual(str(k.to_string())[:51],
'<ns0:kml xmlns:ns0="http://www.opengis.net/kml/2.2" />'[:51])
self.assertEqual(
str(k.to_string())[:51],
'<ns0:kml xmlns:ns0="http://www.opengis.net/kml/2.2" />'[:51]
)
k2 = kml.KML()
k2.from_string(k.to_string())
......@@ -144,14 +147,13 @@ class BuildKmlTestCase(unittest.TestCase):
k.append(f)
f2 = kml.Folder(ns, 'id2', 'name2', 'description2')
k.append(f2)
self.assertEqual(len(list(k.features())),2)
self.assertEqual(len( list( list(k.features())[0].features())),1)
self.assertEqual(len(list(k.features())), 2)
self.assertEqual(len(list(list(k.features())[0].features())), 1)
k2 = kml.KML()
s = k.to_string()
k2.from_string(s)
self.assertEqual(s, k2.to_string())
def test_placemark(self):
ns = '{http://www.opengis.net/kml/2.2}'
k = kml.KML(ns=ns)
......@@ -161,7 +163,7 @@ class BuildKmlTestCase(unittest.TestCase):
p2.geometry = LineString([(0, 0, 0), (1, 1, 1)])
k.append(p)
k.append(p2)
self.assertEqual(len(list(k.features())),2)
self.assertEqual(len(list(k.features())), 2)
k2 = kml.KML()
k2.from_string(k.to_string(prettyprint=True))
self.assertEqual(k.to_string(), k2.to_string())
......@@ -170,15 +172,15 @@ class BuildKmlTestCase(unittest.TestCase):
ns = '{http://www.opengis.net/kml/2.2}'
self.assertRaises(ValueError, kml.Schema, ns)
s = kml.Schema(ns, 'some_id')
self.assertEqual(len(list(s.simple_fields)),0)
self.assertEqual(len(list(s.simple_fields)), 0)
s.append('int', 'Integer', 'An Integer')
self.assertEqual(list(s.simple_fields)[0]['type'], 'int')
self.assertEqual(list(s.simple_fields)[0]['name'], 'Integer')
self.assertEqual(list(s.simple_fields)[0]['displayName'], 'An Integer')
s.simple_fields = None
self.assertEqual(len(list(s.simple_fields)),0)
self.assertEqual(len(list(s.simple_fields)), 0)
self.assertRaises(TypeError, s.append, ('none', 'Integer', 'An Integer'))
self.assertRaises(TypeError, s.simple_fields, [('none', 'Integer', 'An Integer'),])
self.assertRaises(TypeError, s.simple_fields, [('none', 'Integer', 'An Integer')])
self.assertRaises(TypeError, s.simple_fields, ('int', 'Integer', 'An Integer'))
fields = {'type': 'int', 'name': 'Integer', 'displayName': 'An Integer'}
s.simple_fields = fields
......@@ -193,7 +195,6 @@ class BuildKmlTestCase(unittest.TestCase):
self.assertEqual(list(s.simple_fields)[1]['name'], 'Integer')
self.assertEqual(list(s.simple_fields)[1]['displayName'], 'An Integer')
def test_schema_data(self):
ns = '{http://www.opengis.net/kml/2.2}'
self.assertRaises(ValueError, kml.SchemaData, ns)
......@@ -211,7 +212,6 @@ class BuildKmlTestCase(unittest.TestCase):
self.assertEqual(sd.data[0], {'value': 'Some new Text', 'name': 'text'})
self.assertEqual(sd.data[1], {'value': 2, 'name': 'Integer'})
def test_untyped_extended_data(self):
ns = '{http://www.opengis.net/kml/2.2}'
k = kml.KML(ns=ns)
......@@ -244,7 +244,6 @@ class BuildKmlTestCase(unittest.TestCase):
self.assertEqual(extended_data.elements[1].value, 'blue skies')
self.assertEqual(extended_data.elements[1].display_name, 'Weather')
def test_untyped_extended_data_nested(self):
ns = '{http://www.opengis.net/kml/2.2}'
k = kml.KML(ns=ns)
......@@ -274,7 +273,6 @@ class BuildKmlTestCase(unittest.TestCase):
self.assertEqual(folder_data.elements[0].name, 'type')
self.assertEqual(folder_data.elements[0].value, 'Folder')
def test_document(self):
k = kml.KML()
ns = '{http://www.opengis.net/kml/2.2}'
......@@ -287,21 +285,18 @@ class BuildKmlTestCase(unittest.TestCase):
f2 = kml.Folder(ns, 'id2', 'name2', 'description2')
d.append(f2)
p = kml.Placemark(ns, 'id', 'name', 'description')
p.geometry = Polygon([(0, 0, 0), (1, 1, 0), (1, 0, 1)])
p.geometry = Polygon([(0, 0, 0), (1, 1, 0), (1, 0, 1)])
p2 = kml.Placemark(ns, 'id2', 'name2', 'description2')
#p2 does not have a geometry!
# p2 does not have a geometry!
f2.append(p)
nf.append(p2)
self.assertEqual(len(list(k.features())),1)
self.assertEqual(len(list((list(k.features())[0].features()))),2)
self.assertEqual(len(list(k.features())), 1)
self.assertEqual(len(list((list(k.features())[0].features()))), 2)
k2 = kml.KML()
k2.from_string(k.to_string())
self.assertEqual(k.to_string(), k2.to_string())
def test_author(self):
k = kml.KML()
d = kml.Document()
d.author = 'Christian Ledermann'
self.assertTrue('Christian Ledermann' in str(d.to_string()))
......@@ -316,8 +311,6 @@ class BuildKmlTestCase(unittest.TestCase):
d2.from_string(d.to_string())
self.assertEqual(d.to_string(), d2.to_string())
d.author = None
#print (d.to_string())
def test_link(self):
d = kml.Document()
......@@ -332,8 +325,8 @@ class BuildKmlTestCase(unittest.TestCase):
self.assertEqual(d.to_string(), d2.to_string())
d.link = None
class KmlFromStringTestCase( unittest.TestCase ):
class KmlFromStringTestCase(unittest.TestCase):
def test_document(self):
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Document targetId="someTargetId">
......@@ -362,16 +355,14 @@ class KmlFromStringTestCase( unittest.TestCase ):
</kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertEqual(len(list(list(k.features())[0].features())),2)
self.assertEqual(len(list(k.features())), 1)
self.assertEqual(len(list(list(k.features())[0].features())), 2)
k2 = kml.KML()
k2.from_string(k.to_string())
self.assertEqual(k.to_string(), k2.to_string())
def test_folders(self):
doc="""<kml xmlns="http://www.opengis.net/kml/2.2">
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Folder>
<name>Folder.kml</name>
<open>1</open>
......@@ -412,15 +403,14 @@ class KmlFromStringTestCase( unittest.TestCase ):
</kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertEqual(len(list(list(k.features())[0].features())),3)
self.assertEqual(len(list(k.features())), 1)
self.assertEqual(len(list(list(k.features())[0].features())), 3)
k2 = kml.KML()
k2.from_string(k.to_string())
self.assertEqual(k.to_string(), k2.to_string())
def test_placemark(self):
doc="""<kml xmlns="http://www.opengis.net/kml/2.2">
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Placemark>
<name>Simple placemark</name>
<description>Attached to the ground. Intelligently places itself
......@@ -432,15 +422,14 @@ class KmlFromStringTestCase( unittest.TestCase ):
</kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertEqual(len(list(k.features())), 1)
self.assertEqual(list(k.features())[0].name, "Simple placemark")
k2 = kml.KML()
k2.from_string(k.to_string())
self.assertEqual(k.to_string(), k2.to_string())
def test_extended_data(self):
doc="""<kml xmlns="http://www.opengis.net/kml/2.2">
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Placemark>
<name>Simple placemark</name>
<description></description>
......@@ -489,23 +478,132 @@ class KmlFromStringTestCase( unittest.TestCase ):
self.assertEqual(sd.data[1]['value'], '347.45')
def test_polygon(self):
doc= """<kml xmlns="http://www.opengis.net/kml/2.2">
<Placemark>
<name>South Africa</name>
<Polygon><outerBoundaryIs><LinearRing><coordinates>31.521,-29.257,0 31.326,-29.402,0 30.902,-29.91,0 30.623,-30.424,0 30.056,-31.14,0 28.926,-32.172,0 28.22,-32.772,0 27.465,-33.227,0 26.419,-33.615,0 25.91,-33.667,0 25.781,-33.945,0 25.173,-33.797,0 24.678,-33.987,0 23.594,-33.794,0 22.988,-33.916,0 22.574,-33.864,0 21.543,-34.259,0 20.689,-34.417,0 20.071,-34.795,0 19.616,-34.819,0 19.193,-34.463,0 18.855,-34.444,0 18.425,-33.998,0 18.377,-34.137,0 18.244,-33.868,0 18.25,-33.281,0 17.925,-32.611,0 18.248,-32.429,0 18.222,-31.662,0 17.567,-30.726,0 17.064,-29.879,0 17.063,-29.876,0 16.345,-28.577,0 16.824,-28.082,0 17.219,-28.356,0 17.387,-28.784,0 17.836,-28.856,0 18.465,-29.045,0 19.002,-28.972,0 19.895,-28.461,0 19.896,-24.768,0 20.166,-24.918,0 20.759,-25.868,0 20.666,-26.477,0 20.89,-26.829,0 21.606,-26.727,0 22.106,-26.28,0 22.58,-25.979,0 22.824,-25.5,0 23.312,-25.269,0 23.734,-25.39,0 24.211,-25.67,0 25.025,-25.72,0 25.665,-25.487,0 25.766,-25.175,0 25.942,-24.696,0 26.486,-24.616,0 26.786,-24.241,0 27.119,-23.574,0 28.017,-22.828,0 29.432,-22.091,0 29.839,-22.102,0 30.323,-22.272,0 30.66,-22.152,0 31.191,-22.252,0 31.67,-23.659,0 31.931,-24.369,0 31.752,-25.484,0 31.838,-25.843,0 31.333,-25.66,0 31.044,-25.731,0 30.95,-26.023,0 30.677,-26.398,0 30.686,-26.744,0 31.283,-27.286,0 31.868,-27.178,0 32.072,-26.734,0 32.83,-26.742,0 32.58,-27.47,0 32.462,-28.301,0 32.203,-28.752,0 31.521,-29.257,0 </coordinates></LinearRing></outerBoundaryIs><innerBoundaryIs><LinearRing><coordinates>28.978,-28.956,0 28.542,-28.648,0 28.074,-28.851,0 27.533,-29.243,0 26.999,-29.876,0 27.749,-30.645,0 28.107,-30.546,0 28.291,-30.226,0 28.848,-30.07,0 29.018,-29.744,0 29.325,-29.257,0 28.978,-28.956,0 </coordinates></LinearRing></innerBoundaryIs></Polygon>
</Placemark> </kml>"""
doc = """
<kml xmlns="http://www.opengis.net/kml/2.2">
<Placemark>
<name>South Africa</name>
<Polygon>
<outerBoundaryIs>
<LinearRing>
<coordinates>
31.521,-29.257,0
31.326,-29.402,0
30.902,-29.91,0
30.623,-30.424,0
30.056,-31.14,0
28.926,-32.172,0
28.22,-32.772,0
27.465,-33.227,0
26.419,-33.615,0
25.91,-33.667,0
25.781,-33.945,0
25.173,-33.797,0
24.678,-33.987,0
23.594,-33.794,0
22.988,-33.916,0
22.574,-33.864,0
21.543,-34.259,0
20.689,-34.417,0
20.071,-34.795,0
19.616,-34.819,0
19.193,-34.463,0
18.855,-34.444,0
18.425,-33.998,0
18.377,-34.137,0
18.244,-33.868,0
18.25,-33.281,0
17.925,-32.611,0
18.248,-32.429,0
18.222,-31.662,0
17.567,-30.726,0
17.064,-29.879,0
17.063,-29.876,0
16.345,-28.577,0
16.824,-28.082,0
17.219,-28.356,0
17.387,-28.784,0
17.836,-28.856,0
18.465,-29.045,0
19.002,-28.972,0
19.895,-28.461,0
19.896,-24.768,0
20.166,-24.918,0
20.759,-25.868,0
20.666,-26.477,0
20.89,-26.829,0
21.606,-26.727,0
22.106,-26.28,0
22.58,-25.979,0
22.824,-25.5,0
23.312,-25.269,0
23.734,-25.39,0
24.211,-25.67,0
25.025,-25.72,0
25.665,-25.487,0
25.766,-25.175,0
25.942,-24.696,0
26.486,-24.616,0
26.786,-24.241,0
27.119,-23.574,0
28.017,-22.828,0
29.432,-22.091,0
29.839,-22.102,0
30.323,-22.272,0
30.66,-22.152,0
31.191,-22.252,0
31.67,-23.659,0
31.931,-24.369,0
31.752,-25.484,0
31.838,-25.843,0
31.333,-25.66,0
31.044,-25.731,0
30.95,-26.023,0
30.677,-26.398,0
30.686,-26.744,0
31.283,-27.286,0
31.868,-27.178,0
32.072,-26.734,0
32.83,-26.742,0
32.58,-27.47,0
32.462,-28.301,0
32.203,-28.752,0
31.521,-29.257,0
</coordinates>
</LinearRing>
</outerBoundaryIs>
<innerBoundaryIs>
<LinearRing>
<coordinates>
28.978,-28.956,0
28.542,-28.648,0
28.074,-28.851,0
27.533,-29.243,0
26.999,-29.876,0
27.749,-30.645,0
28.107,-30.546,0
28.291,-30.226,0
28.848,-30.07,0
29.018,-29.744,0
29.325,-29.257,0
28.978,-28.956,0
</coordinates>
</LinearRing>
</innerBoundaryIs>
</Polygon>
</Placemark>
</kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertTrue(isinstance(
list(k.features())[0].geometry, Polygon))
self.assertEqual(len(list(k.features())), 1)
self.assertTrue(
isinstance(list(k.features())[0].geometry, Polygon)
)
k2 = kml.KML()
k2.from_string(k.to_string())
self.assertEqual(k.to_string(), k2.to_string())
def test_multipoints(self):
doc="""<kml xmlns="http://www.opengis.net/kml/2.2">
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Placemark id="feat_2">
<name>MultiPoint</name>
<styleUrl>#stylesel_9</styleUrl>
......@@ -550,18 +648,17 @@ class KmlFromStringTestCase( unittest.TestCase ):
</Placemark></kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertTrue(isinstance(
list(k.features())[0].geometry, MultiPoint))
self.assertEqual(len(list(k.features())), 1)
self.assertTrue(
isinstance(list(k.features())[0].geometry, MultiPoint)
)
self.assertEqual(len(list(k.features())[0].geometry.geoms), 12)
k2 = kml.KML()
k2.from_string(k.to_string())
self.assertEqual(k.to_string(), k2.to_string())
def test_multilinestrings(self):
doc="""<kml xmlns="http://www.opengis.net/kml/2.2">
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Placemark>
<name>Dnipro (Dnieper)</name>
<MultiGeometry>
......@@ -573,15 +670,15 @@ class KmlFromStringTestCase( unittest.TestCase ):
</Placemark> </kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertTrue(isinstance(
list(k.features())[0].geometry, MultiLineString))
self.assertEqual(len(list(k.features())), 1)
self.assertTrue(
isinstance(list(k.features())[0].geometry, MultiLineString)
)
self.assertEqual(len(list(k.features())[0].geometry.geoms), 4)
k2 = kml.KML()
k2.from_string(k.to_string())
self.assertEqual(k.to_string(), k2.to_string())
def test_multipolygon(self):
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Placemark>
......@@ -590,9 +687,10 @@ class KmlFromStringTestCase( unittest.TestCase ):
</Placemark> </kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertTrue(isinstance(
list(k.features())[0].geometry, MultiPolygon))
self.assertEqual(len(list(k.features())), 1)
self.assertTrue(
isinstance(list(k.features())[0].geometry, MultiPolygon)
)
k2 = kml.KML()
k2.from_string(k.to_string())
self.assertEqual(k.to_string(), k2.to_string())
......@@ -665,7 +763,6 @@ class KmlFromStringTestCase( unittest.TestCase ):
self.assertEqual(sd1.schema_url, '#TrailHeadTypeId')
self.assertEqual(sd.to_string(), sd1.to_string())
def test_snippet(self):
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Placemark>
......@@ -690,8 +787,7 @@ class KmlFromStringTestCase( unittest.TestCase ):
self.assertRaises(TypeError, doc.from_string, '<xml></xml>')
class StyleTestCase( unittest.TestCase ):
class StyleTestCase(unittest.TestCase):
def test_styleurl(self):
f = kml.Document()
f.styleUrl = '#somestyle'
......@@ -707,8 +803,8 @@ class StyleTestCase( unittest.TestCase ):
def test_style(self):
lstyle = styles.LineStyle(color='red', width=2.0)
style = styles.Style(styles = [lstyle])
f = kml.Document(styles = [style])
style = styles.Style(styles=[lstyle])
f = kml.Document(styles=[style])
f2 = kml.Document()
f2.from_string(f.to_string(prettyprint=True))
self.assertEqual(f.to_string(), f2.to_string())
......@@ -769,8 +865,7 @@ class StyleUsageTestCase(unittest.TestCase):
self.assertEqual(place.to_string(), place3.to_string())
class StyleFromStringTestCase( unittest.TestCase ):
class StyleFromStringTestCase(unittest.TestCase):
def test_styleurl(self):
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
......@@ -781,13 +876,12 @@ class StyleFromStringTestCase( unittest.TestCase ):
</kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertEqual(len(list(k.features())), 1)
self.assertEqual(list(k.features())[0].styleUrl, '#default')
k2 = kml.KML()
k2.from_string(k.to_string())
self.assertEqual(k.to_string(), k2.to_string())
def test_balloonstyle(self):
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
......@@ -816,9 +910,10 @@ class StyleFromStringTestCase( unittest.TestCase ):
</kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertTrue(isinstance(
list(list(k.features())[0].styles())[0], styles.Style))
self.assertEqual(len(list(k.features())), 1)
self.assertTrue(
isinstance(list(list(k.features())[0].styles())[0], styles.Style)
)
style = list(list(list(k.features())[0].styles())[0].styles())[0]
self.assertTrue(isinstance(style, styles.BalloonStyle))
self.assertEqual(style.bgColor, 'ffffffbb')
......@@ -844,9 +939,10 @@ class StyleFromStringTestCase( unittest.TestCase ):
</kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertTrue(isinstance(
list(list(k.features())[0].styles())[0], styles.Style))
self.assertEqual(len(list(k.features())), 1)
self.assertTrue(
isinstance(list(list(k.features())[0].styles())[0], styles.Style)
)
style = list(list(list(k.features())[0].styles())[0].styles())[0]
self.assertTrue(isinstance(style, styles.LabelStyle))
self.assertEqual(style.color, 'ff0000cc')
......@@ -855,7 +951,6 @@ class StyleFromStringTestCase( unittest.TestCase ):
k2.from_string(k.to_string())
self.assertEqual(k.to_string(), k2.to_string())
def test_iconstyle(self):
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
......@@ -874,9 +969,10 @@ class StyleFromStringTestCase( unittest.TestCase ):
</kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list((k.features()))),1)
self.assertTrue(isinstance(
list(list(k.features())[0].styles())[0], styles.Style))
self.assertEqual(len(list((k.features()))), 1)
self.assertTrue(
isinstance(list(list(k.features())[0].styles())[0], styles.Style)
)
style = list(list(list(k.features())[0].styles())[0].styles())[0]
self.assertTrue(isinstance(style, styles.IconStyle))
self.assertEqual(style.color, 'ff00ff00')
......@@ -888,9 +984,8 @@ class StyleFromStringTestCase( unittest.TestCase ):
k2.from_string(k.to_string())
self.assertEqual(k.to_string(), k2.to_string())
def test_linestyle(self):
doc="""<kml xmlns="http://www.opengis.net/kml/2.2">
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>LineStyle.kml</name>
<open>1</open>
......@@ -904,9 +999,10 @@ class StyleFromStringTestCase( unittest.TestCase ):
</kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertTrue(isinstance(
list(list(k.features())[0].styles())[0], styles.Style))
self.assertEqual(len(list(k.features())), 1)
self.assertTrue(
isinstance(list(list(k.features())[0].styles())[0], styles.Style)
)
style = list(list(list(k.features())[0].styles())[0].styles())[0]
self.assertTrue(isinstance(style, styles.LineStyle))
self.assertEqual(style.color, '7f0000ff')
......@@ -915,9 +1011,8 @@ class StyleFromStringTestCase( unittest.TestCase ):
k2.from_string(k.to_string())
self.assertEqual(k.to_string(), k2.to_string())
def test_polystyle(self):
doc="""<kml xmlns="http://www.opengis.net/kml/2.2">
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>PolygonStyle.kml</name>
<open>1</open>
......@@ -929,12 +1024,13 @@ class StyleFromStringTestCase( unittest.TestCase ):
</Style>
</Document>
</kml>"""
#XXX fill and outline
# XXX fill and outline
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertTrue(isinstance(
list(list(k.features())[0].styles())[0], styles.Style))
self.assertEqual(len(list(k.features())), 1)
self.assertTrue(
isinstance(list(list(k.features())[0].styles())[0], styles.Style)
)
style = list(list(list(k.features())[0].styles())[0].styles())[0]
self.assertTrue(isinstance(style, styles.PolyStyle))
self.assertEqual(style.color, 'ff0000cc')
......@@ -943,9 +1039,8 @@ class StyleFromStringTestCase( unittest.TestCase ):
k2.from_string(k.to_string())
self.assertEqual(k.to_string(), k2.to_string())
def test_styles(self):
doc="""<kml xmlns="http://www.opengis.net/kml/2.2">
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<!-- Begin Style Definitions -->
<Style id="myDefaultStyles">
......@@ -974,9 +1069,10 @@ class StyleFromStringTestCase( unittest.TestCase ):
</kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertTrue(isinstance(
list(list(k.features())[0].styles())[0], styles.Style))
self.assertEqual(len(list(k.features())), 1)
self.assertTrue(
isinstance(list(list(k.features())[0].styles())[0], styles.Style)
)
style = list(list(list(k.features())[0].styles())[0].styles())
self.assertEqual(len(style), 4)
k2 = kml.KML()
......@@ -984,7 +1080,7 @@ class StyleFromStringTestCase( unittest.TestCase ):
self.assertEqual(k.to_string(), k2.to_string())
def test_stylemapurl(self):
doc= """<kml xmlns="http://www.opengis.net/kml/2.2">
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<StyleMap id="styleMapExample">
<Pair>
......@@ -1000,21 +1096,21 @@ class StyleFromStringTestCase( unittest.TestCase ):
</kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertTrue(isinstance(
list(list(k.features())[0].styles())[0], styles.StyleMap))
self.assertEqual(len(list(k.features())), 1)
self.assertTrue(
isinstance(list(list(k.features())[0].styles())[0], styles.StyleMap)
)
sm = list(list(list(k.features())[0].styles()))[0]
self.assertTrue(isinstance(sm.normal, styles.StyleUrl))
self.assertEqual(sm.normal.url, '#normalState')
self.assertTrue(isinstance(sm.highlight, styles.StyleUrl))
self.assertEqual(sm.highlight.url, '#highlightState')
k2 = kml.KML()
ks = k.to_string()
k2.from_string(k.to_string())
self.assertEqual(k.to_string(), k2.to_string())
def test_stylemapstyles(self):
doc= """<kml xmlns="http://www.opengis.net/kml/2.2">
doc = """<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<StyleMap id="styleMapExample">
<Pair>
......@@ -1043,9 +1139,10 @@ class StyleFromStringTestCase( unittest.TestCase ):
</kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertTrue(isinstance(
list(list(k.features())[0].styles())[0], styles.StyleMap))
self.assertEqual(len(list(k.features())), 1)
self.assertTrue(
isinstance(list(list(k.features())[0].styles())[0], styles.StyleMap)
)
sm = list(list(list(k.features())[0].styles()))[0]
self.assertTrue(isinstance(sm.normal, styles.Style))
self.assertEqual(len(list(sm.normal.styles())), 1)
......@@ -1089,7 +1186,7 @@ class StyleFromStringTestCase( unittest.TestCase ):
</kml>"""
k = kml.KML()
k.from_string(doc)
self.assertEqual(len(list(k.features())),1)
self.assertEqual(len(list(k.features())), 1)
document = list(k.features())[0]
style = document.get_style_by_url('http://localhost:8080/somepath#exampleStyleDocument')
self.assertTrue(isinstance(list(style.styles())[0], styles.LabelStyle))
......@@ -1099,8 +1196,7 @@ class StyleFromStringTestCase( unittest.TestCase ):
self.assertTrue(isinstance(style, styles.StyleMap))
class DateTimeTestCase( unittest.TestCase ):
class DateTimeTestCase(unittest.TestCase):
def test_timestamp(self):
now = datetime.datetime.now()
......@@ -1109,12 +1205,11 @@ class DateTimeTestCase( unittest.TestCase ):
self.assertTrue('TimeStamp>' in str(ts.to_string()))
self.assertTrue('when>' in str(ts.to_string()))
self.assertTrue(now.isoformat() in str(ts.to_string()))
y2k = datetime.date(2000,1,1)
y2k = datetime.date(2000, 1, 1)
ts = kml.TimeStamp(timestamp=y2k)
self.assertEqual(ts.timestamp, [y2k, 'date'])
self.assertTrue('2000-01-01' in str(ts.to_string()))
def test_timestamp_resolution(self):
now = datetime.datetime.now()
ts = kml.TimeStamp(timestamp=now)
......@@ -1133,10 +1228,9 @@ class DateTimeTestCase( unittest.TestCase ):
ts.timestamp = None
self.assertRaises(TypeError, ts.to_string)
def test_timespan(self):
now = datetime.datetime.now()
y2k = datetime.datetime(2000,1,1)
y2k = datetime.datetime(2000, 1, 1)
ts = kml.TimeSpan(end=now, begin=y2k)
self.assertEqual(ts.end, [now, 'dateTime'])
self.assertEqual(ts.begin, [y2k, 'dateTime'])
......@@ -1151,7 +1245,6 @@ class DateTimeTestCase( unittest.TestCase ):
ts.begin = None
self.assertRaises(ValueError, ts.to_string)
def test_feature_timestamp(self):
now = datetime.datetime.now()
f = kml.Document()
......@@ -1168,7 +1261,7 @@ class DateTimeTestCase( unittest.TestCase ):
def test_feature_timespan(self):
now = datetime.datetime.now()
y2k = datetime.date(2000,1,1)
y2k = datetime.date(2000, 1, 1)
f = kml.Document()
f.begin = y2k
f.end = now
......@@ -1190,7 +1283,7 @@ class DateTimeTestCase( unittest.TestCase ):
def test_feature_timespan_stamp(self):
now = datetime.datetime.now()
y2k = datetime.date(2000,1,1)
y2k = datetime.date(2000, 1, 1)
f = kml.Document()
f.begin = y2k
f.end = now
......@@ -1219,7 +1312,7 @@ class DateTimeTestCase( unittest.TestCase ):
self.assertTrue('end>' in str(f.to_string()))
self.assertFalse('TimeStamp>' in str(f.to_string()))
self.assertFalse('when>' in str(f.to_string()))
#We manipulate our Feature so it has timespan and stamp
# We manipulate our Feature so it has timespan and stamp
ts = kml.TimeStamp(timestamp=now)
f._time_stamp = ts
# this raises an exception as only either timespan or timestamp
......@@ -1228,55 +1321,55 @@ class DateTimeTestCase( unittest.TestCase ):
def test_read_timestamp(self):
ts = kml.TimeStamp(ns='')
doc ="""
doc = """
<TimeStamp>
<when>1997</when>
</TimeStamp>
"""
ts.from_string(doc)
self.assertEqual(ts.timestamp[1], 'gYear')
self.assertEqual(ts.timestamp[1], 'gYear')
self.assertEqual(ts.timestamp[0], datetime.datetime(1997, 1, 1, 0, 0))
doc ="""
doc = """
<TimeStamp>
<when>1997-07</when>
</TimeStamp>
"""
ts.from_string(doc)
self.assertEqual(ts.timestamp[1], 'gYearMonth')
self.assertEqual(ts.timestamp[1], 'gYearMonth')
self.assertEqual(ts.timestamp[0], datetime.datetime(1997, 7, 1, 0, 0))
doc ="""
doc = """
<TimeStamp>
<when>199808</when>
</TimeStamp>
"""
ts.from_string(doc)
self.assertEqual(ts.timestamp[1], 'gYearMonth')
self.assertEqual(ts.timestamp[1], 'gYearMonth')
self.assertEqual(ts.timestamp[0], datetime.datetime(1998, 8, 1, 0, 0))
doc ="""
doc = """
<TimeStamp>
<when>1997-07-16</when>
</TimeStamp>
"""
ts.from_string(doc)
self.assertEqual(ts.timestamp[1], 'date')
self.assertEqual(ts.timestamp[1], 'date')
self.assertEqual(ts.timestamp[0], datetime.datetime(1997, 7, 16, 0, 0))
#dateTime (YYYY-MM-DDThh:mm:ssZ)
#Here, T is the separator between the calendar and the hourly notation of time, and Z indicates UTC. (Seconds are required.)
doc ="""
# dateTime (YYYY-MM-DDThh:mm:ssZ)
# Here, T is the separator between the calendar and the hourly notation of time, and Z indicates UTC. (Seconds are required.)
doc = """
<TimeStamp>
<when>1997-07-16T07:30:15Z</when>
</TimeStamp>
"""
ts.from_string(doc)
self.assertEqual(ts.timestamp[1], 'dateTime')
self.assertEqual(ts.timestamp[1], 'dateTime')
self.assertEqual(ts.timestamp[0], datetime.datetime(1997, 7, 16, 7, 30, 15, tzinfo=tzutc()))
doc ="""
doc = """
<TimeStamp>
<when>1997-07-16T10:30:15+03:00</when>
</TimeStamp>
"""
ts.from_string(doc)
self.assertEqual(ts.timestamp[1], 'dateTime')
self.assertEqual(ts.timestamp[1], 'dateTime')
self.assertEqual(ts.timestamp[0], datetime.datetime(1997, 7, 16, 10, 30, 15, tzinfo=tzoffset(None, 10800)))
def test_read_timespan(self):
......@@ -1288,14 +1381,14 @@ class DateTimeTestCase( unittest.TestCase ):
</TimeSpan>
"""
ts.from_string(doc)
self.assertEqual(ts.begin[1], 'date')
self.assertEqual(ts.begin[1], 'date')
self.assertEqual(ts.begin[0], datetime.datetime(1876, 8, 1, 0, 0))
self.assertEqual(ts.end[1], 'dateTime')
self.assertEqual(ts.end[1], 'dateTime')
self.assertEqual(ts.end[0], datetime.datetime(1997, 7, 16, 7, 30, 15, tzinfo=tzutc()))
def test_featurefromstring(self):
d=kml.Document(ns='')
doc="""<Document>
d = kml.Document(ns='')
doc = """<Document>
<name>Document.kml</name>
<open>1</open>
<TimeStamp>
......@@ -1309,9 +1402,7 @@ class DateTimeTestCase( unittest.TestCase ):
d.from_string(doc)
class AtomTestCase( unittest.TestCase ):
class AtomTestCase(unittest.TestCase):
def test_author(self):
a = atom.Author(name="Christian Ledermann")
self.assertEqual(a.name, "Christian Ledermann")
......@@ -1323,7 +1414,7 @@ class AtomTestCase( unittest.TestCase ):
self.assertTrue('name>' in str(a.to_string()))
self.assertTrue('uri>' in str(a.to_string()))
self.assertTrue('email>' in str(a.to_string()))
#print (a.to_string())
# print (a.to_string())
a.email = 'christian'
self.assertFalse('email>' in str(a.to_string()))
a2 = atom.Author()
......@@ -1334,10 +1425,10 @@ class AtomTestCase( unittest.TestCase ):
l = atom.Link(href="http://localhost/", rel="alternate")
self.assertEqual(l.href, "http://localhost/")
self.assertEqual(l.rel, "alternate")
l.title="Title"
l.type="text/html"
l.hreflang ='en'
l.length="4096"
l.title = "Title"
l.type = "text/html"
l.hreflang = 'en'
l.length = "4096"
self.assertTrue('href="http://localhost/"' in str(l.to_string()))
self.assertTrue('rel="alternate"' in str(l.to_string()))
self.assertTrue('title="Title"' in str(l.to_string()))
......@@ -1352,12 +1443,12 @@ class AtomTestCase( unittest.TestCase ):
l.href = None
self.assertRaises(ValueError, l.to_string)
class SetGeometryTestCase( unittest.TestCase ):
class SetGeometryTestCase(unittest.TestCase):
def test_altitude_mode(self):
geom=Geometry()
geom.geometry = Point(0,1)
self.assertEqual(geom.altitude_mode , None)
geom = Geometry()
geom.geometry = Point(0, 1)
self.assertEqual(geom.altitude_mode, None)
self.assertFalse('altitudeMode' in str(geom.to_string()))
geom.altitude_mode = 'unknown'
self.assertRaises(AssertionError, geom.to_string)
......@@ -1373,12 +1464,12 @@ class SetGeometryTestCase( unittest.TestCase ):
self.assertTrue('altitudeMode>absolute</' in str(geom.to_string()))
def test_extrude(self):
geom=Geometry()
self.assertEqual(geom.extrude , False)
geom.geometry = Point(0,1)
geom.extrude=False
geom = Geometry()
self.assertEqual(geom.extrude, False)
geom.geometry = Point(0, 1)
geom.extrude = False
self.assertFalse('extrude' in str(geom.to_string()))
geom.extrude=True
geom.extrude = True
geom.altitude_mode = 'clampToGround'
self.assertFalse('extrude' in str(geom.to_string()))
geom.altitude_mode = 'relativeToGround'
......@@ -1387,9 +1478,9 @@ class SetGeometryTestCase( unittest.TestCase ):
self.assertTrue('extrude>1</' in str(geom.to_string()))
def test_tesselate(self):
geom=Geometry()
self.assertEqual(geom.tessellate , False)
geom.geometry = LineString([(0,0), (1,1)])
geom = Geometry()
self.assertEqual(geom.tessellate, False)
geom.geometry = LineString([(0, 0), (1, 1)])
self.assertFalse('tessellate' in str(geom.to_string()))
geom.altitude_mode = 'clampToGround'
self.assertFalse('tessellate' in str(geom.to_string()))
......@@ -1406,14 +1497,14 @@ class SetGeometryTestCase( unittest.TestCase ):
self.assertFalse('tessellate' in str(geom.to_string()))
geom.altitude_mode = 'clampToGround'
self.assertTrue('tessellate>1</' in str(geom.to_string()))
#for geometries != LineString tesselate is ignored
geom.geometry = Point(0,1)
# for geometries != LineString tesselate is ignored
geom.geometry = Point(0, 1)
self.assertFalse('tessellate' in str(geom.to_string()))
geom.geometry = Polygon([(0,0), (1,0), (1,1), (0,0)])
geom.geometry = Polygon([(0, 0), (1, 0), (1, 1), (0, 0)])
self.assertFalse('tessellate' in str(geom.to_string()))
def testPoint(self):
p = Point(0,1)
p = Point(0, 1)
g = Geometry(geometry=p)
self.assertEqual(g.geometry, p)
g = Geometry(geometry=p.__geo_interface__)
......@@ -1422,7 +1513,7 @@ class SetGeometryTestCase( unittest.TestCase ):
self.assertTrue('coordinates>0.000000,1.000000</' in str(g.to_string()))
def testLineString(self):
l = LineString([(0,0), (1,1)])
l = LineString([(0, 0), (1, 1)])
g = Geometry(geometry=l)
self.assertEqual(g.geometry, l)
self.assertTrue('LineString' in str(g.to_string()))
......@@ -1432,7 +1523,7 @@ class SetGeometryTestCase( unittest.TestCase ):
self.assertEqual(g.to_string(), g2.to_string())
def testLinearRing(self):
l = LinearRing([(0,0), (1,0), (1,1), (0,0)])
l = LinearRing([(0, 0), (1, 0), (1, 1), (0, 0)])
g = Geometry(geometry=l)
self.assertEqual(g.geometry, l)
self.assertTrue('LinearRing' in str(g.to_string()))
......@@ -1441,8 +1532,8 @@ class SetGeometryTestCase( unittest.TestCase ):
in str(g.to_string()))
def testPolygon(self):
#without holes
l = Polygon([(0,0), (1,0), (1,1), (0,0)])
# without holes
l = Polygon([(0, 0), (1, 0), (1, 1), (0, 0)])
g = Geometry(geometry=l)
self.assertEqual(g.geometry, l)
self.assertTrue('Polygon' in str(g.to_string()))
......@@ -1452,8 +1543,11 @@ class SetGeometryTestCase( unittest.TestCase ):
self.assertTrue(
'coordinates>0.000000,0.000000 1.000000,0.000000 1.000000,1.000000 0.000000,0.000000</'
in str(g.to_string()))
#with holes
p = Polygon([(-1,-1), (2,-1), (2,2), (-1,-1)],[[(0,0), (1,0), (1,1), (0,0)]])
# with holes
p = Polygon(
[(-1, -1), (2, -1), (2, 2), (-1, -1)],
[[(0, 0), (1, 0), (1, 1), (0, 0)]],
)
g = Geometry(geometry=p)
self.assertEqual(g.geometry, p)
self.assertTrue('Polygon' in str(g.to_string()))
......@@ -1468,29 +1562,32 @@ class SetGeometryTestCase( unittest.TestCase ):
in str(g.to_string()))
def testMultiPoint(self):
p0 = Point(0,1)
p1 = Point(1,1)
g = Geometry(geometry=MultiPoint([p0,p1]))
p0 = Point(0, 1)
p1 = Point(1, 1)
g = Geometry(geometry=MultiPoint([p0, p1]))
self.assertTrue('MultiGeometry' in str(g.to_string()))
self.assertTrue('Point' in str(g.to_string()))
self.assertTrue('coordinates>0.000000,1.000000</' in str(g.to_string()))
self.assertTrue('coordinates>1.000000,1.000000</' in str(g.to_string()))
def testMultiLineString(self):
l0 = LineString([(0,0), (1,0)])
l1 = LineString([(0,1), (1,1)])
g = Geometry(geometry=MultiLineString([l0,l1]))
l0 = LineString([(0, 0), (1, 0)])
l1 = LineString([(0, 1), (1, 1)])
g = Geometry(geometry=MultiLineString([l0, l1]))
self.assertTrue('MultiGeometry' in str(g.to_string()))
self.assertTrue('LineString' in str(g.to_string()))
self.assertTrue('coordinates>0.000000,0.000000 1.000000,0.000000</' in str(g.to_string()))
self.assertTrue('coordinates>0.000000,1.000000 1.000000,1.000000</' in str(g.to_string()))
def testMultiPolygon(self):
#with holes
p0 = Polygon([(-1,-1), (2,-1), (2,2), (-1,-1)],[[(0,0), (1,0), (1,1), (0,0)]])
#without holes
p1 = Polygon([(3,0), (4,0), (4,1), (3,0)])
g = Geometry(geometry=MultiPolygon([p0,p1]))
# with holes
p0 = Polygon(
[(-1, -1), (2, -1), (2, 2), (-1, -1)],
[[(0, 0), (1, 0), (1, 1), (0, 0)]]
)
# without holes
p1 = Polygon([(3, 0), (4, 0), (4, 1), (3, 0)])
g = Geometry(geometry=MultiPolygon([p0, p1]))
self.assertTrue('MultiGeometry' in str(g.to_string()))
self.assertTrue('Polygon' in str(g.to_string()))
self.assertTrue('outerBoundaryIs' in str(g.to_string()))
......@@ -1507,16 +1604,16 @@ class SetGeometryTestCase( unittest.TestCase ):
in str(g.to_string()))
def testGeometryCollection(self):
po = Polygon([(3,0), (4,0), (4,1), (3,0)])
lr = LinearRing([(0,-1), (1,-1), (1,1), (0,-1)])
ls = LineString([(0,0), (1,1)])
p = Point(0,1)
geo_if = {'type': 'GeometryCollection', 'geometries': [
po.__geo_interface__, p.__geo_interface__,
ls.__geo_interface__, lr.__geo_interface__ ]}
g = Geometry(geometry=GeometryCollection([po,p,ls,lr]))
#g1 = Geometry(geometry=as_shape(geo_if))
#self.assertEqual(g1.__geo_interface__, g.__geo_interface__)
po = Polygon([(3, 0), (4, 0), (4, 1), (3, 0)])
lr = LinearRing([(0, -1), (1, -1), (1, 1), (0, -1)])
ls = LineString([(0, 0), (1, 1)])
p = Point(0, 1)
# geo_if = {'type': 'GeometryCollection', 'geometries': [
# po.__geo_interface__, p.__geo_interface__,
# ls.__geo_interface__, lr.__geo_interface__]}
g = Geometry(geometry=GeometryCollection([po, p, ls, lr]))
# g1 = Geometry(geometry=as_shape(geo_if))
# self.assertEqual(g1.__geo_interface__, g.__geo_interface__)
self.assertTrue('MultiGeometry' in str(g.to_string()))
self.assertTrue('Polygon' in str(g.to_string()))
self.assertTrue('outerBoundaryIs' in str(g.to_string()))
......@@ -1530,8 +1627,8 @@ class SetGeometryTestCase( unittest.TestCase ):
self.assertTrue('Point' in str(g.to_string()))
self.assertTrue('coordinates>0.000000,1.000000</' in str(g.to_string()))
class GetGeometryTestCase( unittest.TestCase ):
class GetGeometryTestCase(unittest.TestCase):
def test_altitude_mode(self):
doc = """<kml:Point xmlns:kml="http://www.opengis.net/kml/2.2">
<kml:coordinates>0.000000,1.000000</kml:coordinates>
......@@ -1568,8 +1665,10 @@ class GetGeometryTestCase( unittest.TestCase ):
</kml:Point>"""
g = Geometry()
g.from_string(doc)
self.assertEqual(g.geometry.__geo_interface__,
{'type': 'Point', 'coordinates': (0.0, 1.0)})
self.assertEqual(
g.geometry.__geo_interface__,
{'type': 'Point', 'coordinates': (0.0, 1.0)}
)
def testLineString(self):
doc = """<kml:LineString xmlns:kml="http://www.opengis.net/kml/2.2">
......@@ -1577,8 +1676,10 @@ class GetGeometryTestCase( unittest.TestCase ):
</kml:LineString>"""
g = Geometry()
g.from_string(doc)
self.assertEqual(g.geometry.__geo_interface__,
{'type': 'LineString', 'coordinates': ((0.0, 0.0), (1.0, 1.0))})
self.assertEqual(
g.geometry.__geo_interface__,
{'type': 'LineString', 'coordinates': ((0.0, 0.0), (1.0, 1.0))}
)
def testLinearRing(self):
doc = """<kml:LinearRing xmlns:kml="http://www.opengis.net/kml/2.2">
......@@ -1587,8 +1688,11 @@ class GetGeometryTestCase( unittest.TestCase ):
"""
g = Geometry()
g.from_string(doc)
self.assertEqual(g.geometry.__geo_interface__,
{'type': 'LinearRing', 'coordinates': ((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0))})
self.assertEqual(
g.geometry.__geo_interface__,
{'type': 'LinearRing', 'coordinates': ((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0))}
)
def testPolygon(self):
doc = """<kml:Polygon xmlns:kml="http://www.opengis.net/kml/2.2">
<kml:outerBoundaryIs>
......@@ -1600,11 +1704,19 @@ class GetGeometryTestCase( unittest.TestCase ):
"""
g = Geometry()
g.from_string(doc)
self.assertEqual(g.geometry.__geo_interface__,
{'type': 'Polygon', 'coordinates':
(((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)),)}
)
doc ="""<kml:Polygon xmlns:kml="http://www.opengis.net/kml/2.2">
self.assertEqual(
g.geometry.__geo_interface__,
{
'type': 'Polygon',
'coordinates': ((
(0.0, 0.0),
(1.0, 0.0),
(1.0, 1.0),
(0.0, 0.0)
),)
}
)
doc = """<kml:Polygon xmlns:kml="http://www.opengis.net/kml/2.2">
<kml:outerBoundaryIs>
<kml:LinearRing>
<kml:coordinates>-1.000000,-1.000000 2.000000,-1.000000 2.000000,2.000000 -1.000000,-1.000000</kml:coordinates>
......@@ -1618,11 +1730,16 @@ class GetGeometryTestCase( unittest.TestCase ):
</kml:Polygon>
"""
g.from_string(doc)
self.assertEqual(g.geometry.__geo_interface__,
{'type': 'Polygon', 'coordinates':
(((-1.0, -1.0), (2.0, -1.0), (2.0, 2.0), (-1.0, -1.0)),
((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)))}
)
self.assertEqual(
g.geometry.__geo_interface__,
{
'type': 'Polygon',
'coordinates': (
((-1.0, -1.0), (2.0, -1.0), (2.0, 2.0), (-1.0, -1.0)),
((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)),
)
}
)
def testMultiPoint(self):
doc = """
......@@ -1639,7 +1756,6 @@ class GetGeometryTestCase( unittest.TestCase ):
g.from_string(doc)
self.assertEqual(len(g.geometry), 2)
def testMultiLineString(self):
doc = """
<kml:MultiGeometry xmlns:kml="http://www.opengis.net/kml/2.2">
......@@ -1723,28 +1839,28 @@ class GetGeometryTestCase( unittest.TestCase ):
self.assertEqual(g.geometry.geom_type, 'GeometryCollection')
class Force3DTestCase( unittest.TestCase ):
class Force3DTestCase(unittest.TestCase):
def test3d(self):
ns =''
ns = ''
p2 = kml.Placemark(ns, 'id', 'name', 'description')
p2.geometry = Polygon([(0, 0), (1, 1), (1, 0)])
p2.geometry = Polygon([(0, 0), (1, 1), (1, 0)])
p3 = kml.Placemark(ns, 'id', 'name', 'description')
p3.geometry = Polygon([(0, 0, 0), (1, 1, 0), (1, 0, 0)])
p3.geometry = Polygon([(0, 0, 0), (1, 1, 0), (1, 0, 0)])
config.FORCE3D = False
#p3.altitudeMode = 'absolute'
# p3.altitudeMode = 'absolute'
self.assertNotEqual(p2.to_string(), p3.to_string())
config.FORCE3D = True
self.assertEqual(p2.to_string(), p3.to_string())
#altitudeMode clampToGround indicates to ignore an altitude specification.
#p3.altitudeMode = 'clampToGround'
#self.assertEqual(p2.to_string(), p3.to_string())
#config.FORCE3D = False
#self.assertNotEqual(p2.to_string(), p3.to_string())
# altitudeMode clampToGround indicates to ignore an altitude specification.
# p3.altitudeMode = 'clampToGround'
# self.assertEqual(p2.to_string(), p3.to_string())
# config.FORCE3D = False
# self.assertNotEqual(p2.to_string(), p3.to_string())
#Important: Set FORCE3D back to False!
# Important: Set FORCE3D back to False!
config.FORCE3D = False
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(BaseClassesTestCase))
......@@ -1761,4 +1877,3 @@ def test_suite():
if __name__ == '__main__':
unittest.main()
-r common.txt
pytest
pep8
coveralls
from setuptools import setup, find_packages
from setuptools.command.test import test as TestCommand
import sys, os
import sys
import os
class PyTest(TestCommand):
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True
def run_tests(self):
#import here, cause outside the eggs aren't loaded
# import here, cause outside the eggs aren't loaded
import pytest
errno = pytest.main(self.test_args)
sys.exit(errno)
......@@ -16,14 +19,16 @@ class PyTest(TestCommand):
version = '0.7'
setup(name='fastkml',
version=version,
description="Fast KML processing in python",
long_description=open(
"README.rst").read() + "\n" +
open(os.path.join("docs", "HISTORY.txt")).read() + "\n" +
open(os.path.join("docs", "TODO.txt")).read(),
classifiers=[
setup(
name='fastkml',
version=version,
description="Fast KML processing in python",
long_description=(
open("README.rst").read() + "\n" +
open(os.path.join("docs", "HISTORY.txt")).read() + "\n" +
open(os.path.join("docs", "TODO.txt")).read()
),
classifiers=[
"Topic :: Scientific/Engineering :: GIS",
"Programming Language :: Python",
'Programming Language :: Python :: 2',
......@@ -37,23 +42,23 @@ setup(name='fastkml',
'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
'Development Status :: 4 - Beta',
'Operating System :: OS Independent',
], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
keywords='GIS KML Google Maps OpenLayers',
author='Christian Ledermann',
author_email='christian.ledermann@gmail.com',
url='https://github.com/cleder/fastkml',
license='LGPL',
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
include_package_data=True,
zip_safe=False,
tests_require=['pytest'],
cmdclass = {'test': PyTest},
install_requires=[
# -*- Extra requirements: -*-
'pygeoif',
'python-dateutil',
],
entry_points="""
# -*- Entry points: -*-
""",
)
], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
keywords='GIS KML Google Maps OpenLayers',
author='Christian Ledermann',
author_email='christian.ledermann@gmail.com',
url='https://github.com/cleder/fastkml',
license='LGPL',
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
include_package_data=True,
zip_safe=False,
tests_require=['pytest'],
cmdclass = {'test': PyTest},
install_requires=[
# -*- Extra requirements: -*-
'pygeoif',
'python-dateutil',
],
entry_points="""
# -*- Entry points: -*-
""",
)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment